前言
这段时间写游戏遇到实现敌人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 currentState = new List();
//存储所有状态
protected Dictionary states = new Dictionary();
//初始化状态机,传入所需状态并切换到初始状态
protected void InitFSM(Dictionary states, List startState)
{
this.states = states;
TransitionState(startState);
}
//执行所有状态的OnUpdate方法
void Update()
{
if(currentState.Count != 0)
{
for (int i = 0; i _.OnExit());
//切换成传入的新状态
currentState.Clear();
List stateList = new List();
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();
attackState = GetComponent();
lookAtState = GetComponent();
deathState = GetComponent();
//初始化FSM
InitFSM(new Dictionary() {
{ FSMStateType.Seek, seekState },
{ FSMStateType.Attack, attackState },
{ FSMStateType.LookAt,lookAtState},
{ FSMStateType.Death,deathState}
}, new List() { 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 nextState; //完成后是否切换到下一个状态
bool isComplete;
void Start()
{
agent = GetComponent();
}
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) temp.Add((FSMStateType)Enum.Parse(typeof(FSMStateType), _)));
//由于里氏替换原则我们不需要获取Enemy1AI直接获取FSM即可
GetComponent().TransitionState(temp);
}
}
}
}