敌人的行为逻辑
本课属于 Coddy Unity 学习旅程的“Unity 游戏架构”单元。第 39 课,共 64 课。
敌人的大脑是一个状态机,每种行为对应一个状态。这个骷髅会在几个据点之间来回行走,追逐进入 6 个单位范围内的玩家,并在靠近玩家时每秒挥剑一次。每个状态都会决定如何退出,骷髅则会保留它们共享的辅助方法:
public class Skeleton : MonoBehaviour
{
public Transform player;
public Transform[] posts;
public float speed = 2f;
public StateMachine machine = new StateMachine();
public IState patrol, chase, attack;
void Start()
{
patrol = new PatrolState(this);
chase = new ChaseState(this);
attack = new AttackState(this);
machine.ChangeState(patrol);
}
void Update() { machine.Tick(); }
public float PlayerDistance() { return Vector3.Distance(transform.position, player.position); }
public void WalkTo(Vector3 target)
{
target.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
public class PatrolState : IState
{
private Skeleton skel;
private int next;
public PatrolState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() < 6f) { skel.machine.ChangeState(skel.chase); return; }
Vector3 post = skel.posts[next].position;
post.y = skel.transform.position.y;
skel.WalkTo(post);
if (skel.transform.position == post) next = (next + 1) % skel.posts.Length;
}
}
public class ChaseState : IState
{
private Skeleton skel;
public ChaseState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
float d = skel.PlayerDistance();
if (d > 6f) { skel.machine.ChangeState(skel.patrol); return; }
if (d < 1.5f) { skel.machine.ChangeState(skel.attack); return; }
skel.WalkTo(skel.player.position);
}
}
public class AttackState : IState
{
private Skeleton skel;
private float cooldown;
public AttackState(Skeleton skel) { this.skel = skel; }
public void Enter() { cooldown = 0f; }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() > 1.5f) { skel.machine.ChangeState(skel.chase); return; }
cooldown -= Time.deltaTime;
if (cooldown <= 0f) { Debug.Log("Swing!"); cooldown = 1f; }
}
}这些状态只创建一次,并且会与骷髅共存,因此它们的字段会在每次进入状态之间保留。追逐结束后,PatrolState 的 next 仍会保持原值,骷髅会继续走向它原本前往的据点。Enter 只会重置应该重新开始的内容:AttackState 会将 cooldown 设为 0,因此骷髅一到玩家身边就会立刻挥出第一剑。
所有移动都通过 WalkTo 完成,此处使用的是 transform 上的 MoveTowards,它会穿过墙壁。如果改用 Rigidbody 移动骷髅,WalkTo 会设置刚体的速度,而骷髅会在 FixedUpdate 中更新状态机,此时 Time.deltaTime 表示物理步长。这三个状态无需改变:它们说明要去哪里,由所有者决定如何移动。
挑战
中等为骑士添加 PatrolState 和 ChaseState,并将骑士传给它们。
- 巡逻:走向
waypoints[next];距离小于0.3时,将next移到下一个位置,并循环回到开头;在到达下一个路点之前,保持当前目标不变。距离小于sightRange时,开始追逐。 - 追逐:走向玩家;距离大于
loseRange时,开始巡逻。 - 在
Knight中,在 Awake 里创建状态机和状态,在 Start 里进入巡逻状态,在 FixedUpdate 里更新状态。
按“开始”,然后走近并跑动。接着按“检查”。
亲手试试
using UnityEngine;
// PatrolState 和 ChaseState
Unity 游戏架构中的所有课
自己练习: Unity 在线编辑器