基于unity3d(C#)的有限状态机设计

1.什么是有限状态机

有限状态机是把一个对象的行为分解称为易于处理的“块”或状态。例如,灯的开关,就是一个简单的有限状态机。它有两种状态:开和关。

假想一个机器小猫。它在肚子有一个插槽,放有很多模块(用来控制小猫的状态)。这些模块里包含这小猫的不同行为。例如:玩毛线、吃鱼,或者睡觉。如果没有一个模块,小猫就会是一个死气沉沉的金属雕塑品,只会静静坐着。猫在玩毛线的状态时,会监控小猫饥饿的等级,当它感到饥饿等级上升时,会自己把模块转换到吃鱼。吃鱼模块运行,直到小猫吃饱后,再跳回玩毛线状态。这只小猫就是我们的程序,里面的模块就是程序里的各种状态。

2.为什么要用有限状态机

通常,在一个程序里面,转换各种状态,需要使用一系列的if-then语句或者switch语句。比如:While(游戏未结束)

{

  Switch(游戏状态)

  Case 资源加载:

  Case 进入关卡:

  Load_gate();//加载背景、飞机、炮弹的图片。

  Case 游戏菜单:

  If (游戏结束) 计算游戏结果

  ase 游戏进行:

  New_paodan();//产生新炮弹

  Move();//计算出该时刻飞机以及所有炮弹所在的位置

  Is_pengzhuang();//碰撞判断

  ase 游戏暂停:

  Thread_pause();//游戏暂停操作。

  • Draw()函数框架的伪代码如下:

  Draw()

  {

  Switch(游戏状态)

  Case 游戏进行:

  Draw_background();//绘制背景

  Draw_paodan();//画炮弹

  Draw_feiji();//画飞机

  Case 其他:

  略....

  } 

这种方法容易理解,让人也觉得合理,但是当应用的再稍微复杂一点点,switch/if-then 这种方法就变成了一个怪物,它会潜伏起来等着突袭。随着更多状态和条件被加入,这种结构就像意大利面条一样跳来跳去,使得程序流程很难理解,你调试它也会是个噩梦。此外,它不灵活,当你第一次计划好整个程序时,你几乎肯定会发现,你需要经常的、频繁的修改switch/if-then语句(除非你是天才)。

此外,当你想让对象处于初始进入状态或者退出状态时,你会常常需要一个状态完成一个制定的行动。例如,一个(敌人)对象进入逃跑状态,你可能会希望它挥着胳膊还道:啊!,当他最后逃脱并进入巡逻状态,你可能会希望它发出一声叹息,擦去额头的汗水。这些行为都只能是在进入或退出某个状态时出现的,而不会发生在通常的更新步骤中。因此,这个附加的函数必须理想地建立在你的switch/if-then语句中。在这个架构中你想做到这些,必定会咬牙切齿、恶心反胃,并且写出相当糟糕的代码。

3.如何使用有限状态机

整个程序的世界由BaseGameEntity继承来。它用来储存每个对象的ID号码,并且定义一个Update函数,在每个更新步骤被调用(unity3d中每个对象都自带了update函数,所以基类不需要再有)。

BaseGameEntity类声明如下:

public class BaseGameEntity : MonoBehaviour
{

	private int m_ID;//每个对象具有一个唯一的识别数字
	
	private static ArrayList m_idArray = new ArrayList();
	public int ID ()
	{
		return m_ID;
	}

	protected void SetID (int val)
	{
		
		//这个函数用来确认ID是否正确设置
		if (m_idArray.Contains(val)) {
			Debug.LogError ("id cuo wu ");
			return;
		}
		
		m_idArray.Add(val);
		m_ID = val;
	}
	
	public int getID(){
		return m_ID;
	}
} 

对象类从BaseGameEntity类继承,包含该对象的各种各样特性数据成员,例如这个对象是个人物角色,类中就包含它的健康、疲劳程度、它的位置,等等。一个对象有一个指向state(状态)类的实力,还有一个方法来改变指针指向的状态。

public class People : BaseGameEntity {
	//指向一个状态实例的指针
	StateMachine m_pStateMachine;
	//角色当前的位置
	public location_type m_Location;
	//矿工角色包中装了多少金块
	public int m_iGoldCarried;
	//矿工角色在银行存了多少钱
	public int m_iMontyInBank;
	//矿工角色口渴程度
	public int m_iThirst;
	//矿工角色疲劳程度
	public int m_iFatigue;
	
	void Start () {
		// 设置ID
		SetID((int) People);
		
		//设置状态接口,并指向一个状态
		m_pStateMachine = new StateMachine(this);
		m_pStateMachine.SetCurrentState(People_GloballState .Instance());
	}
	
	void Update ()
	{	
		//调用正在使用的状态		
		m_pStateMachine.SMUpdate();
	}
	
	public StateMachine GetFSM ()
	{
		//返回状态管理机
		return m_pStateMachine;
	}
} 

下面介绍角色的状态类。

先看看状态基类:

//C# 范型
public class State
{
	
	public entity_type Target ;
	//进入状态  
	public virtual void Enter (entity_type entityType)
	{
		
	}

	//状态正常执行
	public virtual void Execute (entity_type entityType)
	{
		
	}

	//退出状态
	public virtual void Exit (entity_type entityType)
	{
		
	}
} 

再看看状态控制机的类:

using UnityEngine;
using System.Collections;

public class StateMachine
{
	//entity 实体
	private entity_type m_pOwner;

	private State m_pCurrentState;
	private State m_pPreviousState;
	private State m_pGlobalState;

	public StateMachine (entity_type owner)
	{
		m_pOwner = owner;
		m_pCurrentState = null;
		m_pPreviousState = null;
		m_pGlobalState = null;
	}
	
	public void GlobalStateEnter()
	{
		m_pGlobalState.Enter(m_pOwner);
	}
	
	public void SetGlobalStateState(State GlobalState)
	{
		m_pGlobalState = GlobalState;
		m_pGlobalState.Target = m_pOwner;
		m_pGlobalState.Enter(m_pOwner);
	}
	
	public void SetCurrentState(State CurrentState)
	{
		m_pCurrentState = CurrentState;
		m_pCurrentState.Target = m_pOwner;
		m_pCurrentState.Enter(m_pOwner);
	}
	public void SMUpdate ()
	{
		//全局状态的运行
		if (m_pGlobalState != null)
			m_pGlobalState.Execute (m_pOwner);
		
		//一般当前状态的运行
		if (m_pCurrentState != null)
			m_pCurrentState.Execute (m_pOwner);
	}

	public void ChangeState (State pNewState)
	{
		if (pNewState == null) {
			
			Debug.LogError ("该状态不存在");
		}

		//退出之前状态
		m_pCurrentState.Exit(m_pOwner);
		//保存之前状态
		m_pPreviousState = m_pCurrentState;
		
		//设置当前状态
		m_pCurrentState = pNewState;
		m_pCurrentState.Target = m_pOwner;
		//进入当前状态
		m_pCurrentState.Enter (m_pOwner);
	}

	public void RevertToPreviousState ()
	{
		//qie huan dao qian yi ge zhuang tai 
		ChangeState (m_pPreviousState);
		
	}

	public State CurrentState ()
	{
		//fan hui dang qian zhuang tai 
		return m_pCurrentState;
	}
	public State GlobalState ()
	{
		//fan hui quan ju zhuang tai 
		return m_pGlobalState;
	}
	public State PreviousState ()
	{
		//fan hui qian yi ge zhuang tai 
		return m_pPreviousState;
	}

	public bool HandleMessage (Telegram msg)
	{
		//the message
		if (m_pCurrentState!=null && m_pCurrentState.OnMessage (m_pOwner, msg)) {
			return true;
		}
		// message to the global state
		if (m_pGlobalState!=null && m_pGlobalState.OnMessage (m_pOwner, msg)) {
			return true;
		}
		
		return false;
	}
} 

角色的状态类继承自State基类:

public class People_GloballState :State<People >
{
	private static People_GloballState instance;

	//Singleton设计模式,确保了一个对象只能实例化一次,它是全局可访问的。
	public static People_GloballState Instance ()
	{
		if (instance == null)
			instance = new People_GloballState ();
		
		return instance;
	}


	public override void Enter (People Entity)
	{
		//base.Enter (Entity);
	}

	public override void Execute (People Entity)
	{
		//base.Execute (Entity);	
	}

	public override void Exit (People Entity)
	{
		//base.Exit (Entity);
	}
} 

这是角色对象的全局状态,其它状态与之类似。

如有任何疑问,可以发邮件给我讨论:liyuan0331@gmail.com。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值