框架核心思路
StateBase类中的字典以键值对为[状态转换枚举类型][状态基类]存数据;
StateBase类中**getState(ETransition trans)**方法通过遍历字典中的枚举名字获取对应的状态类 ;
StateManager类中的listState容器存储状态类
这里实现了状态离开前的函数和下个状态开始的函数
public void doTransition(ETransition trans)
{
StateBase nextState= curState.getState(trans);
if(nextState!=null)
{
//执行状态离开之前的函数(数据还原,一次性操作的逻辑)
curState.doBeforeExit();
//切换状态
curState = nextState;
//下个状态开始之前执行的函数(数据还原,一次性操作的逻辑)
curState.doBeforeEnter();
}
}
状态的基类
声明所有的状态 和 状态转换
声明一个字典 key为枚举类型的状态转换 , Value为状态基类
子类
**addTransition(ETransition transition, StateBase state)**往字典里面存数据
两个抽象方法:action() 、 condition()
子类状态重写抽象方法实现各自的当前状态下的行为 、 进入该状态的条件
子类在重写condition()实现状态转换时 ,创建一个Monster对象 ,里面的doTranstion()方法 ,Monster类中的doTranstion()方法则是调用StateManager的doTranstion() , 这是最底层的逻辑。
两个虚方法:doBeforeExit() doBeforeEnter()
子类可以选择性实现进入状态前做的事情 、 离开当前状态前做的事情
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
/// <summary>
/// AI状态的基类
/// 1、状态名字
/// 2、状态转换
/// </summary>
///
public enum ETransition
{
None,
Idle2Follow,
Idle2Attack,
Idle2Flee,
Idle2Patrol,
Follow2Attack,
Follow2Idle,
Attack2Follow,
Attack2Idle,
Attack2Flee,
Patrol2Attack,
Patrol2Follow,
Flee2Idle,
Flee2Dead,
Any2Dead
}
public enum EAIState
{
Idle,//闲置
Follow,
Attack,
Dead,
Patrol,//巡逻
Flee,//逃跑
}
public abstract class StateBase
{
//游戏对象上的脚本组件
protected MonsterController controller;
//AI所在的游戏对象的Transform
protected Transform transSelf;
//目标所在的游戏对象的Transform
protected Transform transTarget;
//动画名字,使用老版本的动画
protected string animationName;
//AI状态
protected EAIState stateName;
public string Name
{
get
{
return Enum.GetName(typeof(EAIState), stateName);
}
}
//转换状态
Dictionary<ETransition, StateBase> dic = new Dictionary<ETransition, StateBase>();
//目标和自己之间距离
protected float distance;
public StateBase(MonsterController controller)
{
this.controller = controller;
transSelf = controller.transform;
transTarget = GameObject.FindWithTag("Player").transform;
}
/// <summary>
/// 添加数据
/// </summary>
/// <param name="transition">转换线</param>
/// <param name="state">转换过去的状态</param>
public void addTransition(ETransition transition, StateBase state)
{
if(!dic.ContainsKey(transition))
{
dic[transition] = state;
}
}
public void delTransition(ETransition transition)
{
if (dic.ContainsKey(transition))
{
dic.Remove(transition);
}
}
/// <summary>
/// 查找状态
/// </summary>
/// <param name="trans">转换线</param>
/// <returns></returns>
public StateBase getState(ETransition trans)
{
if(dic.ContainsKey(trans))
{
return dic[trans];
}
return null;
}
/// <summary>
/// 当前状态的行为
/// </summary>
public abstract void action();
/// <summary>
/// 条件转换的理由
/// </summary>
public virtual void reason()
{
distance = Vector3.Distance(transSelf.position, transTarget.position);
}
/// <summary>
/// 离开当前状态之前做的事情
/// </summary>
public virtual void doBeforeExit() { }
/// <summary>
/// 进入当前状态做的事情
/// </summary>
public virtual void doBeforeEnter() { }
}
状态管理器StateManager
addState(StateBase state)
这个方法实现往容器listState中添加状态
声明curState用来引用当前容器里面的某一状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 状态管理器
/// 某一时刻只有一个状态在执行
/// 增,删,改,查
/// </summary>
public class StateManager
{
List<StateBase> listState = new List<StateBase>();
//当前状态的引用,引用listState容器中的某一个状态
public StateBase curState;
/// <summary>
/// 往容器中添加状态
/// </summary>
/// <param name="state"></param>
public void addState(StateBase state)
{
if (state == null) return;
if(!listState.Contains(state))
{
listState.Add(state);
}
//把第一个添加进来的状态当成默认状态
if (curState==null)
{
curState = state;
}
}
public void setDefaultState(StateBase state)
{
if(state!=null)
{
curState = state;
curState.doBeforeEnter();
}
}
public void deleState(StateBase state)
{
if (state == null) return;
if (!listState.Contains(state))
{
listState.Remove(state);
}
}
/// <summary>
/// 切换状态
/// </summary>
/// <param name="trans">切换线</param>
public void doTransition(ETransition trans)
{
StateBase nextState= curState.getState(trans);
if(nextState!=null)
{
//执行状态离开之前的函数(数据还原,一次性操作的逻辑)
curState.doBeforeExit();
//切换状态
curState = nextState;
//下个状态开始之前执行的函数(数据还原,一次性操作的逻辑)
curState.doBeforeEnter();
}
}
public void OnUpdate()
{
if(curState!=null)
{
Debug.Log(curState.Name);
curState.action();
curState.condition();
}
}
}
怪物控制类:
这是一个Mono类 , 把这个类挂载到游戏物体上运行时脚本才开始跑, 所以在start()函数中运行makeFSM() , 初始化状态管理器类中的字典的数据 , 想要添加状态时只要往里面添加对应状态的addTranstion()方法就可以。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class MonsterController : MonoBehaviour
{
//随机点的距离
public float randomDistance = 5.0f;
public float fleeDistance = 5.0f;//逃跑距离
public float speed=2.0f;
public float speedAngle=360.0f;
public float attackDistance = 2.0f;
public float followDistance = 6.0f;
public float hp = 100;
//CharacterController cc;
NavMeshAgent agent;
Animation anim;
StateManager stateMng;
public string[] animAttackNames =
{
"Attack",
"Attack Paw"
};
public Vector3 RandomPos
{
get
{
Vector3 dir= Random.onUnitSphere;
dir.y = 0;
dir.Normalize();
Vector3 targetPos = transform.position + dir * randomDistance;
GameObject go= GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.transform.position = targetPos;
go.transform.localScale = Vector3.one * 0.1f;
agent.SetDestination(targetPos);
return targetPos;
//NavMeshHit hit;
//if(agent.SamplePathPosition(1 << LayerMask.NameToLayer("Map"), 20, out hit))
//{
// return hit.position;
//}
//return Vector3.zero;
}
}
public void doTransition(ETransition trans)
{
stateMng.doTransition(trans);
}
public void playAnimation(string name)
{
//可以执行到动画最后
anim.Play(name);
//动画之间的过渡(不会执行到上一个动作的最后)
//anim.CrossFade(name);
}
public Vector3 getRandomPos()
{
return Vector3.zero;
}
public void stopMove()
{
agent.isStopped = true;
}
public void move(Vector3 pos)
{
agent.isStopped = false;
agent.SetDestination(pos);
}
public void playAttack()
{
int index = Random.Range(0, animAttackNames.Length);
print("playAttack:"+ animAttackNames[index]);
anim.Stop();
anim.Play(animAttackNames[index]);
}
public void rotateToTarget(Transform target)
{
}
// Start is called before the first frame update
void Start()
{
agent = GetComponent<NavMeshAgent>();
anim = GetComponent<Animation>();
agent.speed = speed;
agent.angularSpeed = speedAngle;
makeFSM();
}
void makeFSM()
{
//生成AI状态管理器
stateMng = new StateManager();
//生成所有的AI状态
Idle idle = new Idle(this);
Attack attack = new Attack(this);
Follow follow = new Follow(this);
Patrol patrol = new Patrol(this);
Flee flee = new Flee(this);
Dead dead = new Dead(this);
stateMng.addState(idle);
stateMng.addState(attack);
stateMng.addState(follow);
stateMng.addState(patrol);
stateMng.addState(flee);
stateMng.addState(dead);
stateMng.setDefaultState(patrol);
//每一个状态的转换条件
idle.addTransition(ETransition.Idle2Attack, attack);
idle.addTransition(ETransition.Idle2Follow, follow);
idle.addTransition(ETransition.Idle2Flee, flee);
idle.addTransition(ETransition.Any2Dead, dead);
idle.addTransition(ETransition.Idle2Patrol, patrol);
follow.addTransition(ETransition.Follow2Attack, attack);
follow.addTransition(ETransition.Follow2Idle, idle);
follow.addTransition(ETransition.Any2Dead, dead);
attack.addTransition(ETransition.Attack2Follow, follow);
attack.addTransition(ETransition.Attack2Idle, idle);
attack.addTransition(ETransition.Attack2Flee, flee);
attack.addTransition(ETransition.Any2Dead, dead);
flee.addTransition(ETransition.Flee2Idle, idle);
flee.addTransition(ETransition.Flee2Dead, dead);
flee.addTransition(ETransition.Any2Dead, dead);
patrol.addTransition(ETransition.Patrol2Attack, attack);
patrol.addTransition(ETransition.Patrol2Follow, follow);
}
// Update is called once per frame
void Update()
{
if(stateMng!=null)
{
stateMng.OnUpdate();
}
}
}
以上框架差不多了 只剩下在各个状态中重写抽象方法和虚方法
Attack类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attack : StateBase
{
float attackPerTime;//攻击频率
float curTime;
float timer = 0;
public Attack(MonsterController controller):base(controller)
{
animationName = "Attack";
stateName = EAIState.Attack;
}
public override void doBeforeEnter()
{
controller.stopMove();
controller.playAttack();
}
public override void action()
{
//有条件的播放攻击动画
//curTime -= Time.deltaTime;
//if(curTime<=0)
//{
// controller.playAnimation(animationName);
// curTime = attackPerTime;
//}
transSelf.LookAt(transTarget);
}
public override void condition()
{
base.condition();
if (controller.hp <= 0)
{
controller.doTransition(ETransition.Any2Dead);
}
else if (controller.hp<=30)
{
//有一定的几率逃跑
controller.doTransition(ETransition.Attack2Flee);
return;
}
if (distance > 2.0f&&distance<6.0)//追寻范围
{
//切换攻击状态
controller.doTransition(ETransition.Attack2Follow);
}
else if (distance > 6.0f)//idle状态
{
controller.doTransition(ETransition.Attack2Idle);
}
}
}