使用状态机的目的就是对角色复杂的行为逻辑代码进行解耦。在同一个act根据状态id不同,调用不同的类执行代码。
以一个士兵,有查找敌人、移动、攻击、胜利、自身死亡,这五种状态为例。
FMS_State_ShiBing.cs是状态机的父类。主要完成定义状态枚举和构建状态字段
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum Translate_ShiBing //如果进入一个新的状态,需要一些触发,比如NPC看到了Player,由巡逻状态进入跟踪状态
{
Unknow,
ShiBing_FindObject,
ShiBing_MoveToObjct,
ShiBing_ActObject,
ShiBing_Victory,
ShiBing_Death
}
public enum StateID_ShiBing //每个状态都应该有一个ID,作为识别改状态的标志
{
Unknow,
ShiBing_FindObject,
ShiBing_MoveToObjct,
ShiBing_ActObject,
ShiBing_Victory,
ShiBing_Death
}
public abstract class FMS_State_ShiBing
{
private StateID_ShiBing id; //定一个状态ID作为变量来标识
public StateID_ShiBing ID
{
set { id = value; }
get { return id; }
}
private Dictionary map = new Dictionary(); //在某一状态下,事件引起了触发进入另一个状态
// 于是我们定义了一个字典,存储的便是触发的类型,以及对应要进入的状态
public void addDictionary(Translate_ShiBing tr, StateID_ShiBing id1) //向字典里添加
{
if (tr == Translate_ShiBing.Unknow)
{
Debug.LogError("Null Trans is not allower to adding into");
return;
}
if (ID == StateID_ShiBing.Unknow)
{
Debug.LogError("Null State id not ~~~");
return;
}
if (map.ContainsKey(tr)) //NPC 任何时候都只能出于一种状态,所以一旦定义了一个触发的枚举类型,对应的只能是接下来的一种状态
{
Debug.LogError(id1.ToString() + "is already added to");
return;
}
map.Add(tr, id1);
}
public void deleateDictionary(Translate_ShiBing tr) //删除字典里存储的一个状态
{
if (tr == Translate_ShiBing.Unknow)
{
Debug.LogError("TransNull is not allowed to delate");
return;
}
if (map.ContainsKey(tr))
{
map.Remove(tr);
return;
}
Debug.LogError(tr.ToString() + "are not exist");
}
public StateID_ShiBing GetOutState(Translate_ShiBing tr) //由状态的触发枚举类型返回一个对应的状态类型
{
if (map.ContainsKey(tr))
{
// Debug.Log("Translate " + tr + "State" + map[tr]);
return map[tr];
}
return StateID_ShiBing.Unknow;
}
public virtual void DoBeforeEnter() { } //虚方法
public virtual void DoBeforeMoveing() { }
// public abstract void Reason(); // 抽象方法
public abstract void Act();
}
FMS_Machine_Manage_ShiBing.cs 这个类主要是实例化状态机,默认为第一个添加的状态。并且声明了当前状态允许跳转的状态清单
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FMS_Machine_Manage_ShiBing : MonoBehaviour {
private List states;//存储所有状态的的List
private FMS_State_ShiBing currentState; //当前状态
private StateID_ShiBing currentStateID;//当前状态ID
public FMS_State_ShiBing CurrentState
{
set { currentState = value; }
get { return currentState; }
}
public StateID_ShiBing CurrentStateID
{
set { currentStateID = value; }
get { return currentStateID; }
}
public GameObject player;
public GameObject[] path;
public GameObject NPC;
public FMS_Machine_Manage_ShiBing()
{
states = new List(); //初始化
}
public void UpdateFunction()
{
// CurrentState.Reason();
CurrentState.Act();
}
public void Revert()
{
}
void Awake()
{
}
public void MakeFMSMachine(GameObject MySelf)
{
FindObject_State_ShiBing FindObject = new FindObject_State_ShiBing(MySelf,this);
FindObject.addDictionary(Translate_ShiBing.ShiBing_MoveToObjct, StateID_ShiBing.ShiBing_MoveToObjct);//放的是下个状态的,并且一次只能跳转到一个状态,但是好像也可以是多个
FindObject.addDictionary(Translate_ShiBing.ShiBing_Victory, StateID_ShiBing.ShiBing_Victory);
AddFmsState(FindObject);
MoveToObject_State_ShiBing MoveToObject = new MoveToObject_State_ShiBing(MySelf,this);
MoveToObject.addDictionary(Translate_ShiBing.ShiBing_ActObject, StateID_ShiBing.ShiBing_ActObject);
MoveToObject.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
AddFmsState(MoveToObject);
ActTarget_State_ShiBing ActTarget = new ActTarget_State_ShiBing(MySelf,this);
ActTarget.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
AddFmsState(ActTarget);
Victory_State_ShiBing VictoryState = new Victory_State_ShiBing(MySelf, this);
VictoryState.addDictionary(Translate_ShiBing.ShiBing_FindObject, StateID_ShiBing.ShiBing_FindObject);
AddFmsState(VictoryState);
}
public void AddFmsState(FMS_State_ShiBing s)//注册所有状态
{
if (s == null)
{
Debug.LogError(" Null reference is not allowed");
}
if (states.Count == 0)
{
states.Add(s); //设置默认状态(important);
currentState = s;
currentStateID = s.ID;
return;
}
foreach (FMS_State_ShiBing state in states)
{
if (state == s)
{
Debug.LogError(s.ID.ToString() + "has already been added");
return;
}
}
states.Add(s);
}
public void delateFmsState(StateID_ShiBing id)
{
if (id == StateID_ShiBing.Unknow)
{
Debug.LogError("NullStateID is not allowed for a real state");
return;
}
foreach (FMS_State_ShiBing state in states)
{
if (state.ID == id)
{
states.Remove(state);
return;
}
}
}
public void changeState(Translate_ShiBing tr) //更改状态
{
if (tr == Translate_ShiBing.Unknow)
{
Debug.LogError("NullTransition is not allowed for a real transition");
return;
}
//if (currentState.GetOutState(tr) == StateID.NullState)
//{
// Debug.Log("translate" + " " + tr + " " + currentState.GetOutState(tr)+" "+CurrentStateID);
// Debug.LogError("1234");
// return;
//}
StateID_ShiBing id = CurrentState.GetOutState(tr); //当前状态会进入的新的状态
currentStateID = id;
// Debug.Log("Prives" + Prives.ID);
foreach (FMS_State_ShiBing state in states) //通过注册的所有状态,进行搜索来获取要进入的状态实例
{
if (currentStateID == state.ID)
{
CurrentState.DoBeforeMoveing(); //退出状态前,留下点什么,比如挥舞下手臂
currentState = state;
CurrentState.DoBeforeEnter(); //进入状态
break;
}
}
}
}
NPC_Control_ShiBing.cs这个类是用来挂在实际pre士兵身上的,其中UpDate实现了动作的心跳执行。
using UnityEngine;
using System.Collections;
public class NPC_Control_ShiBing : MonoBehaviour {
//要攻击的目标
public GameObject TargetObject;
//自己
public GameObject MySelfObject;
public float Speed;//速度
public float TurnSpeed;//旋转速度
public float ActRange;//火力范围
public int damage;//攻击力
public int HP;
private bool IsDestroy = false;
private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;
// Use this for initialization
void Start () {
Speed = 50f;
HP = 100;
TurnSpeed = 0.3f;
ActRange = 3.0f;
damage = 25;
_FMS_Machine_Manage_ShiBing = new FMS_Machine_Manage_ShiBing();
_FMS_Machine_Manage_ShiBing.MakeFMSMachine(MySelfObject);
}
// Update is called once per frame
void Update () {
_FMS_Machine_Manage_ShiBing.UpdateFunction();
}
public bool GetDestroy()
{
return IsDestroy;
}
public void DestroySetTrue()
{
IsDestroy = true;
Destroy(gameObject, 1.01f);//1.01秒后销毁炸弹
}
}
FindObject_State_ShiBing.cs是具体实现功能的类,查找目标。找到后移动,找不到说明没有敌人了,进入胜利状态。找到目标后进入攻击状态
using UnityEngine;
using System.Collections;
public class FindObject_State_ShiBing : FMS_State_ShiBing//查找目标
{
public GameObject[] aEnemy;//所有的敌人
private GameObject _MySelf;//自己
private NPC_Control_ShiBing _NPC_Control_ShiBing;//自己的其他属性
private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;
public FindObject_State_ShiBing(GameObject MySelf, FMS_Machine_Manage_ShiBing __FMS_Machine_Manage_ShiBing)
{
_FMS_Machine_Manage_ShiBing = __FMS_Machine_Manage_ShiBing;
ID = StateID_ShiBing.ShiBing_FindObject;
_MySelf = MySelf;//自己
_NPC_Control_ShiBing = _MySelf.GetComponent("NPC_Control_ShiBing") as NPC_Control_ShiBing;//自己的属性
}
public override void Act() //在该状态下改做点什么呢
{
//所有的敌人
aEnemy = GameObject.FindGameObjectsWithTag("Enemy");
//没有敌人就是胜利了
//if (aJianZhu.Length == 0)
//{
// _FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_Victory);
// return;
//}
//最近的敌人
GameObject NearlyObject = null;
float TempDistance = 999999.0f;
bool AnyJianZhu = false;
foreach (GameObject enemy_GameObject in aEnemy)
{
NPC_Control_Enemy _NPC_Control_Enemy = enemy_GameObject.GetComponent("NPC_Control_Enemy") as NPC_Control_Enemy;
//如果被销毁,不计算当前敌人
if (_NPC_Control_Enemy.GetDestroy())
{
continue;
}
//还有敌人
AnyJianZhu = true;
//计算最小距离,这里最好的做法是在对象初始化的时候得到一个减去值,然后每次得到Y的初始位置
// JianZhu_GameObject.transform.position
//_MySelf.transform.position
float _distance = Vector3.Distance(enemy_GameObject.transform.position, _MySelf.transform.position);
if (_distance < TempDistance)
{
TempDistance = _distance;
NearlyObject = enemy_GameObject;
}
}
if (AnyJianZhu)
{
_NPC_Control_ShiBing.TargetObject = NearlyObject;
//找到目标后转向
_FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_MoveToObjct);
}
else
{
_FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_Victory);
}
}
public override void DoBeforeMoveing()
{
}
public override void DoBeforeEnter()
{
}
}
using UnityEngine;
using System.Collections;
public class ActTarget_State_ShiBing : FMS_State_ShiBing
{
private GameObject _MySelf;//自己
private NPC_Control_ShiBing _NPC_Control_ShiBing;//自己的其他属性
private FMS_Machine_Manage_ShiBing _FMS_Machine_Manage_ShiBing;
private GameObject _target;//目标敌人
private NPC_Control_Enemy _NPC_Control_Enemy;//目标敌人的脚本
private float m_fireTimer = 0.0f;
public ActTarget_State_ShiBing(GameObject MySelf, FMS_Machine_Manage_ShiBing __FMS_Machine_Manage_ShiBing)
{
ID = StateID_ShiBing.ShiBing_ActObject;
_FMS_Machine_Manage_ShiBing = __FMS_Machine_Manage_ShiBing;
_MySelf = MySelf;//自己
_NPC_Control_ShiBing = _MySelf.GetComponent("NPC_Control_ShiBing") as NPC_Control_ShiBing;//自己的属性
}
public override void Act() //在该状态下改做点什么呢
{
// if (_target == null)
// {
_target = _NPC_Control_ShiBing.TargetObject;
// 获取一个对象上的脚本
_NPC_Control_Enemy = _target.GetComponent("NPC_Control_Enemy") as NPC_Control_Enemy;
//TargetHP = JianZhuObject.HP;
// }
if (_NPC_Control_Enemy.GetDestroy())
{
_FMS_Machine_Manage_ShiBing.changeState(Translate_ShiBing.ShiBing_FindObject);
}
else
{
//每两秒开一次火
m_fireTimer -= Time.deltaTime;
if (m_fireTimer <= 0)
{
// JianZhuObject.Jian_BeiGongJiFX();//如果士兵死了要调用一下
m_fireTimer = 2;
_NPC_Control_Enemy.HP = _NPC_Control_Enemy.HP - _NPC_Control_ShiBing.damage;
//如果敌人没血了,设置一个建筑的标志为摧毁,如果当前目标摧毁了,进入寻找状态
if (_NPC_Control_Enemy.HP <= 0)
{
_NPC_Control_Enemy.DestroySetTrue();
}
}
}
}
public override void DoBeforeMoveing()
{
}
public override void DoBeforeEnter()
{
}
}
http://pan.baidu.com/s/1i5wdVyX
博客不让上传附件太蛋疼了