This document is about: QUANTUM 1
SWITCH TO

ナビゲーションシステム

はじめに

Quantumナビゲーションシステムは、予測ナビゲーションメッシュとゲームのワールドの状況を判断して進んでいくエージェントを提供します。以下の2つのパーツで構成されています。

ナビゲーションメッシュ は、エージェントが 歩く ことのできる三角形で構成されたデータ構造です。

  • メッシュは手動で書くことも編集することもでき、またUnityのNavMeshから変換することもできます。
  • 3Dデータをエクスポート済みでも、高さの概念はまだ認識されていません。
  • マップごとに複数のナビゲーションメッシュを使用できます。
  • NavMeshは標準のA*パスファインディングアルゴリズムを搭載しています。
  • メッシュは、動的に切り替え可能な領域を含めることができます(移動できないUnity navmeshの障害物のように)。

ナビゲーションメッシュエージェント は、ナビゲーションメッシュを通してエンティティを進行させるQuantumコンポーネントです。

  • ターゲットを受信した後、エージェントはレベルの中を自立して移動します。
  • エージェントは優先順位に従って回避システムを使用します。
  • エージェントは動体となるようにマークされるため、壁を突き破ったりはできません。

ヒント

ナビゲーションシステム機能はアクションRPGサンプルで使用されています。 ActionRPGのサンプルドキュメントからダウンロードしてください。

ワークフロー

マップデータにMapNavMeshDefinitionスクリプトを追加するか、またはマップデータの下にGameObjectを作成し、そこにNavMeshDefinitionスクリプトを添付します。

ここにQuantum navmeshの中間データがあります。このデータは、後でNavMesh Quantumアセットにベイクされます。

ここからUnity NavMeshをインポートすること(A)も、手動でQuantumナビゲーションシステムを作成および編集すること(B)も可能です。

Navmesh Inspector
Quantum Navmesh Definition Inspector

MapNavMeshDefinitionGameObjectが選択されると、Quantum navmeshがシーンに描画されます。

  • Toggle Always Draw はGameObjectが選択されていない場合でも、navmeshを描画します。
  • Toggle Draw Mesh は三角形を輪郭どおりに描画します。
  • Toggle Optimized Gizmos はデバッグレンダリングを最適化しますが、手動編集モードとの互換性はありません。
  • Export Test Mesh はnavmeshをファイルにエクスポートします。
Navmesh Definition Gizmos
Quantum Navmesh定義gizmo

Quantum Navmeshの作成

A) Unity NavMeshのインポート

  • Unityのドキュメントに従ってUnity NavMeshを作成およびベイクします。(Off Meshリンク、Height Mesh、weightはサポートされていません。)

  • または、 NavMeshComponents アドオン(Unity Githubページからダウンロード可能)を使用します。 次に、コンポーネントをNavMeshSurfacesリストにリンクします。

  • インスペクターで Unityからインポート ボタンを押します。

  • Weld Indentical Vertices は同一の頂点を組み合わせます。 常に有効にしてください。

  • Weld Vertices Epslion はイプシロンを微調整して頂点を溶接します。

  • Fix Triangles On Edges は頂点が他の三角形のエッジ上にある三角形を修正します。誤った境界の検出を防ぐためです。

  • Import Regions によってQuantum navmeshリージョンのインポートが可能になります。リージョンがない場合は、これをオフにします。

  • Region Detection Margin はUnity NavMeshがソースメッシュサイズにあまり適合しないため、必要です。値がnavmeshエリアに追加され、すべてのQuantum Regionスクリプトに対して確認され、正しいリージョンIDが選択されます。リージョンが検出されない場合、この値を大きくします。

  • Convert Unity Areas To Quantum Region リージョンのワークフローを参照してください。

  • Agent Radius はUnity navmeshの生成に使用される最小エージェント半径を抽出することにより、インポート中に上書きされます。 エージェントのより正確なウェイポイントを計算するために使用されます。

1.2.4以前の既知の問題

