Navmeエージェントを作成する
Quantum 2.0以降、navmeshエージェントは複数のコンポーネントに別れました。我々は、navmeshおよびステアリングを使用している開発者が動きの最終結果のコントロールを希望していることに気が付きました。これはしばしばゲーム体験に極めて重要な要素ですので、開発者がこのように希望することはもっともなことです。新しいnavmeshエージェントパーツは開発者がnavmeshサポートの組み合わせを、マルチスレッドパフォーマンスを行わずに、また必要のないパーツを実行したり必要のないメモリ消費を行わずに選ぶ手助けになります。
エージェントコンポーネントはNavMeshPathfinder
、NavMeshSteeringAgent
、 NavMeshAvoidanceAgent
の3つで、NavMeshAvoidanceObstacle
はスタンドアロンのコンポーネントです。
エージェントエンティティはの作成方法は2通りあります。UnityでEntity Prototypesを使用するか、コードでエンティティを組み立てるかです。これらの方法では、NavMeshAgentConfig
Quantumアセットを使用します。
UnityのEntity Prototypesでエージェントを作成する
- Unityメニューを介してからQuantum prototypeを作成する :
GameObject/Quantum/Empty Entity
- エンティティを選択し、
Transform
を2D
に設定する NavMeshPathfinder
コンポーネントをトグルする- デフォルト
NavMeshAgentConfig
を選択する Initial Target
をトグルし、Unityシーンからトランスフォームを選択し移動先位置に初期位置を提供する- ベイクしたQuantum navmeshを選択する (Navmeshワークフローを参照すること)
- デフォルト
NavMeshSteeringAgent
でトグルする- パスギズモを確認するには:
NavMeshAgentConfig
またはデフォルトのShow Debug Steering
を有効化するQuantumEditorSettings
でNavmeshギズモDraw Pathfinder Funnel
を有効化する
- 再生を押す
コード内のコンポーネントでエージェントを作成する
もう一つの手段として、コードでエージェントエンティティを組み立てることもできます。
初めに、エンティティには Transform2D
または Transform3D
コンポーネントが必要となります。 View
コンポーネントを追加することでプレハブがシーンにレンダリングされます。
最も重要なコンポーネントは NavMeshPathfinder
です。これはパスファインディングを行い、目的の位置とユーザー定義済みのウェイポイント数を蓄積してウェイポイントの進行を見つけます。このコンポーネントは、NavMeshPathfinder.Create()
Factoryメソッドで、NavMeshAgentConfig
内にパスして作成する必要があります。
NavMeshSteeringAgent
コンポーネントは任意で、NavMeshPathfinder
を必要とします。これには最高速度、加速度、回転速度の変数があり、いずれもランタイム中に変更可能で、パスに沿ってエンティティをステアリングします。このコンポーネントを使用しない代わりに、開発者はMovementType
をCallback
に変更し、最新の回避データを持ちながら独自の動作を挿入できます。回転速度や加速を無効にするには、0に設定します。
NavMeshAvoidanceAgent
は、このコンポーネントに先立ってエンティティ上でSet()
する必要のあるNavMeshPathfinder
およびNavMeshSteeringAgent
の両方のコンポーネントを要件とします。このエージェントは回避と計算を行い、プライオリティおよびマスクとレイヤーでのフィルタリングを用いて、他の動いているエージェント(HRVO)を回避します。初めにNavMeshAgentConfig
プライオリティで設定を行います。マスクとレイヤーはランタイム中にコンポーネント上で変更可能です。
エージェントが物体にステアリングされることで、例えばエージェントが静的コリジョンを貫通しないようにするには、エンティティに PhysicsCollider2D/3D
および PhysicsBody2D
が必要となります。これを有効にするには、NavMeshAgentConfig
でMovementType
をDynamicBody
に設定する必要があります。
C#
public override void OnInit(Frame f) {
base.OnInit(f);
var entity = f.Create();
f.Set(entity, new Transform3D() { Position = FPVector3.Zero, Rotation = FPQuaternion.Identity });
var config = f.FindAsset<NavMeshAgentConfig>(NavMeshAgentConfig.DEFAULT_ID);
var pathfinder = NavMeshPathfinder.Create(f, entity, config);
// find a random point to move to
var navmesh = f.Map.NavMeshes["Navmesh"];
if (navmesh.FindRandomPointOnNavmesh(FPVector2.Zero, FP._10, f.RNG, *f.NavMeshRegionMask, out FPVector2 randomPoint)) {
pathfinder.SetTarget(f, randomPoint, navmesh);
}
f.Set(entity, pathfinder);
f.Set(entity, new NavMeshSteeringAgent());
}
NavMeshエージェントギズモDraw Nav Mesh Agents
をアクティベートして、エージェントギズモがシーンウィンドウで描画できるようにします。
重要なエージェント設定
###パスファインダー###
NavMeshPathfinder.SetConfig()
はコンポーネント作成およびランタイムの間に実行できます。現在、エージェントがパスやウェイポイントに従っているのであれば、新しいコンフィグからのカウントは異なり、パスはリセットされます。コンフィグは得エンティティのNavMeshSteeringAgent
とNavMeshAvoidanceAgent
コンポーネント上で自動的にアップデートされ、Speed、Acceleration、AvoidancePriority、Layer、Maskの値はコンフィグ値にリセットされます。
NavMeshAgentConfig.MaxRepathTimeout
は、ウェイポイントがこの時間に到達しない場合、エージェントパスファインディングをトリガーする秒数です。これはフェイルセーフ的なもので、行き詰ったエージェントを解放します。無効にするには、値を0に設定します。
NavMeshAgentConfig.LineOfSightFunneling
は、メインのnavmeshの真ん中に配置されたnavmeshリージョンが使用された場合にアクティベートします。例えば、破壊できるビルなどです。リージョンによって導入された追加のトライアングルは、アクティブなリージョンの近くで少々奇妙なパスになることがあります。このオプションによって、リージョンの近くの不必要なウェイポイントが取り除かれます。
NavMeshAgentConfig.DynamicLineOfSight
は、ウェイポイントが各ティックでスキップ可能かどうかエージェントチェックを行います。このオプションはコストがかかりますが、パス上にある不必要なウェイポイントをすべて取り除きます。
NavMeshAgentConfig.DynamicLineOfSightWaypointRange
が設定されると、サイトチェックの行がウェイポイント(範囲)に近い場合のみ各ティックで実行されます。DynamicLineOfSight
が有効化されていなくてもこのように動作します。
NavMeshAgentConfig.FindValidTargetCellRange
は、SetTarget()
中にnavmesh外の位置が選択された場合に有用です。範囲パラメータが隣合わせのセルを検索して、navmesh内にある目標の最適な置換を行います。
以下の画像で、黄色のxでマークされた目標をご覧ください。特定のセル範囲で検索されたセルには数字が入っています。例えば、0
のセル範囲は目的地を見つけられなかったセルです。一方で範囲1
は、黄色の点線で書かれているようにnavmesh上で最も近い位置を見つけます。セル範囲を不合理に大きくするとパフォーマンスに影響しますので注意してください。
NavMeshAgentConfig.CachedWaypointCount
を用いてNavMeshPathfinder
上でキャッシュされるウェイポイントの数を設定します。一時的ではないデータを多く保管すると、シミュレーションの速度が遅くなるのでご注意ください。はじめにキャッシュに保管されたウェイポイントが、SetTarget()
が呼び出されたときのエージェントの現在の位置で、ウェイポイント到達検出を強化するのに使用します。エージェントが最後のウェイポイントに向かってステアを開始すると、自動的に再度パスファインディングが行われ、エージェントがフレームの計算時に有効なウェイポイントを持たない状況を解決します。
ウェイポイント到達検出を有効にすると、エージェントがウェイポイントに到達するのに問題がある場合、 NavMeshAgentConfig.EnableWaypointDetection
に便利です(例えば、回転速度が遅いまたは回避など)。それに続くパラメータAxis Extend
およびAxis Offset
はウェイポイント到達検出軸(黒い線)を定めています。エージェントが黄色のゾーンに入ると、ウェイポイントに到達したとみなされます。
パスファインダーコンポーネントに付随するステアリングコンポーネントがない場合、ウェイポイント到達検出を実行するのに DefaultWaypointDetectionDistance
が使用され、エージェントの最大速度×デルタ時間に設定されます。ウェイポイント到達検出の拡張方法については「便利なNavmeshエージェント設定」セクションを参照してください。
エージェントをステアリングする
NavMeshAgentConfig.StoppingDistance
および AutoBraking
は、最終目標に近づきつつあるエージェントに適用されます。 StoppingDistance
は、エージェントが目的地の前で停止する絶対的な距離です。この値を設定するとエージェントが安定してオーバーシュートしないようにすることができます。残りの距離が、エージェントの現在のティック毎の移動距離よりも少なくなると、エージェントは常に停止します。
AutoBraking
はどちらかというと視覚的な機能で、目的地に到着する前にエージェントの速度を落とし、エージェントの停止挙動を安定させるのにも使用できます。。AutoBrakingDistance
は、目的地の周りでエージェントが速度を落とし始める半径を決めます。内部的には、ブレーキングをスムーズに行うために、平方根が用いられます。
navmeshエージェントがMovementType PhysicsBody
を使用してジオメトリで跳ね返されない場合、特に回避を用いている時には、エージェントはnavmeshの外側に移動します。これを完全に防ぐには、エージェントをnavmeshのボーダーに沿ってスライドさせ、 NavMeshAgentConfig.ClampAgentToNavmesh
を有効にします。
エージェントの半径はnavmeshのMinAgentRadius
よりも大きい可能性があります。Quantumでは、エージェントのウェイポイントをボーダーよりも遠くに移動させることでこれに対応しています。ただし、これはエージェントをnavmeshにより複雑にクランプすることになり、パラメーター ClampAgentToNavmeshRadiusThreshold
がどのテクニックを使用すればいいか選択する助けになります。エージェントが小さい場合に半径を増やすと、navmeshの外側に言おうする傾向があります。
訂正を安定させるため、エージェントは侵入度全体の1パーセント(ClampAgentToNavmeshCorrection
)しか移動しません。
インターバルを更新する
パフォーマンスの最適化のため、シミュレーションティックのたびに経路探索と回避を**実行しない **ように個々のエージェント(config)は設定できます。NavMeshAgentConfig.UdpateInterval`を1より大きい値に設定すると、取得する更新の量が少なくなります。これはエージェントの応答性を低下させますが、CPU時間を節約することができます。エージェントエンティティインデックスは、更新する正確なティックを定義するために使用されるので、すべてのエンティティが同じティックで更新されるわけではありません。
数式は以下のとおりです:
C#
updateAgent = entity.Index % agentConfig.UpdateInterval == f.Number % agentConfig.UpdateInterval
- 1 = ティックのたびに更新
- 2 = ティック1つおきに更新
- 8 = ティック8つごとに更新、等
Navmeshエージェントコールバックを使用する
エージェントからのすべてのコールバックはメインスレッドから呼び出され、他のコンポーネントやエンティティにアクセスしたり記述する際に、マルチスレッディングの問題を引き起こしません。
ナビゲーションエージェントコールバックはオプトインされている必要があります。自分のシミュレーションコンフィグを開いて、Enable Navigation Callbacks
をトグルします。
次のシグナルは、エージェントをさらに制御する野に使用できる、差し迫ったフィードバックを提供します。
C#
namespace Quantum {
public unsafe partial class NavMeshAgentTestSystem : SystemMainThread,
ISignalOnNavMeshSearchFailed,
ISignalOnNavMeshWaypointReached,
ISignalOnNavMeshMoveAgent {
}
}
ISignalOnNavMeshSearchFailed
は、エージェントが現在の位置とSetTarget()
で設定された目標の間にパスを作成できなかった場合呼び出されます。例えば、目的地はnavmeshと一致させられません。このコールバックの間、SetTarget()
を実行する場合はresetAgent
パラメータをfalseに設定します。
ISignalOnNavMeshWaypointReached
はエージェントが目標へのパス上でウェイポイントに到達した場合に呼び出されます。ウェイポイントのTarget
、LinkStart
、LinkEnd
についての詳細は、WaypointFlags
列挙を確認してください。
ISignalOnNavMeshMoveAgent
が呼び出されるのはNavMeshAgentConfig.MovementType
がCallback
に設定されていてエージェントが
NavMeshSteeringAgent
コンポーネントを持っている場合のみです。desiredDirection
パラメータは、内部のステアリングや回避がエージェントの移動ベクトルであると認識する、標準化された方向です。
C#
public void OnNavMeshMoveAgent(Frame f, EntityRef entity, FPVector2 desiredDirection) {
var agent = f.Unsafe.GetPointer<NavMeshSteeringAgent>(entity);
// simple demonstration how to move the agent.
if (f.Has<Transform2D>(entity)) {
var transform = f.Unsafe.GetPointer<Transform2D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Y.RawValue = transform->Position.Y.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Rotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
} else if (f.Has<Transform3D>(entity)) {
var transform = f.Unsafe.GetPointer<Transform3D>(entity);
transform->Position.X.RawValue = transform->Position.X.RawValue + ((desiredDirection.X.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
transform->Position.Z.RawValue = transform->Position.Z.RawValue + ((desiredDirection.Y.RawValue * f.DeltaTime.RawValue) >> FPLut.PRECISION);
var desiredRotation = FPVector2.RadiansSignedSkipNormalize(FPVector2.Up, desiredDirection);
transform->Rotation = FPQuaternion.AngleAxis(desiredRotation * FP.Rad2Deg, -FPVector3.Up);
}
}
一般的なNavmeshエージェント設定
パスファインディングのみ
MapNavMeshPathfinder
コンポーネントを使用して、マルチスレッドパスファインディング、目標をウェイポイントの保存を実行し、ウェイポイントのインデックス進行を行います。自分のシステム内のステアリング、回避、移動を自分で制御します。
ウェイポイントのインデックス進行がパスファインダーコンポーネントを実行するには、ウェイポイントにどれくらいの速度で近づいているかの情報が要件となります。各フレームにWaypointDetectionDistanceSqr
プロパティを設定します。
回避なし
PathfinderコンポーネントとSteeringAgentコンポーネントのみを使用します。回避コードは1つも実行されず、コンポーネントは回避に関するデータを保存しません。SimulationConfig.Navigation.EnableAvoidance
をトグルオフして、CPU時間を節約してください。
Quantum Avoidanceを使用したカスタムムーブメント
3つのコンポーネントすべてを使用します(Pathfinder、SteeringAgent、AvoidanceAgent)。AvoidanceAgentsは、上書きしようとしてもSteeringAgentのパーツに依存します。NavMeshAgentConfig
内でMovementType
をCallback
に設定し、ISignalOnNavMeshMoveAgent
シグナル(前のセクションを参照)を実装します。desiredDirection
パラメータは移動方向を変更した回避も含んでいます。