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

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

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

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

1.创建状态机对象

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

1.创建状态机组件

self.fsm = {}
cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods()

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

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

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

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

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

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

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

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

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

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

events = {
	{name = "move", from = {"idle", "jump"}, to = "walk"},
	{name = "attack", from = {"idle", "walk"}, to = "jump"},
	{name = "normal", from = {"walk", "jump"}, to = "idle"},
},

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

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

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

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

例如

callbacks = {
	onenteridle = function ()  --或者 onidle
		print("idle")
	end,
},

此外还有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类,为其添加一个状态机,

local Player = class("Player", function ()
	return display.newSprite("icon.png")
end)

function Player:ctor()
	self:addStateMachine()
end

function Player:doEvent(event)
	self.fsm:doEvent(event)
end

function Player:addStateMachine()
	self.fsm = {}
	cc.GameObject.extend(self.fsm):addComponent("components.behavior.StateMachine"):exportMethods()

	self.fsm:setupState({
		initial = "idle",

		events = {
			{name = "move", from = {"idle", "jump"}, to = "walk"},
			{name = "attack", from = {"idle", "walk"}, to = "jump"},
			{name = "normal", from = {"walk", "jump"}, to = "idle"},
		},

		callbacks = {
			onenteridle = function ()
				local scale = CCScaleBy:create(0.2, 1.2)
				self:runAction(CCRepeat:create(transition.sequence({scale, scale:reverse()}), 2))
			end,

			onenterwalk = function ()
				local move = CCMoveBy:create(0.2, ccp(100, 0))
				self:runAction(CCRepeat:create(transition.sequence({move, move:reverse()}), 2))
			end,

			onenterjump = function ()
				local jump = CCJumpBy:create(0.5, ccp(0, 0), 100, 2)
				self:runAction(jump)
			end,
		},
	})
end

return Player

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

回到我们的MyScene.lua中,

local Player = import("..views.Player")

local MyScene = class("MyScene", function ()
	return display.newScene("MyScene")
end)

function MyScene:ctor()	

    local player = Player.new()
    player:setPosition(display.cx, display.cy)
    self:addChild(player)

    local function menuCallback(tag)
        if tag == 1 then
            player:doEvent("normal")
        elseif tag == 2 then
            player:doEvent("move")
        elseif tag == 3 then
            player:doEvent("attack")
        end
    end

    local mormalItem = ui.newTTFLabelMenuItem({text = "normal", x = display.width*0.3, y = display.height*0.2, listener = menuCallback, tag = 1})
    local moveItem =  ui.newTTFLabelMenuItem({text = "move", x = display.width*0.5, y = display.height*0.2, listener = menuCallback, tag = 2})
    local attackItem =  ui.newTTFLabelMenuItem({text = "attack", x = display.width*0.7, y = display.height*0.2, listener = menuCallback, tag = 3})
    local menu = ui.newMenu({mormalItem, moveItem, attackItem})
    self:addChild(menu)

end

return MyScene

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

时间: 2024-10-31 00:02:38

quick-cocos2d-x游戏开发【14】——StateMachine状态机的相关文章

简述游戏开发中的状态机

为什么我们需要状态机 实行较多状态的角色,把动作全写在一个部分中会导致维护成本高,拓展性低 例如:走路,跳跃,射击,躲避的相互转换,有些可以转换,有些不能,实现逻辑复杂 (满屏幕都是if - else) 状态模式switch实现 //包含着所有的状态 enum class State{StateA, StateB, StateC, ...} activeState; ... //通过switch语句切换状态,根据具体情况实现细节 switch (activeState) { case State

6_State 游戏开发中使用状态机

### State 不好的代码 ``` //处理玩家输入的代码 void Heroine::handleInput(Input input) { if (input == PRESS_B) { if (!isJumping_ && !isDucking_) { // Jump... } } else if (input == PRESS_DOWN) { if (!isJumping_) { isDucking_ = true; setGraphics(IMAGE_DUCK); } else

史上最全最完整的IOS 游戏开发 PDF电子书定制下载