Advanced Options を展開して Navmeshの検証 を実行し、頂点の距離の組み合わせ を調整します。UnityNavMeshをインポートする際、互いに非常に近くにある頂点は結合されます。まれに、正しい結果を得るためにデルタを拡大する必要があります。Validate Navmesh を実行するとわかります。

  • UnityのNavMeshフォーマットは弊社のものとは異なります。例えば、メッシュがチャンクに分かれていて、実際に必要な数よりも多くの三角形が作成されてしまい、さらに三角形は頂点を共有していません。安全にボーダーを検出するため、これを適切なメッシュに変換する必要があります。まさにこの2つの事実が、適切なボーダーを検出することでQuantumでのNavMeshベイキングを時折失敗させる要因になります。このような時は、エージェントが明らかに空っぽの道におかしい選択をするのでわかります。この現象は、Unityの NavMeshComponents (UnityのGithubページからダウンロード可能)を使用し、NavMeshSurfaceインスペクター上で別のタイル設定を適用(以下を参照してください。)することで軽減できます。
navmesh map data
インスペクターのタイル設定をオーバーライド

B) ナビゲーションメッシュを手動で編集

  • Start Editing NavMesh を押すとNavMeshの編集をするためのマウスクリックを制限しようとします。他のゲームオブジェクトを再度選択できるようにするには Stop Editing NavMesh をクリックします。
  • Add Vertex を3回おこなって新しいメッシュの作成を始めます。新しい3つの頂点は (0,0,0)で作成されます。
  • シーン内で頂点を1つ選択しUnityの移動ツールを使用してトランスレートします。
  • 3つの頂点全てを別々に動かした後、shift を押しながら頂点をクリックして、3つ 全てを選択し、Create Triangle をクリックするか、T キーを押します。後でパスファインディングの際に、三角形だけ使用します。
  • ちなみに、編集の全ステップはすべて元に戻すことができます。
  • 今度は 2つ の頂点を選択し、 Insert Vertex + Create Triangle を使用するかショートカット T を押してメッシュを取り除きます。
  • 頂点を複数選択すると、それらをグループとして移動させることができます。
  • X キーを押すと、頂点の選択を拡大できます。
  • 頂点を選択し Delete Vertices を押すか、ショートカット Backspace を押して削除します。
  • 任意の数の頂点を選択し、Duplicate And Flip を使用してそれらをミラーリングします (0, 0, 0).
navmesh editor
手動でQuantum NavMeshを編集

既知の問題

  • 頂点ハンドルのgizmoがシーン内に描かれないこともあります。Unityレイアウトをリセット(Unityエディターの右上の端、Layoutボタンの下)してください。

C) あらゆるメッシュをMapNavMeshDefinitionとして使用

MapNavMeshDefinitionクラスもしくはMapNavMeshDefinitionEditor.ImportFromUnity()でデータがどのように入力されているか確認します。例として、MeshFilterから情報を入力すると非常に簡単です。

C#

public class MapNavMeshDefinition : MonoBehaviour {
  public MapNavMeshVertex[] Vertices;
  public MapNavMeshTriangle[] Triangles;
}

Quantum NavMeshをベイクする

Unity navmeshをインポートするか、Quantum navmesh定義を手動で編集した後、navmeshをQuantumアセットにベイクする必要があります。

  • グリッドがNavMesh全体を覆っていることを確認してください。
  • Bake Nav Meshes を押します。 この手順では、生のナビゲーションメッシュデータをQuantum NavMesh形式に変換します。境界線のエッジが生成され、三角形と境界線がグリッド構造に挿入されて高速にアクセスされ、グリッドセルごとに最も近い三角形のルックアップテーブルが作成されます。
  • 追加のファイルが作成され、MapData(NavMeshLinks)に添付されます。ファイルを選択すると、それが最終的なNavMesh.AssetObjectであることに気付くでしょうが、バイナリデータの大部分はなく、Data Filepathフィールドで指定されたバイナリファイルに保存されます。理由は、ScriptableObjectsのサイズ制限です。 追加のファイルは、NavMesh.AssetObjectのLoaded-コールバックのUnityDB.Init()で読み込まれます。
navmesh map data
MapDataインスペクターを使用してナビゲーションメッシュをベイクする

ヒント

