在Unity游戏开发中会涉及到怪物AI逻辑的实现,下面将介绍软件设计模式中的状态模式以及其在怪物AI实现中的运用。
状态模式
在面向对象系统设计中,经常会遇到依赖于状态的对象。对象的行为会根据状态的改变而改变。
例如:一个Person(人)对象,在开心的状态下,就会进入吃东西的行为,而在伤心的状态下,就会进入跑步缓解伤心的行为。
如果一个对象(类)是依赖于状态的,那么程序员在描述该对象的类中,通常会使用许多条件语句来覆盖所有的条件以及在这些条件下的对象的行为。
例如:
if (person is 开心)
? 吃东西();
else if (person is 伤心)
? 跑步();
但是这种方法对于比较复杂的状态判断容易产生错误。基于大量的条件语句的处理方法有很多不便之处。首先,增加一个新的状态将会导致对类的大量修改,因此该设计不容易扩展与维护;另外,状态的转换不明显,也可能发生编程错误。
一个有效的方法就是利用状态模式。
状态模式的结构类图如下:
(1)Context:定义了与客户程序的接口,它保持了一个ConcreteState的代表现在状态的实例。
(2)State:定义了状态的接口,它的各个子类封装了在各种不同状态的行为。
(3)ConcreteState子类:封装了在各种不同状态下的行为。
状态的改变可以由Context类或状态类负责。下面通过实现一个简单的基于状态模式的怪物AI逻辑。
状态模式在怪物ai实现的例子
在该例子中,怪物只具有两个状态,站立状态和巡逻状态,状态图如下:
其类图如下:
(1)FSM:相当于Context类,Finite State Machine的缩写。属性parameter封装了怪物移动速度、血量、攻击力等数值;currentState表示当前怪物所处的状态;states维护了注册到FSM的状态,包括IdleState、ParolState;transitionState()方法的作用为切换状态。
(2)IState:相当于State接口,OnEnter()方法在进入状态时的执行;OnUpdate()方法在该状态下时执行;OnExit()方法在退出该状态时执行。
(3)IdleState和PatrolState:相当于ConcreteState类,类中的manager表示状态机类FSM。
具体实现如下:
(1)在FSM类中注册站立状态和巡逻状态,即:将两个状态类的对象添加到states中,并将当前状态currentState设置为IdleState。
(2)当要切换状态时,状态类会在OnUpdate()方法中利用其属性manager调用FSM类的transitionState()方法,切换状态;transitionState()方法会利用currentState属性依次调用前一状态的OnExit()方法,和新状态的OnEnter()方法进行初始化。
代码实现如下:
/* IState接口 */
public interface IState
{
void OnEnter();
void OnUpdate();
void OnExit();
}
/* 站立状态 */
public class IdleState : IState
{
private FSM manager;
private Parameter parameter;
private float timer;
public IdleState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameter;
}
public void OnEnter()
{
}
public void OnExit()
{
timer = 0;
}
public void OnUpdate()
{
timer += Time.deltaTime;
if (timer >= parameter.idleTime)
{
manager.transitionState(StateType.Patrol);
}
}
}
/* 巡逻状态 */
public class PatrolState : IState
{
private FSM manager;
private Parameter parameter;
private int patrolPosition;
public PatrolState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameter;
}
public void OnEnter()
{
}
public void OnExit()
{
patrolPosition++;
if (patrolPosition >= parameter.patrolPoints.Length)
{
patrolPosition = 0;
}
}
public void OnUpdate()
{
manager.transform.position = Vector2.MoveTowards(manager.transform.position,
parameter.patrolPoints[patrolPosition].position, parameter.moveSpeed * Time.deltaTime);
if (Vector2.Distance(manager.transform.position, parameter.patrolPoints[patrolPosition].position) < 0.1f)
{
manager.transitionState(BeeStateType.Idle);
}
}
}
/* 状态枚举 */
public enum StateType
{
Idle,Patrol
}
/* 怪物包含的参数 */
public class Parameter
{
public float moveSpeed;
public float idleTime;
public Transform[] patrolPoints;
public Animator animator;
}
/* 怪物的有限状态机FSM */
public class FSM : MonoBehaviour
{
public Parameter parameter;
private IState currentState;
private Dictionary<StateType, IState> states = new Dictionary<StateType, IState>();
void Start()
{
states.Add(StateType.Idle, new IdleState(this));
states.Add(StateType.Patrol, new PatrolState(this));
transitionState(StateType.Idle);
}
void Update()
{
currentState.OnUpdate();
}
public void transitionState(StateType type)
{
if (currentState != null)
{
currentState.OnExit();
}
currentState = states[type];
currentState.OnEnter();
}
}