前言
这段时间写游戏遇到实现敌人AI的需求,打算使用FSM。当然无论是FSM、行为树、亦或者简单粗暴的ifelse都能制作AI,但是考虑到FSM比ifelse、switch架构更清晰方便管理,比行为树更好把控(毕竟自己纯手撸代码),最后决定使用FSM。
代码多处使用到里氏替换原则,不了解的同学可以先了解一下更便于理解。
核心代码
FSM管理类
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
//AI状态
public enum FSMStateType
{
Idle, Patrol, Seek, Attack,LookAt, Hit, Death
}
public class FSM : MonoBehaviour
{
//这里使用list主要考虑到有可能需要状态并行
private List<IState> currentState = new List<IState>();
//存储所有状态
protected Dictionary<FSMStateType, IState> states = new Dictionary<FSMStateType, IState>();
//初始化状态机,传入所需状态并切换到初始状态
protected void InitFSM(Dictionary<FSMStateType, IState> states, List<FSMStateType> startState)
{
this.states = states;
TransitionState(startState);
}
//执行所有状态的OnUpdate方法
void Update()
{
if(currentState.Count != 0)
{
for (int i = 0; i < currentState.Count; i++)
{
currentState[i].OnUpdate();
}
}
}
//状态切换
public void TransitionState(List<FSMStateType> type)
{
//执行当前状态的OnExit
if (currentState != null)
currentState.ForEach(_ => _.OnExit());
//切换成传入的新状态
currentState.Clear();
List<IState> stateList = new List<IState>();
type.ForEach(_ => stateList.Add(states[_]));
currentState = stateList;
//执行新状态的OnEnter
currentState.ForEach(_ => _.OnEnter());
}
}
IState类,所实现的具体状态类需集成该接口
public interface IState
{
void OnEnter();
void OnUpdate();
void OnExit();
}
实际应用
具体的AI类,需继承FSM类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy1AI : FSM
{
//实现的状态类
private SeekState seekState;
private EnemyAttackState attackState;
private LookAtTargetState lookAtState;
private EnemyDeathState deathState;
private void Start()
{
seekState = GetComponent<SeekState>();
attackState = GetComponent<EnemyAttackState>();
lookAtState = GetComponent<LookAtTargetState>();
deathState = GetComponent<EnemyDeathState>();
//初始化FSM
InitFSM(new Dictionary<FSMStateType, IState>() {
{ FSMStateType.Seek, seekState },
{ FSMStateType.Attack, attackState },
{ FSMStateType.LookAt,lookAtState},
{ FSMStateType.Death,deathState}
}, new List<FSMStateType>() { FSMStateType.Seek });
}
}
具体实现状态,这里是实现了一个寻路状态。注意继承IState接口。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class SeekState : MonoBehaviour,IState
{
private NavMeshAgent agent;
public string tagStr; //根据标签寻找
public Transform target; //目标
public Vector3 targetPos; //目标点
public float arriveDis; //距离多少为到达
public float speed; //移速
public float acceleration; //加速度
public float angularSpeed; //角速度
public List<string> nextState; //完成后是否切换到下一个状态
bool isComplete;
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
public void OnEnter()
{
isComplete = false;
if (tagStr != string.Empty)
{
target = GameObject.FindWithTag(tagStr).transform;
}
}
public void OnUpdate()
{
//Debug.Log(Vector3.Distance(target.position, transform.position));
if (isComplete)
return;
if (target != null)
{
DoSeek(target.position);
}
else
{
DoSeek(targetPos);
}
}
public void OnExit()
{
}
void DoSeek(Vector3 pos)
{
agent.stoppingDistance = arriveDis;
agent.angularSpeed = angularSpeed;
agent.speed = speed;
agent.acceleration = acceleration;
agent.SetDestination(pos);
if (Vector3.Distance(pos, transform.position) <= arriveDis)
{
//Debug.Log(Vector3.Distance(pos, transform.position));
//Debug.Log("完成");
isComplete = true;
if (nextState.Count != 0)
{
List<FSMStateType> temp = new List<FSMStateType>();
nextState.ForEach(_ => temp.Add((FSMStateType)Enum.Parse(typeof(FSMStateType), _)));
//由于里氏替换原则我们不需要获取Enemy1AI直接获取FSM即可
GetComponent<FSM>().TransitionState(temp);
}
}
}
}