Quantumマップのベイク処理にカスタムスクリプトを挿入するのは簡単です。OnBeforeBake-callbackにチェーンすることにより、上記のすべてのステップを自動的に実行できます。 コミュニティWebサイトの Auto Baking Map Dataページをご覧ください。

Quantum NavMeshエージェントのセットアップ

SystemSetupでNavMeshAgentSystemを有効にします:

C#

  public static class SystemSetup {
    public static SystemBase[] CreateSystems(RuntimeConfig gameConfig, SimulationConfig simulationConfig) {
      return new SystemBase[] {
        // pre-defined core systems
        new Core.PhysicsSystemPre(),
        new Core.NavMeshAgentSystem(),
        // ..
      };
    }
  }

Quantum DSLのエンティティにNavMeshAgentコンポーネントを追加します(たとえば、メインキャラクターに)。Transform2Dは必須で、DynamicBodyは任意です。

entity Character[PLAYER_COUNT] {
  use Transform2D;
  use DynamicBody;
  use Prefab;
  use NavMeshAgent;

  fields {
    player_ref Player;
  }
}

エンティティを作成する際に、目的のAgentConfigを設定:

C#

c->NavMeshAgent.AgentConfig = DB.FindAsset<NavMeshAgentConfig>("MyAgentConfig");

コンポーネントに目的のターゲットを設定し、渡されたNavMeshを使用してそれに向かって移動:

C#

var nm = f.Map.NavMeshes["NavMesh"];
var i = f.GetPlayerInput(c->Player);
c->NavMeshAgent.SetTarget(i->Data.Target, nm, false);

ヒント

エージェントが「UsePhysics」に設定されている場合、エージェントの物理マテリアルでFreezeRotationをtrueに設定するようにしてください。こうすると、オーバーステアやその他エージェントのステアリングで起きる回転に関する問題を防ぐことができます。

エージェントゲームオブジェクトにアニメーションを設定するには、次のような処理を行うスクリプトをプレハブに添付します。 Speed変数を使用するアニメーションコントローラーを作成してください。

C#

public class CharacterAnimation : MonoBehaviour {
  public float AnimSpeedFactor = 0.5f;
  private Animator anim;
  private EntityPrefabRoot root;

  void Start() {
    anim = GetComponentInChildren<Animator>();
    root = GetComponent<EntityPrefabRoot>();
  }

  unsafe void Update() {
    if (QuantumRunner.Default != null) {
      var entity = QuantumRunner.Default.Game.Frames.Current.GetEntity(root.EntityRef);
      var body = Quantum.Entity.GetDynamicBody(entity);
      float speed = body->Velocity.Magnitude.AsFloat;
      anim.SetFloat("Speed", speed);

      if (speed > 0.1f)
        anim.speed = speed * AnimSpeedFactor;
      else
        anim.speed = 1;
    }
  }
}

Quantum 1.2.4以降

Navmeshリージョンは、実行中に確定的にアクティブ化およびインアクティブ化できるnavmeshの事前定義された部分です。ダイナミックカービングテクニックと比べるとリージョンのパフォーマンスオーバーヘッドはわずかです。

Unity NavMeshエリアは、Quantumリージョンが存在するはずの三角形を生成するために利用されます。追加のゲームオブジェクト(MeshRenderを使用)は、リージョンを生成する位置に配置する必要があります。

NavMeshSurfacesを使用する場合も同様です。 さらに、NavmeshModifierスクリプトを追加のゲームオブジェクトに追加します。

リージョンの最大数は64です。同じリージョンIDを様々な場所で使用することができます。限界に近づいた場合には、リージョンIDを再利用してください。

デフォルトのQuantum navmeshはリージョンではないため、切り替えることはできません。

  1. 1つのUnity NavMeshエリアを予約して、「QuantumRegion」として指定します。通常、必要なのは1つだけです。リージョンが正確に隣り合っている場合(エッジを共有している場合)、異なるエリアIDでセットアップする必要があります。
areas
Unity Navigationタブ エリアの選択
  1. 手順1)で作成したUnity NavMesh Areasを、Quantum NavMeshインスペクターのConvert Unity Areas To Quantum Regions に追加します:
