加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

quick-cocos2d-x游戏开发——StateMachine状态机

发布时间:2020-12-14 19:19:33 所属栏目:百科 来源:网络整理
导读:转载自:http://blog.csdn.net/w337198302/article/details/39430647# 状态机在quick中是一个亮点,如果我们做一款RPG游戏,一个角色一般会拥有idle,attack,walk,run,death这些状态,如果游戏角色的状态采用分支条件判断的话,会造成非常庞大而难以维护

转载自:http://blog.csdn.net/w337198302/article/details/39430647#

状态机在quick中是一个亮点,如果我们做一款RPG游戏,一个角色一般会拥有idle,attack,walk,run,death这些状态,如果游戏角色的状态采用分支条件判断的话,会造成非常庞大而难以维护,但一旦使用了状态机这种模式,就会显得简单方便。


对于quick中的状态机是如何实现的咱们先不去了解,首先看看如何去使用它。

总结起来,如果让一个类拥有状态机,主要有两步:

1.创建状态机对象

2.初始化状态机,主要包括事件和回调函数


1.创建状态机组件

[html] view plain copy
  1. self.fsm={}
  2. cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods()

这样就创建了一个状态机对象,接下来我们要对其初始化,其实也就是设置各个状态的逻辑。


2.初始化状态机(设置状态逻辑)

设置状态逻辑是重写setupState方法,这其中有这么几个字段参数,

  • initial:状态机的初始状态
  • terminal (final):结束状态
  • events:状态发生转变时对应的事件
  • callbacks:发生转变时的回调函数

一般我们会设置initial,events和callbacks这三个。


先看events,在events中需要分清楚“事件”和“状态”,events采用table结构,例如我们来写一个

copy
    events={
  1. {name="move",from={"idle","jump"},to="walk"},

这其中move是事件,就像触摸事件event.name那样,name表示事件名称,而from和to后面跟的idle,jump,walk表示状态。所以上面的意思就是,当执行move事件时,如果状态是idle或者jump,那么都会跳转到walk状态上。

from的状态可以是单一状态,也可以使集合状态,就是几个状态,但to的状态只能唯一,不然程序还给你来个随机状态?肯定不行的。


所以这里需要想好我们的主角有哪些状态,当什么事件发生时,他会从什么状态变到什么状态上去。例如我简单这么写,

copy
    {name="attack","walk"},to="jump"},
  1. {name="normal",from={"walk",to="idle"},108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> },

解释一下,如果是normal事件,不管主角在走路walk还是跳跃jump,都会变成闲置idle状态。其他同理。


接下来一个重点是callbacks参数,

即所谓回调了,就是事件触发,会执行一系列的函数。

  • onbeforeEVNET: 在事件EVENT开始前被激活
  • onleaveSTATE: 在离开旧状态STATE时被激活
  • onenterSTATE 或 onSTATE:在进入新状态STATE时被激活
  • onafterEVENT 或 onEVENT:在事件EVENT结束后被激活

例如

copy
    callbacks={
  1. onenteridle=function()--或者onidle
  2. print("idle")
  3. end,
  4. 此外还有5种通用型的回调来捕获所有事件和状态的变化:
    • onbeforeevent: 在任何事件开始前被激活
    • onleavestate: 在离开任何状态时被激活
    • onenterstate:在进入任何状态时被激活
    • onafterevent :在任何事件结束后被激活
    • onchangestate :当状态发生改变的时候被激活
    这里面的名称是不可以修改的,它是针对于任何事件和任何状态的。
    所以大家可以想象一下这其中有多少事件回调和多少状态回调,它们的先后顺序,咱们可以自己分别print一下就知道调用的先后了,这里就不演示了。

    最后,就是调用这些事件了,通过self.fsm:doEvent(event)就可以了,参数event对应events参数名称。此外还有这些,
    • fsm:isReady() :返回状态机是否就绪
    • fsm:getState() :返回当前状态
    • fsm:isState(state) :判断当前状态是否是参数state状态
    • fsm:canDoEvent(eventName) :当前状态如果能完成eventName对应的event的状态转换,则返回true
    • fsm:cannotDoEvent(eventName) :当前状态如果不能完成eventName对应的event的状态转换,则返回true
    • fsm:isFinishedState() :当前状态如果是最终状态,则返回true
    • fsm:doEventForce(name,...) :强制对当前状态进行转换

    接下来在实际运用一下,我们创建一个Player类,为其添加一个状态机,
    [html] view plain copy
    1. localPlayer=class("Player",function()
    2. returndisplay.newSprite("icon.png")
    3. end)
    4. functionPlayer:ctor()
    5. self:addStateMachine()
    6. end
    7. functionPlayer:doEvent(event)
    8. self.fsm:doEvent(event)
    9. functionPlayer:addStateMachine()
    10. self.fsm={}
    11. cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods()
    12. self.fsm:setupState({
    13. initial="idle",
    14. events={
    15. {name="move",
    16. {name="attack",108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> {name="normal",248)"> },
    17. callbacks={
    18. onenteridle=function()
    19. localscale=CCScaleBy:create(0.2,1.2)
    20. self:runAction(CCRepeat:create(transition.sequence({scale,scale:reverse()}),2))
    21. onenterwalk=function()
    22. localmove=CCMoveBy:create(0.2,ccp(100,0))
    23. self:runAction(CCRepeat:create(transition.sequence({move,move:reverse()}),2))
    24. end,
    25. onenterjump=function()
    26. localjump=CCJumpBy:create(0.5,ccp(0,0),100,2)
    27. self:runAction(jump)
    28. })
    29. returnPlayer

    比较简单,回调函数只是写了进入三个状态的回调,然后为Player添加一个doEvent函数,调用状态机中doEvent。

    回到我们的MyScene.lua中,
    copy
      localPlayer=import("..views.Player")
    1. localMyScene=class("MyScene",248)"> returndisplay.newScene("MyScene")
    2. functionMyScene:ctor()
    3. localplayer=Player.new()
    4. player:setPosition(display.cx,display.cy)
    5. self:addChild(player)
    6. localfunctionmenuCallback(tag)
    7. iftag==1then
    8. player:doEvent("normal")
    9. elseiftag==2then
    10. player:doEvent("move")
    11. elseiftag==3then
    12. player:doEvent("attack")
    13. end
    14. localmormalItem=ui.newTTFLabelMenuItem({text="normal",x=display.width*0.3,y=display.height*0.2,listener=menuCallback,tag=1})
    15. localmoveItem=ui.newTTFLabelMenuItem({text="move",x=display.width*0.5,tag=2})
    16. localattackItem=ui.newTTFLabelMenuItem({text="attack",x=display.width*0.7,tag=3})
    17. localmenu=ui.newMenu({mormalItem,moveItem,attackItem})
    18. self:addChild(menu)
    19. returnMyScene

    添加我们刚才的Player,记得import或者require,这里为了方便我就通过菜单按钮的形式来分别doEvent了。

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读