<iOS 5游戏开发>作者:(新西兰)James·Sugrue著 页数:191 出版社:北京市:人民邮电出版社 出版日期:2012.08 简介:<iOS5游戏开发>是一本iOS5游戏开发的基础入门书.全书使用通俗易懂的简单实例,带领读者经历构建经典动作游戏的整个周期.读者在本书的阅读过程中,将经历从开发概念.规划设计一直到编写实际代码的全过过程.本书的每一章,都将演示游戏创建过程中的一个逻辑步骤,读者将在其中学习如何创建Sprite,用触摸屏.重力感应器和屏幕游戏棒控制玩家角色等-

【Cocos2D研究院之游戏开发】

http://www.xuanyusong.com/archives/category/ios/cocos2d_game 分类目录归档:[Cocos2D研究院之游戏开发] 201211-19 Cocos2D研究院之打开全新ViewController与返回(八) 雨松MOMO [Cocos2D研究院之游戏开发] 围观5745次 17条评论          之前cocos2d的文章都是由魏凯同学维护,从今天开始我也会抽时间写点cocos2d的文章.最近在研究如何将IOS游戏与软件结合起来.通常游

cocos2d 游戏开发实战

文章转自:http://uliweb.clkg.org/tutorial/read/40 6   cocos2d 游戏开发实战 6.1   创建cocos2d项目 6.2   cocos2d v3 "hello world" 6.2.1   显示一个飞船 6.3   精灵 6.4   開始 space viking 之旅 6.4.1   添加 sneakyinput 6.5   精灵的移动效果,旋转效果 6.6   定时器效果 6.7   启动 cocos2d,默认竖屏 6.8   检

[ios5 cocos2d游戏开发实战] 笔记3-FileUtils, notificationCenter

FileUtils //文件管理工具 FileUtils::getInstance() std::string getStringFromFile(const std::string& filename);//读取文件中的字符串 Data getDataFromFile(const std::string& filename);//获取文件数据 void setSearchPaths(const std::vector<std::string>& searchPaths

14、Cocos2dx 3.0游戏开发找小三之Scene and Layer:一场游戏一场梦

重开发者的劳动成果,转载的时候请务必注明出处:http://blog.csdn.net/haomengzhu/article/details/30474393 Scene :场景 了解了Director 之后,接下来介绍 Scene 这个与它紧密相关的游戏组件. 通过之前的学习,我们已经了解了场景以及它在流程控制中的地位. 在 Cocos2d-x 中,Scene 定义了一个场景.场景只是层的容器,包含了所有需要显示的游戏元素. 因此相对于其他游戏元素,Scene 并没有提供什么特别的功能,就是一

使用 Cocos2d 3.1.1 创建 Windows Phone 8 游戏开发环境

cocos2d-x 是目前流行的游戏游戏开发框架,目前最新的版本是 3.1.1, 网上有些教程已经比较老了,本文将会介绍如何使用最新的 3.1.1 创建 Windows Phone 8 开发环境. 本文假设你已经安装了 VS2012 或者 VS2013,并且已经安装了 Windows Phone8 的 SDK. 一.下载和安装 Cocos2d-x 官网地址:http://www.cocos2d-x.org/ 点击菜单栏中的 Download, 进入下载页面. 我们下载最新的 V3.1.1,新版功

cocos2d 游戏开发:Cocos2d v3 &quot;hello world&quot;+显示飞船

V3 RC4 版本图片 显示一个飞船 将Chapter1中 SpaceCargoShip.png 文件 添加到项目里面. 代码在 init : CCSprite *spaceCargoShip = [CCSprite spriteWithImageNamed:@"SpaceCargoShip.png"]; [spaceCargoShip setPosition:ccp(200.0f,150.9f)]; [self addChild:spaceCargoShip]; 在上面的代码下增加如

[游戏开发日志]Windows下Cocos2d-x 3.14环境搭建

总介绍 我们小组使用的是cocos2d-x的游戏开发引擎,因此在所有开发工作之前,我们需要对这个引擎进行环境的搭建. 搭建过程 VS2013的下载和安装 VS只是作为一个开发环境而已,简单来说就是敲代码用的,都中文的,搞起来很简单,我就不介绍了 图1: 在cocos2d-x的网站上即可下到cocos2d-x的最新版本,下载后用vs即可打开相应的工程文件 我们下载版本3.14.1,完成下载后,我们打开该压缩包 图2: 此外,我们还需要进行python的安装,是因为在编译这个引擎时,python是必