areaselection
Quantum Navmesh Definition inspector 
  1. MeshRenderer があり、Quantum RegionをNavMeshに投影するシーンのGameObjectsにMapNavMeshRegionスクリプトを追加します。
    手順1)からUnity NavMeshエリアを選択し、Navigation Staticがオンに切り替えられていることを確認し、リージョンにId(シミュレーションで参照できるリージョンとして)を指定します。
regionscript
MapNavmeshRegionスクリプトをメッシュレンダラーゲームオブジェクトに追加
  1. Unity NavMeshをベイクします

生成された三角形は、ソースメッシュレンダラーと完全には一致しません。Region Detection Margin は、リージョンを識別するためにメッシュレンダラーのバウンディングボックスに対して結果の三角形を確認する際、マージンを追加するために使用されます。QuantumEditorSettings.DrawNavMeshRegionIdsを切り替えてデバッグします。

  1. UnityからQuantum NavMeshをインポートする

  2. NavMeshをベイクする(マップインスペクター上)

コード内のQuantumリージョンの切り替え

C#

var nm = f.Map.NavMeshes["NavMeshUnity"];

var isActive = nm.IsRegionActive(f, "Door_002");

// Toggle by name
nm.SetRegionActive(f, "Door_002", !isActive);

// Get region index from region name
byte GetNavMeshIndex(Frame f, NavMesh navMesh, string name)
{
    NavMeshRegion navMeshRegion;
    if (navMesh.RegionMap.TryGetValue(name, out navMeshRegion))
    {
        return navMeshRegion.Index;
    }

    return byte.MaxValue;
}

// Toggle region on off by its index (starting by 0)
nm.SetRegionActive(f, regionIndex, !nm.IsRegionActive(f, flags));

既知の問題

  • 新しいマップを読み込む際は、Frame.ClearAllNavMeshRegions()を呼び出す必要があります。

  • リージョンを有効または無効にするとき、すべてのエージェントはパスを変更する必要があります: NavMeshAgent.ForceRepath()

  • (1.2.4 B3以降) Quantumナビメッシュは、QUANTUM_XYもアクティブな場合に内部的にXZ面表示も使用します。ベイキングの際に、XY面でNavMeshSurfaceによって生成されたUnityナビメッシュははずされます。

  • (PathQuality Bestを使用して1.2.4 B3で修正)余分な三角形(有効なリージョンの近く)の導入により、ヒューリスティックは視覚的に正しくない初期ウェイポイントを選択することがあります。また、アクティブなリージョンに向かって若干曲がっているパスを作成することがあります。

  • (1.2.4 B3で修正)リージョンはまだ法線を生成しません。最大のユニットが視覚的に周辺を移動することができるように、リージョンを十分に余裕のある大きさにしてください。

実行時のデバッグ

EditorConfigSettingsに移動すると、Unityエディターで実行時にNavMeshと追加情報を描画するためのオプションが追加されます。

QuantumEditorSettings
QuantumEditorSettings
  • Draw Nav Mesh はNavMesh Unityのように、さらにリージョンをレンダリングします。 無効なリージョンはグレー表示されます。

  • Draw Nav Mesh Borders では、最後にNavMeshラインキャストに使用される境界線を視覚化できます。リージョンがオフに切り替わると、リージョンの境界線(赤色)が有効になります。

  • Draw Pathfinder Funnel は最新の情報でのみ機能するため、エージェントが1つしかない場合にのみ実際に使用できます。 コードはUnity内にあり、簡単に拡張できます。

Debug Rendering Runtime
レンダリングランタイムのデバッグ

Quantumエンティティ状態インスペクターでもエージェントの状態をデバッグできます。

navmesh entity state inspector
Quantumエンティティ状態インスペクターを使用して、NavMeshAgentフィールドを参照

API

