开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

教程分享

关注:559

当前位置:游戏蛮牛 技术专区 教程分享

查看: 947|回复: 0

[基础知识] 基于unity3d(C#)的有限状态机设计 (一)

[复制链接]  [移动端链接]
抢楼 抢楼 本帖为抢楼帖,欢迎抢楼! 
8常驻蛮牛
9841/10000
排名
293
昨日变化

3421

主题

3691

帖子

9841

积分

Rank: 8Rank: 8

UID
1235
好友
168
蛮牛币
158507
威望
110
注册时间
2013-7-29
在线时间
432 小时
最后登录
2016-10-9

社区QQ达人游戏蛮牛QQ群会员蛮牛妹

发表于 2015-11-15 10:00:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
  1.什么是有限状态机

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

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

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

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

[AppleScript] 纯文本查看 复制代码
  {

  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类声明如下:

[AppleScript] 纯文本查看 复制代码
  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(状态)类的实力,还有一个方法来改变指针指向的状态。

[AppleScript] 纯文本查看 复制代码
  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)

  {

  }

  }


  再看看状态控制机的类:

[AppleScript] 纯文本查看 复制代码
  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基类:

[AppleScript] 纯文本查看 复制代码
  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);

  }

  }


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

我是一朵内心长满小碎花的女汉子!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表