您的位置:首页 > 移动开发 > Unity3D

unity3d中使用状态机

2016-07-28 14:35 429 查看
使用状态机的目的就是对角色复杂的行为逻辑代码进行解耦。在同一个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<Translate_ShiBing, StateID_ShiBing> map = new Dictionary<Translate_ShiBing, StateID_ShiBing>();    //在某一状态下,事件引起了触发进入另一个状态
// 于是我们定义了一个字典,存储的便是触发的类型,以及对应要进入的状态
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);
return map;

}
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<FMS_State_ShiBing> 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<FMS_State_ShiBing>();   //初始化
}

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()
{

}

}


ActTarget_State_ShiBing.cs攻击状态,持续对敌人造成伤害。如果敌人死亡,进入查找敌人状态

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
博客不让上传附件太蛋疼了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity3d 状态机