navmesh agent config
NavMeshAgentConfig
  • Radius エージェントの半径です。

  • Speed ユニット内のエージェントの秒速です。

  • Angular Speed はエージェントの回転速度に影響します。0 に設定すると、エージェントは回転しません。高い値 に設定すると、回転が瞬時になります。

  • Acceleration は加速時のエージェントの速さに影響します。0 に設定すると、加速は行われません。

  • Epsilon は目的に到達するまでの距離のしきい値です。Loaded()の際にNNavMeshAgentConfig.MinEpsilonにクランプされます。

  • Max Repath Timeout 秒単位。この間隔では再パスが強制されます。

  • Path Quality 経路探索経験則の品質を選択します。

    • Fast はA経験則に対してマンハッタン距離を使用し、Aノード候補に三角形の中心を使用します。
    • Good はA*ノード候補を作成する場合、三角形の角を計算に入れます
    • Best は、Aノード位置に可能な場合には、A経験則に対しユークリッド距離を使用し、(1.2.4 RC1以降では)Atriangle vs. scan line intersectionを使用します。リージョンを使用している場合は、このオプションを推奨します!
  • Use Physics これを切り替えて、直接translationを設定する代わりにエージェントが動体(DynamicBodyコンポーネントが必要です。)に速度を設定するようにします。

  • Dynamic Line Of Sight エージェントがNavMesh.LineOfSight()チェックを使用してウェイポイントを確認できる場合、ここを切り替えて スキップ するようにします。

  • Dynamic Line Of Sight Waypoint Range 上記と同じですが、ウェイポイントまでの範囲内にある場合にのみ有効になります。無効にするには0に設定します。

  • FindValidTargetCellRange SetTarget()の位置がnavmesh 上に ない* 場合、それは修正され、navmesh 上の 有効な位置に移動します。デフォルトでは、これは視覚的に不正確な場合があり、この検索を有効にすることで結果の品質を上げることができます(値> 0)。位置のセルおよび各方向のX-1隣接セルは、navmesh上の 最も近い位置を検索します。検索は非常に複雑になる可能性があるため、この値を高く設定しすぎないでください。

    • 既知の問題
  • Show Debug エージェントの動作と経路探索のgizmoを描く:ピンクの小さな点がターゲットを示し、黄色の大きな点が現在のウェイポイントです。

  • 回避レガシー

    • Allow Flock Behavior 移動方向が近接距離にいる他のエージェントに影響されます。
    • Priority 優先度が高い((高い( 値の)エージェントは、回避を実行するときに他のエージェントの影響を受けません。
  • Avoidance Experimental (Quantum 1.2.4以降)

    • Priority 優先度の高い(高い 値の)エージェントは、優先度の低いエージェントと回避率を25%から75%に分割します。
    • Avoidance Quality None: 回避はオフ、Low: レガシー回避、Medium: 最小候補、Good: 最高の費用対効果、High = 実験的
    • Max Avoidance Candidates 相互に影響を与えるエージェントの品質と数に応じて、スムーズな回避を維持するにはこの値を増やす必要があります。
    • Clamp Agent To Navmesh を使用して、エージェントが積極的に回避してnavmesh内に留まるようにします。
    • Clamp Agent Radius Threshold 最適化されたクランプ計算を使用するエージェントの最小半径です(NavMesh.MinAgentRadiusを減算)。小さなエージェントがnavmeshの外に出る場合、これを増やすことでエージェントを含みます。
    • Clamp Agent Correction は、各ティックに適用される修正の割合です。 これを増やすと、浸透がさらに少なくなります。
    • Reduce Avoidance At Waypoints これを有効にして、ウェイポイントに近づくときのエージェントの回避を減らします。移動エージェントがエージェントがnavmeshから外れるのを緩和している場合にのみ使用します。これに代わるものはまだ開発中です。物理的なボディを使用してウェイポイントで立ち往生しているエージェントを緩和する場合は、Dynamic Line Of Sight Waypoint Rangeを設定してみてください。
    • Reduce Avoidance Factor これはエージェントの半径と乗算され、回避の影響が二次的に減少する距離を表します。
  • ブレイク

    • BreakingDistance 0に設定した場合(デフォルトおよび推奨)、エージェントは現在の速度を基準として使用してブレイクを開始します。 距離をオーバーライドするには、この値を0より上に設定します。
    • BreakingOnWaypoint 有効にすると、エージェントはウェイポイントの範囲に到達したときにブレイクを適用します。
    • BreakingOnWaypointFactor ウェイポイントに適用されるブレイクの量を減らします。
    • BreakingOnWaypointMinAngleRad ウェイポイントでのブレイクを適用する必要がある場合の最小角度を設定します。

SimluationConfig.NavMeshAgent

navmesh simulation config
SimluationConfig.NavMeshAgent
  • Proximity Factor (Quantum 1.2.3以前) 近接と見なされるエージェント間の距離。2つのエージェントを組み合わせた半径で乗算されます。衝突回避ベクトルの計算中に近隣エージェントを照会するための最大距離として使用されます。大きいほど、より正確に回避できますが、より多くのCPUを使用します。
  • Avoidance Range (Quantum 1.2.4以降) 2つのエージェント間の距離から、お互いに影響を及ぼし始めたときの半径を引いたもの。
  • Update Interval は、アップデート動作を参照します。(1 = 1ティックごと、2 = 2ティックごと)
  • Default Nav Mesh Agent この設定は、特定の設定に割り当てられていない全てのエージェントに使用されます。
  • Use Legacy Avoidance (since Quantum 1.2.4以降) これを無効にすると、新しい回避システムが有効になります。

C#

public struct NavMeshAgent {

    // Agent linear velocity
    FPVector2 CurrentVelocity;

    // Target position that has be set
    FPVector2 Target { get; }

    // Target position that may have been corrected
    FPVector2 InternalTarget { get; }

    // Is the agent working
    bool IsActive { get; set; }

    // Set the config also when it is running
    NavMeshAgentConfig AgentConfig { get; set; }

    // Access how many waypoints the agent currently uses (max 4)
    int MaxWaypoints { get; }

    // Access what waypoint the agents currently steers towards (-1 is inactive)
    int CurrentWaypoint { get; }

    // Use this to initilizes the agent and set the config
    void Init(NavMeshAgentConfig agentConfig = null);

    // See below
    void SetTarget(FPVector2 target, NavMesh navMesh, bool resetSpeed);

    // Triggers a repath next tick (internally sets CurrentWaypoint to -1)
    void ForceRepath();

    // Access agent waypoint positions (throws when index > MaxWaypoints)
    FPVector2 GetWaypoint(int index)
}

SetTarget(target, navMesh, resetSpeed)

ナビゲーションエージェントの目的のターゲットを設定します。

  • target 2Dにおけるエージェントのターゲットです。
  • navMesh ナビゲーションに用いられるNavMeshです。
  • resetSpeed 再ターゲット化する際は現在のエ-ジェントの速度はリセットされます。

NavMeshは、マップのディクショナリ(NavMeshAsset.Nameをキーとして使用)に保管されます。以下に従ってアクセスしてください。

C#

Frame.Map.NavMeshes["NavMeshName"];

C#

public class NavMesh : AssetObject {

    // The path finder object
    NavMeshPathFinder PathFinder;

    // Makes sure that the position is on the map grid.
    void ClampToGrid(ref FPVector2 position);

    // Will return true is the a triangle can be found in the NavMesh at this position (uses FindTriangle internally).
    // If inclusive is false, positions what lie exactly on the triangle edge will return false.
    bool Contains(FPVector2 position, INavMeshRegionMask areaMask, Boolean inclusive = false);

    // Same as the above. The y-component of the 3D position is set to zero.
    bool Contains(FPVector3 position, INavMeshRegionMask areaMask, Boolean inclusive = false);


    // Will return true if the position is inside the map grid. Sets the out parameters to the closest triangle vertices.
    bool FindClosestTriangle(FPVector2 position, out FPVector2 v0, out FPVector2 v1, out FPVector2 v2, INavMeshRegionMask regionMask)

    // Will return a triangle index >= 0 when the position could be moved to inside the navmesh (closestPosition).
    // The position is tried to be moved minDistanceToBorder units into the navmesh.
    // Cell arround the position are checked when maxCellsChecked < 0.
    Int32 FindClosestTriangle(FPVector2 position, FP minDistanceToBorder, Int32 maxCellsChecked, INavMeshRegionMask regionMask, out FPVector2 closestPosition)

    // Returns true is the two positions are in line of sight of each other on the NavMesh.
    bool LineOfSight(FPVector2 p0, FPVector2 p1, INavMeshRegionMask regionMask);
    bool LineOfSight(FPVector2 p0, FPVector2 p1, INavMeshRegionMask regionMask, ref FPVector2 hit);

    // Same as the above. The y-component of the 3D position is set to zero.
    bool LineOfSight(FPVector3 p0, FPVector3 p1, INavMeshRegionMask regionMask);
    bool LineOfSight(FPVector3 p0, FPVector3 p1, INavMeshRegionMask regionMask, ref FPVector3 hit);

    // Use this to convert region ids to cache its index
    Dictionary<string, NavMeshRegion> RegionMap { get; }

    // Check is region is active
    bool IsRegionActive(INavMeshRegionMask f, byte index);
    bool IsRegionActive(INavMeshRegionMask f, string id);
    bool IsRegionActive(INavMeshRegionMask f, ulong flags);

    // Toggle a region
    void SetRegionActive(INavMeshRegionMask f, ulong flags, bool isActive);
    void SetRegionActive(INavMeshRegionMask f, byte index, bool isActive);
    void SetRegionActive(INavMeshRegionMask f, string id, bool isActive);
}

LineOfSight(p0, p1, regionMask, ref hit)

  • Frameクラスは INavMeshRegionMaskインターフェースを実装します。これは、有効なリージョンを確認するために必要です。
  • 任意で、p0に最も近いヒット位置を生成します。

全てのナビゲーションメッシュにインスタンス化したパスファインダーがあります。NavMesh.PathFinder経由でアクセス可能です。

C#

public class NavMeshPathFinder : IDisposable {

    // Funneled path
    int PathSize { get; }
    NavMeshPathVertex[] Path { get; }

    // Pathfinder raw path
    int RawPathSize { get; }
    NavMeshPathVertex[] RawPath { get; }

    // Find a path from start to end (check PathSize > 1 for progress)
    FPVector2 FindPath(FPVector2 start, FPVector2 end, FP epsilon, Int32 findValidTargetCellRange, INavMeshRegionMask regionMask, PathQuality quality = PathQuality.Good, NavMesh navmesh = null);

    // Same as the above. The y-component of the 3D position is set to zero.
    FPVector3 FindPath(FPVector3 start, FPVector3 end, FP epsilon, Int32 findValidTargetCellRange, INavMeshRegionMask regionMask, PathQuality quality = PathQuality.Good, NavMesh navmesh = null);

    // Sets the navmesh the path finder operates on.
    void SetNavMesh(NavMesh navmesh);
}

FindPath(start, end, epsilon, findValidTargetCellRange, regionMask, quality, navmesh)

navmeshの外側にある場合、正しい終了位置を返します。

  • epsilon ターゲットをnavmeshの境界からnavmesh内に移動するためのオフセット。0より大きいはずです。エージェントは、半径からNavmesh.MinAgentRadiusを引いた値を使用します。
  • findValidTargetCellRange 0より大きい場合:位置がオフメッシュの場合、周囲のセルの境界交差が確認されます。
  • regionMask FrameクラスはINavMeshRegionMaskインターフェースを実装します。これは、有効なリージョンを確認するために必要です。
  • quality PathQualityは、ファネルアルゴリズムがヒューリスティックを使用する範囲を定義します。
  • navmesh オプションで、navmeshを渡します。内部的にSetNavMesh()が呼び出されます。

ISignalOnNavMeshTargetReached は、エージェントがNavMeshAgentConfigAsset上で Epsilon パラメータを使用して目標のターゲットに到達したとき呼び出されます。

ISignalOnNavMeshUpdateSteering (Quantum 1.2.3以降)内部の進行システムを上書きするのに使用します。以下の例を参照してください。

これは全てのエージェントに対し一度呼ばれます。ここでブランチを切るとエージェントごとに別のロジックを作成できます。SimulationConfig内でNavMeshAgent-UpdateIntervalを設定すると、このコールバックが、内部バージョンと同様に各エージェントに対し低頻度で呼ばれます。

NavMeshAgent.SetTarget()を呼び出せば、ここからパスファインダーをリセットできます。強制的に再パスを行う場合は、NavMeshAgent.ForceRepath()を呼び出します。

下記のサンプルでの操作(steering)と回避は、次の設定でうまく実行できます:

    • エージェントには動的または運動学的コライダーがあります
  • AgentConfig.UsePhysics = True
  • SimulationConfig.ProximityFactor = 2 および UpdateInterval = 1
  • PhysicsMaterial.Drag = 4

C#


  public struct NavMeshAgentSteeringData {
    FPVector3 Waypoint;          // Current waypoint
    Boolean IsTarget;            // Is the waypoint the final target
    FP BreakingFactor;           // Internally calculated breaking factor
    FPVector3 AvoidanceVector;   // Accumulated avoidance forces
    bool IgnoreInternalSteering; // Set this to true to deactivate the internal steering
  }

C#

public unsafe class MyNavSystem : SystemBase, ISignalOnNavMeshUpdateSteering {

    public void OnNavMeshUpdateSteering(Frame f, NavMeshAgent* agent, Entity* entity, NavMeshAgentSteeringData* data) {

      // This is important as it will disable the internal steering. Otherwise your changes will be overwritten.
      data->IgnoreInternalSteering = true;

      var transform = Entity.GetTransform2D(entity);
      var direction = (data->Waypoint.XZ - transform->Position).Normalized;

      // Add the avoidance vector into our desired moving direction
      // Use the dot product with the agents right vector to only take lateral avoidance into account.
      var avoidance = data->AvoidanceVector.XZ;
      var localRight = FPVector2.Rotate(FPVector2.Right, transform->Rotation);
      var dot = FPVector2.Dot(localRight, avoidance);
      direction += localRight * dot;

      // agent->CurrentVelocity is used to apply velocity to the agent.
      // You can also set the velocity on the dynamic body directly, but then you have to disable UsePhysics on the NavMeshAgentConfigAsset.
      agent->CurrentVelocity = direction.Normalized * agent->AgentConfig.Speed;

      // Set the direction that the actor is facing or set it on the dynamic body directly
      // The dynamic body might still be rotating by itself after steering because it received angular rotation velocity due to physics contacts.
      // Reset it manually or switch rotations off completely.
      transform->Rotation = FPMath.Lerp(transform->Rotation, FPVector2.RadiansSigned(FPVector2.Up, direction), f.DeltaTime * agent->AgentConfig.AngularSpeed);
    }
}

OnNavMeshUpdateAvoidance (Quantum 1.2.3以降) 内部回避ベクトル計算を上書きします。下記のサンプルを参照してください。

このコールバックは、SimulationConfig.ProximityFactor値域に定義された同じセルの中にあるエージェントのペアごとに一度呼ばれます。

C#

  public struct NavMeshAgentAvoidanceData
  {
    FPVector3 AvoidanceVectorA;
    FPVector3 AvoidanceVectorB;
    Boolean IgnoreInternalAvoidance;
  }

C#

public unsafe class MyNavSystem : SystemBase, ISignalOnNavMeshTargetReached, ISignalOnNavMeshUpdateAvoidance {

    public void OnNavMeshUpdateAvoidance(Frame f, NavMeshAgent* agentA, Entity* entityA, NavMeshAgent* agentB, Entity* entityB, NavMeshAgentAvoidanceData* data) {

      // This will deactivate the internal avoidance calculation that would happen afterwards.
      // You can still add avoidance here and let the internal avoidance add even more.
      data->IgnoreInternalAvoidance = true;

      // This is a very simliplied approach to calculate an avoidance vector for this particular pair.
      // AvoidanceVectorA set here for example will be added to the the accumulated NavMeshAgentSteeringData.AvoidanceVector in the steering callback.
      // Additional features could be taking the agents looking direction or agent->CurrentVelocity into account.
      // Or using a cross vector to improve the evasion actors facing each other.
      // Or adding agent priority, ...
      var posA = Entity.GetTransform2D(entityA)->Position;
      var posB = Entity.GetTransform2D(entityB)->Position;
      var direction = posB - posA;
      var dist = direction.Magnitude;
      direction = direction.Normalized / dist;
      data->AvoidanceVectorA = -direction.XOY;
      data->AvoidanceVectorB = direction.XOY;
    }
}
Back to top