在各类RPG游戏中,NPC是整个游戏结构中不可缺少的一环,也可以说是整个游戏的重要组成部分。
在设计NPC结构时,我们往往会给他们添加许多不同的状态,实现他们在游戏中的不同功能。倘若状态数过多,在一个NPC类使用switch,if-else语句来进行状态管理,在理论上违反了开放封闭原则,同时也不便于我们对程序的进一步维护管理。因此,我们引入了状态机这一设计模式。下文以NPC待机,巡逻状态为例。
整体状态机的设计分为三个部分:接口:IState,状态机管理类:FSM,状态类:以IdleState,PatrolState等等为例。
第一部分:IState的设计代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//接口IState
public interface IState
{
void OnEnter();//进入状态时执行
void OnExit();//退出状态时执行
void OnUpdate();//状态响应时执行
}
第二部分:FSM管理类的设计代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[Serializable]//注意这里的序列化!
//参数列表,根据你所需要的人物设计进行添加
public class Parameter
{
public float moveSpeed;
public float idleTime;
public Transform[] patrolPoints;
}
public class FSM : MonoBehaviour
{
public enum StateType
{
Idle,Patrol,InDialog//待机状态,巡逻状态,对话状态等等
}
public Parameter parameters;//传参数
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));
states.Add(StateType.InDialog, new DialogState(this));
TransitionState(StateType.Idle);//状态机需要有一个初始状态,这里给到一个待机(IdleState)
}
// Update is called once per frame
void Update()
{
currentState.OnUpdate();//持续执行当前状态响应
}
//切换状态的方法//
public void TransitionState(StateType type)
{
if(currentState!=null)
{
currentState.OnExit();
}
currentState = states[type];//通过键值type找到相应的状态值
currentState.OnEnter();
}
}
第三部分:状态类的设计:
以下以待机类,巡逻状态类举例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdleState : IState
{
private FSM manager;//状态机管理类引入
private Parameter parameter;//参数获取途径
private float timer=0;//待机状态持续多长时间,通过一个Timer变量计时
public IdleState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameters;
}//构造函数,是用于状态的存储
public void OnEnter()
{
}
public void OnExit()
{
timer = 0;
}
public void OnUpdate()
{
timer += Time.deltaTime;
if(timer>=parameter.idleTime)
{
manager.TransitionState(FSM.StateType.Patrol);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PatrolState : IState
{
private FSM manager;
private Parameter parameter;
private int patrolPostion;
public PatrolState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameters;
}
public void OnEnter()
{
Debug.Log("进入巡逻状态");
}
public void OnExit()
{
patrolPostion++;
if(patrolPostion>=parameter.patrolPoints.Length)
patrolPostion=0;
}
public void OnUpdate()
{
manager.transform.position = Vector2.MoveTowards(manager.transform.position,
parameter.patrolPoints[patrolPostion].position, parameter.moveSpeed * Time.deltaTime);
if (Vector2.Distance(manager.transform.position, parameter.patrolPoints[patrolPostion].position) <= 0f)
{
manager.TransitionState(FSM.StateType.Idle);//如果小于0f的距离,那就说明在待机点的附近,此时角色切换到待机状态
}
}
}
运行实例:
把FSM挂在到对应的NPC物体上,通过Inspector窗口对相关变量进行赋值:
运行效果: