笨木头  2018-04-27 21:36     Game Framework     阅读(15933)     评论(15)
转载请注明,原文地址: http://www.benmutou.com/archives/2643
文章来源:笨木头与游戏开发
本文 Game Framework 版本:3.1.1

本文 Unity3D 版本:2017.3

更多GF教程和实例:https://github.com/mutouzdl/gameframework_demo.git

 
转载请注明,原文地址:http://www.benmutou.com/archives/2643 文章来源:笨木头与游戏开发
 

最近有朋友需要Game Framework状态机的使用教程,所以我就简单说说状态机的用法吧。

本Demo只介绍Game Framework框架状态机的使用方式,至于什么是有限状态机,以及状态机的最佳使用方式,大家自行学习。

1.FsmState(有限状态机基类)






状态机包括角色的各个状态,只要让我们的状态类继承FsmState即可。

在继承FsmState的时候需要指定一个类型,这个类型就是状态的持有者。比如我们有一个英雄,英雄有行走和站立的状态,那么,这些状态的持有者就是这个英雄。

本Demo演示一个英雄的行走和站立状态切换,其中英雄是GameFramework中的Entity,如果大家还记得Demo6的话,我们创建实体的时候需要一个实体逻辑处理类,这个逻辑处理类就可以作为状态的持有者。

我们来看代码:
public class Demo10_ProcedureLaunch : ProcedureBase {
    protected override void OnEnter(ProcedureOwner procedureOwner)
    {
        base.OnEnter(procedureOwner);
        // 获取框架实体组件
        EntityComponent entityComponent
            = UnityGameFramework.Runtime.GameEntry.GetComponent<EntityComponent>();
        // 创建实体
        entityComponent.ShowEntity<Demo10_HeroLogic>(1, "Assets/Demo10/CubeEntity.prefab", "EntityGroup");
    }
}
 

在初始场景里加载一个实体,实体的预制体大家自己去源码下载,我就不演示怎么创建这个预制体了。

其中Demo10_HeroLogic是这个实体的逻辑处理类。

 

于是,站立状态类和行走状态类要这么创建:

public class Demo10_HeroIdleState : FsmState<Demo10_HeroLogic>

public class Demo10_HeroWalkState : FsmState<Demo10_HeroLogic>

和之前说的一样,状态类继承了FsmState,并且需要指定状态的持有者。

2.站立状态类(HeroIdleState






我们来看看站立状态类的具体实现:
public class Demo10_HeroIdleState : FsmState<Demo10_HeroLogic> {
	protected override void OnInit (IFsm<Demo10_HeroLogic> fsm) { }
	
	protected override void OnEnter (IFsm<Demo10_HeroLogic> fsm) {
		Log.Info("进入站立状态");
	}
	
	protected override void OnUpdate (IFsm<Demo10_HeroLogic> fsm, float elapseSeconds, float realElapseSeconds) {
		/* 按W、S或者上下方向键移动 */
		float inputVertical = Input.GetAxis ("Vertical");
		if (inputVertical != 0) {
			/* 移动 */
			ChangeState<Demo10_HeroWalkState>(fsm);
		}
	}
	
	protected override void OnLeave (IFsm<Demo10_HeroLogic> fsm, bool isShutdown) {
	}
	
	protected override void OnDestroy (IFsm<Demo10_HeroLogic> fsm) {
		base.OnDestroy (fsm);
	}
}
是不是角色状态类似曾相识?(旁白:并没有)

没错,你们猜对了,这和流程的生命周期简直一模一样。(旁白:不是的,我压根没有猜过)

因为流程就是状态类!流程就是状态类!流程就是,状态类。



所以,大家应该和容易理解状态类的用法。

如果我没有瞎猜错的话,对于状态类的各个生命周期,我们用的最多的会是OnEnter(进入状态时调用)和OnUpdate(轮询)。

比如,在进入站立状态时,切换到站立动画,在OnUpdate中判断是否切换到其它状态。这个大家根据实际情况使用。

Demo里的站立状态会判断是否按下了移动键,如果是,则切换到行走状态。

3.行走状态类(HeroWalkState






至于行走状态,基本上和站立状态一样,只是OnUpdate稍有不同:



如果按下了移动键,则调用状态持有者的Forward函数进行移动(这是自己写的函数,大家看源码即可)。

如果没有按下移动键,则切换回站立状态。

于是,在按下移动键和松开移动键的时候,英雄就会在行走和站立之间不断切换。

4.Owner(持有者)






状态类的每一个生命周期函数都会有一个fsm对象,通过fsm.Owner可以获得状态机持有者对象,这里的Owner就是我们的Demo10_HeroLogic类了。

因此,在各个状态里,可以对持有者进行操作。

5.让状态机启动






状态类有了,持有者有了,但是,它们怎么运作起来?

这个就要看Demo10_HeroLogic类了:



启动状态注意有以下步骤:

a.创建状态数组,有多少个状态类,都加到数组里

b.通过框架的FsmComponent组件的CreateFsm函数创建状态机,需要指定持有者类型、持有者对象、状态数组

c.调用状态机的Start函数启动初始状态,需要指定状态类型

6.运行






OK,现在一切都准备好,下载源码,运行游戏,看看效果吧。

按下键盘的w、s或者上下方向键控制英雄(就是,那个蓝色的方块)移动,如果一切顺利的话,应该要看到以下输出:



我还在场景里创建了地形,这是为了更清除地看到英雄的,咳咳,是蓝色方块的移动。

另外,按A和D还能旋转角度,很厉害吧(所以有个屁用啊)。

 

好了,状态机的使用就介绍这么多,只要理清了FsmState和持有者,其它的看看API文档就行了。
15 条评论
  • 誓要问天 2019-09-17 18:20:00

    木大木大,你在切换线程哪部分通过判断VerticalInput的值判断角色状态时,我怎么没办法转换到idle状态,貌似verticalInput值并不能到0,是版本更新的问题吗
    0回复
    • 博主 笨木头 2019-09-19 07:29:57

      你没有按键盘的时候,Input.GetAxis ("Vertical")的值打印出来是多少呢?
      0回复
  • 酷雪 2019-08-28 15:54:53

    请教下大神,这个例子里假如我想创建两个英雄,也就是我ShowEntity两次 赋予不同的ID,如下:
    entityComponent.ShowEntity(1, "Assets/Demo10/CubeEntity.prefab",
    "EntityGroup");
    entityComponent.ShowEntity(2, "Assets/Demo10/CubeEntity.prefab",
    "EntityGroup");
    但是unity却报错了,说FSM已经存在了,如下:
    Show entity failure, entity id '2', asset name 'Assets/Demo10/CubeEntity.prefab', entity group name 'EntityGroup', error message 'GameFramework.GameFrameworkException: Already exist FSM 'Demo10_HeroLogic'.

    难道说,所有英雄实例都必须公用一个FSM吗? 谢谢。
    0回复
    • 博主 笨木头 2019-08-29 07:48:59

      是因为状态机的名称不能重复,你可以这样创建:
      stateFsm = GameEntry.Fsm.CreateFsm("StateFsm" + this.Id, this, new FsmState[] {
      new FightEntityCDIdleState (),
      new FightEntityAtkCDState (),
      });

      第一个参数:【"StateFsm" + this.Id】,这样每个实体的FSM名称都不相同。
      0回复
  • kuxuee 2019-06-22 16:31:56

    请问大神,为什么cube前进的逻辑代码不放在WalkState中去实现而要放在实体logic中去实现呢,我想如果WalkState用来控制行走,那代码放在这里不是更合适吗,不然仅仅只是做状态监测会不会不合理。
    0回复
    • kuxuee 2019-06-22 16:35:48

      我懂了,是不是因为state类并不是继承自MonoBehaviour的原因,所以还是要依赖Logic来操作
      0回复
      • 博主 笨木头 2019-06-24 07:30:47

        也许更准确的原因是,State只是控制状态的转换,具体逻辑,还是由对象自身来实现。
        0回复
  • cool 2019-06-22 14:50:30

    感谢分享,想问问大神现在游戏做的怎么样了 瞻仰瞻仰
    0回复
    • 博主 笨木头 2019-06-24 07:36:13

      就是《炼裙者2第九感》,之前是给玩家群的一点点人测,现在是在TapTap上测试(人也超少),游戏很简陋,不存在瞻仰什么的( ╯□╰ )
      0回复
  • 小王 2019-01-04 17:12:52

    感谢~大赞,正在为入门这个框架烦恼时候~看到了你的分享,是我辈楷模~
    0回复
    • 博主 笨木头 2019-01-08 07:40:20

      (●ˇ∀ˇ●)(自豪)
      1回复
  • fanios 2018-06-29 01:31:59

    太棒了!相见恨晚!
    1回复
  • Ace丶双鱼 2018-06-28 11:58:15

    从讲StarForce第一篇,跟着写到Demo10,逐步了解了GF这个框架的功能,感谢木头的分享!
    后面也会自己写一些模块Demo ~。~
    0回复
    • 博主 笨木头 2018-06-28 13:20:34

      哈哈,记得告诉我你发布文章的地方
      0回复
  • 356688 2018-05-25 19:44:07

    拜读了,多多学习总是好的!
    1回复
发表评论
粤ICP备16043700号

本博客基于 BlazorAnt Design Blazor 开发