Unityによるパックマンの作り方 ~ゴーストの状態~
スポンサードリンク
IStateを継承したクラスで、ゴーストの状態の遷移と、そのときの行動を処理します。Dictionary<State, IState>とenum Stateで各stateを登録・参照し、Mover、Animatorで移動、アニメーションを行います。
移動に関しては下記を参照してください。
アニメーションに関しては下記を参照してください。
public interface IState { //パックマンに接触したときの処理を登録。 event EventHandler OnTouch; //毎フレーム実行する処理(移動、アニメーション) IState Excute(); //恐慌状態になったときの処理。 IState Frighten(float span); //恐慌状態が終わったときの処理。 IState Calm(); //パックマンに接触したときにOnTouchイベントを実行。 IState Eaten(); //ワープしたときの処理。 void Warp(Vector2 pos, float span); //アニメーションの処理。 void Animate(); }
//待機、準備、巡回、追跡、恐慌、帰還、死亡 public enum State { Wait, Init, Scatter, Chase, Scare, Return, Dead };
public class Wait : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Queue<Vector2> waypoints; private readonly Mover mover; private readonly Animator animator; //待機期間。 private readonly float span; //待機終了時間。 private float limitTime; public Wait(Mover mover, Dictionary<State, IState> states, Animator animator, Queue<Vector2> waypoints, float span) { this.mover = mover; this.states = states; this.animator = animator; this.waypoints = waypoints; this.span = span; mover.SetWaypoints(waypoints); states.Add(State.Wait, this); } //一定時間巣で待機した後、準備状態になる。 public IState Excute() { if (limitTime < Time.fixedTime) { var next = states[State.Init] as Init; next.SetState(); return next; } mover.MoveToWaypoints(true); return this; } public void SetState() { limitTime = Time.fixedTime + span; mover.SetWaypoints(waypoints); } public IState Frighten(float scareSpan) { var next = states[State.Scare] as Scare; next.SetState(this, scareSpan); return next; } public IState Calm() { return this; } //アカベイだけは待機中も巣の外にいるので食べられる可能性がある。 public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } //待機中ワープは絶対しない。 public void Warp(Vector2 pos, float warpSpan) { throw new System.NotImplementedException(); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }
public class Init : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Queue<Vector2> waypoints; private readonly Mover mover; private readonly Animator animator; public Init(Mover mover, Dictionary<State, IState> states, Animator animator, Queue<Vector2> waypoints) { this.mover = mover; this.states = states; this.animator = animator; this.waypoints = waypoints; states.Add(State.Init, this); } //巡回の最初のWaypointへ移動し、到着したら巡回状態になる。 public IState Excute() { IState next = this; mover.MoveToWaypointsThen(() => { var scatter = states[State.Scatter] as Scatter; scatter.SetState(); next = scatter; }); return next; } public void SetState() { mover.SetWaypoints(waypoints); } public IState Frighten(float scareSpan) { var next = states[State.Scare] as Scare; next.SetState(this, scareSpan); return next; } public IState Calm() { return this; } public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } //準備中ワープは絶対しない。 public void Warp(Vector2 pos, float warpSpan) { throw new System.NotImplementedException(); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }
public class Scatter : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; //巡回する座標。 private readonly Queue<Vector2> waypoints; private readonly Mover mover; private readonly Animator animator; //巡回期間。 private readonly float span; //巡回終了時間。 private float limitTime; public Scatter(Mover mover, Dictionary<State, IState> states, Animator animator, Queue<Vector2> waypoints, float span) { this.mover = mover; this.states = states; this.animator = animator; this.waypoints = waypoints; this.span = span; states.Add(State.Scatter, this); } //一定時間巡回した後、追跡状態になる。 public IState Excute() { if (limitTime < Time.fixedTime) { var next = states[State.Chase] as Chase; next.SetState(); return next; } mover.MoveToWaypoints(true); return this; } public void SetState() { limitTime = Time.fixedTime + span; mover.SetWaypoints(waypoints); } public IState Frighten(float scareSpan) { var next = states[State.Scare] as Scare; next.SetState(this, scareSpan); return next; } public IState Calm() { return this; } public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } //巡回中ワープは絶対しない。 public void Warp(Vector2 pos, float warpSpan) { throw new System.NotImplementedException(); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }
public class Chase : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Mover mover; private readonly Animator animator; private readonly float span; private readonly SpriteRenderer sprite; //ワープ中かどうか。 private bool warped; //残り時間。 private float leftSpan; //追跡終了時間。 private float limitTime; //ワープ終了時間。 private float warpEndTime; public Chase(Mover mover, Dictionary<State, IState> states, Animator animator, SpriteRenderer sprite, float span) { this.mover = mover; this.states = states; this.animator = animator; this.sprite = sprite; this.span = span; states.Add(State.Chase, this); } //一定時間追跡した後、帰還状態になる。 public IState Excute() { if (!warped) { if (limitTime < Time.fixedTime) { leftSpan = 0; var next = states[State.Return] as Return; next.SetState(); return next; } else { mover.MoveToWaypoints(State.Chase); return this; } } else //ワープ中。 { if (warpEndTime < Time.fixedTime) { sprite.enabled = true; warped = false; limitTime = leftSpan + Time.fixedTime; } return this; } } public void SetState() { limitTime = Time.fixedTime + span; } public IState Frighten(float scareSpan) { //追跡時間を延長。 limitTime += scareSpan; var next = states[State.Scare] as Scare; next.SetState(this, scareSpan); return next; } public IState Calm() { return this; } public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } public void Warp(Vector2 pos, float warpSpan) { sprite.enabled = false; //追跡の残り時間を記録。 leftSpan = Mathf.Max(limitTime - Time.fixedTime, 0); warpEndTime = Time.fixedTime + warpSpan; warped = true; mover.Warp(pos); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }
public class Scare : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Mover mover; private readonly Animator animator; private readonly float defaultSpeed; private readonly SpriteRenderer sprite; //元の状態。 private IState preState; //ワープ中かどうか。 private bool warped; //恐慌終了時間。 private float limitTime; //ワープ終了時間。 private float warpEndTime; public Scare(Mover mover, Dictionary<State, IState> states, Animator animator, SpriteRenderer sprite) { this.mover = mover; this.states = states; this.animator = animator; this.sprite = sprite; defaultSpeed = mover.Speed; states.Add(State.Scare, this); } public IState Excute() { IState state = this; if (!warped) { if (preState is Wait || preState is Init || preState is Scatter) { //元の状態での行動を続ける。 preState = preState.Excute(); } else { //ランダムに移動。 mover.MoveToWaypoints(State.Scare); } } else //ワープ中。 { if (warpEndTime < Time.fixedTime) { sprite.enabled = true; warped = false; } } return state; } public void SetState(IState preState, float span) { this.preState = preState; limitTime = Time.fixedTime + span; mover.Speed = defaultSpeed * 0.8f; animator.SetBool("IsScare", true); } //恐慌中にパックパンがパワーエサを食べたら、恐慌時間は上書き。 public IState Frighten(float span) { limitTime = Time.fixedTime + span; return this; } public IState Calm() { mover.Speed = defaultSpeed; sprite.enabled = true; warped = false; animator.SetBool("IsScare", false); animator.SetBool("IsCalmSoon", false); //恐慌状態が終わったら、元の状態になる。 return preState; } public IState Eaten() { OnTouch(this, EventArgs.Empty); var next = states[State.Dead] as Dead; next.SetState(); return next; } public void Warp(Vector2 pos, float warpSpan) { sprite.enabled = false; warpEndTime = Time.fixedTime + warpSpan; warped = true; mover.Warp(pos); } public void Animate() { //残り期間が3秒をきったら点滅。 if (limitTime - Time.fixedTime < 3) { animator.SetBool("IsCalmSoon", true); } else { animator.SetBool("IsCalmSoon", false); } } }
public class Dead : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Mover mover; private readonly Animator animator; //巣の付近の座標。 private readonly Queue<Vector2> nestWaypoints; private readonly SoundManager soundManager; private readonly float defaultSpeed; //巣の近くにいるかどうか。 private bool isArriveNearNest; public Dead(Mover mover, Dictionary<State, IState> states, Animator animator, Queue<Vector2> nestWaypoints) { this.mover = mover; this.states = states; this.animator = animator; this.nestWaypoints = nestWaypoints; defaultSpeed = mover.Speed; states.Add(State.Dead, this); soundManager = SoundManager.Instance; } public IState Excute() { IState next = this; //巣の近くに来るまでの移動。 if (!isArriveNearNest) { mover.MoveToWaypointsThen(nestWaypoints.Peek(), () => isArriveNearNest = true); } else //巣の付近での移動。巣に戻ったら、待機状態になる。 { mover.MoveToWaypointsThen(() => { mover.Speed = defaultSpeed; isArriveNearNest = false; var wait = states[State.Wait] as Wait; wait.SetState(); next = wait; animator.SetBool("IsDead", false); soundManager.StopSE(); }); } return next; } public void SetState() { mover.SetWaypoints(nestWaypoints); mover.Speed = defaultSpeed *2f; animator.SetBool("IsScare", false); animator.SetBool("IsCalmSoon", false); animator.SetBool("IsDead", true); soundManager.PlaySE("GhostDead", 0.2f); } public IState Frighten(float scareSpan) { return this; } public IState Calm() { return this; } public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } public void Warp(Vector2 pos, float warpSpan) { mover.Warp(pos); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }
public class Return : IState { public event EventHandler OnTouch; private readonly Dictionary<State, IState> states; private readonly Mover mover; private readonly Animator animator; private readonly Vector2 scatterPoint; private readonly SpriteRenderer sprite; private Scatter next; //ワープ中かどうか。 private bool warped; //ワープ終了時間。 private float warpEndTime; public Return(Mover mover, Dictionary<State, IState> states, Animator animator, SpriteRenderer sprite, Vector2 scatterPoint) { this.mover = mover; this.states = states; this.animator = animator; this.sprite = sprite; this.scatterPoint = scatterPoint; states.Add(State.Return, this); } //巡回開始の座標まで移動。到着後は巡回状態になる。 public IState Excute() { IState state = this; if (!warped) { if (next == null) { mover.MoveToWaypointsThen(scatterPoint, () => { next = states[State.Scatter] as Scatter; next.SetState(); }); } else state = next; } else //ワープ中。 { if (warpEndTime < Time.fixedTime) { sprite.enabled = true; warped = false; } } return state; } public void SetState() { next = null; } public IState Frighten(float scareSpan) { var next = states[State.Scare] as Scare; next.SetState(this, scareSpan); return next; } public IState Calm() { return this; } public IState Eaten() { OnTouch(this, EventArgs.Empty); return this; } public void Warp(Vector2 pos, float warpSpan) { sprite.enabled = false; warpEndTime = Time.fixedTime + warpSpan; warped = true; mover.Warp(pos); } public void Animate() { animator.SetFloat("DirX", mover.Direction.x); animator.SetFloat("DirY", mover.Direction.y); } }