概要
ゴーストが移動できる座標(Waypoint)のリスト(Waypoints)とWaypointに向かって移動させるMoverクラスが必要になります。MoverクラスでWaypointへの移動を繰り返すことでゴーストを移動させます。
Waypointsの設定・取得
ゴーストの状態によりWaypointsの取得の仕方が異なります。
待機・準備・巡回状態でのWaypoints
ゴーストの状態が待機・準備・巡回の場合、あらかじめWaypointsを設定しておきます。1匹のゴーストが複数のWaypointsを保持する必要があるので、ScriptableObjectを使って各状態のWaypointsを保持するGhostWaypointsDataを作成します。これらをMazeオブジェクトにセットします。ついでにスタート位置もここで設定します。
[CreateAssetMenu(menuName = "Scriptable/Create GhostWaypointsData")]
public class GhostWaypointsData : ScriptableObject
{
public Vector2 Start;
public List<Vector2> Wait;
public List<Vector2> Init;
public List<Vector2> Scatter;
}
各ゴーストのGhostWaypointsDataをMazeオブジェクトにセットします。
死亡状態でのWaypoints
今回使用した迷路はゴーストの巣の入り口正面のWaypointが整数にならず、追跡状態のようにAIで目的地まで移動させることができません。よって、あらかじめNestWaypointsを設定して、Mazeオブジェクトにセットしておきます。迷路の形にこだわらければ、巣の位置を調整してWaypointが整数になるようにしてもよいと思います。
追跡・恐慌・帰還状態でのWaypoints
追跡・恐慌・帰還状態ではAI(といってもかなりしょぼい)によって経路を決定します。ゴーストのAIについては
下記を参考にしてください。
nullsuke.com
Moverの作成
WaypointへVector2.MoveTowardsとrigid2D.MovePositionを使い移動させるクラスです。
public class Mover : MonoBehaviour
{
[SerializeField] float defaultSpeed = default;
private Queue<Vector2> waypoints;
private AAI ai;
private Rigidbody2D rigid2D;
private Vector2 waypoint;
public Vector2 Direction { get; private set; }
public float Speed { get; set; }
public void Initialize(AAI ai)
{
this.ai = ai;
Speed = defaultSpeed;
}
private void Awake()
{
rigid2D = GetComponent<Rigidbody2D>();
}
public void SetWaypoints(Queue<Vector2> waypoints)
{
this.waypoints = new Queue<Vector2>(waypoints);
}
public void MoveToWaypoints(bool loop = false)
{
if (waypoints.Count == 0) return;
waypoint = waypoints.Peek();
if (Vector3.Distance(transform.localPosition, waypoint) > float.Epsilon)
{
MoveTo(waypoint);
}
else
{
if (loop) waypoints.Enqueue(waypoints.Dequeue());
else waypoints.Dequeue();
}
}
public void MoveToWaypoints(State state)
{
if (Vector3.Distance(transform.localPosition, waypoint) > float.Epsilon)
{
MoveTo(waypoint);
}
else
{
waypoint = ai.GetWaypoint(Direction, state);
}
}
public void MoveToWaypointsThen(Vector2 tp, Action callback)
{
if (Vector3.Distance(transform.localPosition, waypoint) > float.Epsilon)
{
MoveTo(waypoint);
}
else
{
if (Vector3.Distance(transform.localPosition, tp) > float.Epsilon)
{
waypoint = ai.GetWaypoint(Direction, null, tp);
}
else callback();
}
}
public void MoveToWaypointsThen(Action callback)
{
if (waypoints.Count == 0)
{
callback();
return;
}
MoveToWaypoints();
}
private void MoveTo(Vector2 pos)
{
if (pos != (Vector2)transform.localPosition)
{
Direction = Vector3.Normalize(pos - (Vector2)transform.localPosition);
}
var wp = transform.parent.TransformPoint(pos);
var p = Vector2.MoveTowards(transform.position, wp, Speed);
rigid2D.MovePosition(p);
}
}