cocos2dx x y z 旋转敌人状态机z怎么恢复

主题 : cocos2dx3.2开发 RPG《Flighting》(六)角色的状态和控制效果
级别: 侠客
UID: 352460
可可豆: 386 CB
威望: 329 点
在线时间: 54(时)
发自: Web Page
来源于&&分类
cocos2dx3.2开发 RPG《Flighting》(六)角色的状态和控制效果&&&
本帖被 abc88798 执行加亮操作()
一、前言一般来说,我们在做一个稍微有点智能的实体的时候都会用到状态机。这里我没有用到,但是我也为角色设置了几种状态。如果大家看了上一节,也完成了角色的基本移动的话。对比过我一直说的《BattleHeart》之后,你会发现,现在的控制移动操作也太挫了,既没有那一条拉出来的导航线,也没有角色被选中的效果。所以接下来我们要对我们的基本控制操作加入一些效果。二、正文1、角色的状态来来去去我们的角色无非几种基本状态 待机、移动、攻击、受伤、死亡。所以我们定义一个抽象类。声明了所有的状态对应的函数。
#ifndef _ROLEPROTOCOL_H_&&
#define _ROLEPROTOCOL_H_&&
class RoleProtocol{&&
&&&&virtual void stand() = 0;&&
&&&&virtual void move() = 0;&&
&&&&virtual void attack() = 0;&&
&&&&virtual void skill() = 0;&&
&&&&virtual void injured(int effect,int damage=0) = 0;&&
&&&&virtual void die() = 0;&&
我们的Role类也做出相应的修改,继承RoleProtocol并且重写实现这些接口在Role里面定义状态枚举类,并且增加一个成员变量表示当前角色的状态
&&&&enum ROLE_STAT{&&
&&&&&&&&ROLE_STAND,&&
&&&&&&&&ROLE_MOVE,&&
&&&&&&&&ROLE_ATTACK,&&
&&&&&&&&ROLE_SKILL,&&
&&&&&&&&ROLE_INJURED,&&
&&&&&&&&ROLE_DIE,&&
&&&&&&&&ROLE_NULL&&
ROLE_STAT en_&&//当前状态&&
记得上一节的最后我们的两个方法move()和stand(),他们的由来就是上面所说的,下面看他们两个的实现
void Role::stand(){&&
&&&&if(m_arm && en_stat!=ROLE_STAND){&&
&&&&&&&&m_arm-&getAnimation()-&play(&stand&);&&
&&&&&&&&CCLOG(&id=%d:stand&,m_id);&&
&&&&&&&&en_stat = ROLE_STAND;&&
void Role::move(){&&
&&&&if(m_arm && en_stat!=ROLE_MOVE){&&
&&&&&&&&en_stat = ROLE_MOVE;&&
&&&&&&&&m_arm-&getAnimation()-&play(&walk&);&&
很简单,就是先判断是不是已经在播放站立或行走动画了,如果是,没必要在播放(不然会定在那里不动),如果不是,则播放动画。现在暂时会用到的就这两种状态,其他状态以后用到的时候会在清楚说明。2、控制效果如果按现在的操作,点击选中我们的角色之后,要拉出去到目标点,再放开手(原本BattleHeart就是这样操作的)。那么在视觉上起码也要有点效果提醒用户。1)选中的高亮效果实现思路:在Role里面设置一个属性m_isHL,还有一个初始不可见的Sprite,m_select_circleLayer中检测到Role被点击之后,设置m_isHL为true,Role的update函数会时刻检测m_isHL,如果为true的话,m_select_circle变得可见。需要注意的是,还有很多小细节小逻辑需要处理,例如什么时候m_isHL设置为false,用户点击空白区域的时候(就是没有选中Role)怎么处理等等,不过也很简单,相信大家自己也能写出来。2)拖出去的导航线实现思路:当触摸事件为move的时候,就是已经onTouchBegan选中角色了,鼠标在移动但是左键没有松开的时候。在起点(角色的位置)和终点(鼠标的位置)之间画一条线。这里给出我的代码供大家参考。
bool Role::onTouchBegan(Touch* touch,Event* event){&&
&&&&if(!m_controlable){&&
&&&&&&&&&&
&&&&if(m_arm){&&
&&&&&&&&Point point = touch-&getLocationInView();&&
&&&&&&&&point = Director::getInstance()-&convertToGL(point);&&
&&&&&&&&m_endPoint =&&
&&&&&&&&CCLOG(&TOUCH POINT PX = %f,PY = %f&,point.x,point.y);&&
&&&&&&&&&&
&&&&&&&&Rect heroRect = getBoundingBox();&&
&&&&&&&&if(heroRect.containsPoint(point)){&span style=&white-space:pre&&&&&&&&&/span&//是这个if&&
&&&&&&&&&&&&m_trace = Sprite::create(&color.png&);&&&&&&&&
&&&&&&&&&&&&m_trace-&setAnchorPoint(Point(0,0.5f));&&
&&&&&&&&&&&&this-&addChild(m_trace,0);&&
&&&&&&&&&&&&CCLOG(&CLICK MY HERO&);&&
&&&&&&&&&&&&setHeightLight(true);&&
&&&&&&&&&&&&&&
&&&&&&&&}else{&&
&&&&&&&&&&&&CCLOG(&NOT CONTAIN&);&&
&&&&&&&&&&&&&&
&&&&&&&&}&&
onTouchBegan里面判断如果点中角色(上面代码那个if里面),会创建一个叫m_trace的Sprite。m_trace开始的时候只是很小的一块矩形(&color.png&只是很小的一块矩形,并不是一条线)。
void Role::onTouchMoved(Touch* touch,Event* event){&&
&&&&Point point = touch-&getLocationInView();&&
&&&&point = Director::getInstance()-&convertToGL(point);&&
&&&&m_endPoint =&&
明显,onTouchMoved里面就是设置m_endPoint而已(m_endPoint就是导航线的终点)
void Role::onTouchEnded(Touch* touch,Event* event){&&
&&&&if(m_trace){&&
&&&&&&&&m_trace-&removeFromParentAndCleanup(true);&&
&&&&&&&&m_trace =&&
&&&&&&&&setHeightLight(false);&&
onTouchEnded的触发表明已经松开鼠标左键。m_trace(导航线)应该消失了关于导航线重要的还是下面的update函数update中调用update_trace
void Role::update_trace(){&&
&&&&if(m_trace){&&
&&&&&&&&if(getBoundingBox().containsPoint(m_endPoint)){&&
&&&&&&&&&&&&m_endPoint = this-&getPosition();&&
&&&&&&&&}&&
&&&&&&&&Point begin(CCDirector::getInstance()-&convertToGL(this-&getPosition()));&&
&&&&&&&&Point end(CCDirector::getInstance()-&convertToGL(m_endPoint));&&
&&&&&&&&m_trace-&setRotation(CC_RADIANS_TO_DEGREES(ccpToAngle(end - begin)));&&&&//根据起点和终点,设置旋转角度&&
&&&&&&&&m_trace-&setScaleX(ccpDistance(begin,end)/m_trace-&getContentSize().width);&span style=&white-space:pre&&&& &/span&//根据起点和终点的距离,设置小方块的拉伸&&
重要的代码后面已经添加了注释,应该不难理解。最后,附上我们优化完的控制效果。本节结束。我的csdn地址:邮箱地址:如有问题或指教,欢迎与我交流,谢谢。
关注本帖(如果有新回复会站内信通知您)
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 关注CVP公众号
扫一扫 浏览移动版详解cocos2dx状态机
注:写这篇文章的时候,笔者所用的是quick-cocZ喎"/kf/ware/vc/" target="_blank" class="keylink">vczJkLXggMi4yLjFyY7Dmsb6hozwvcD4NCjxwPiZuYnNwOzwvcD4NCjxoMz5xdWlja9e0zKy7+jwvaDM+DQo8cD7XtMysu/q1xMnovMajrMS/tcS+zcrHzqrBy7Hcw+K088G/17TMrLXExdC2z7T4wLS1xLi01NPQ1KOsz/uz/cXTtPO1xMz1vP631tan0+++5KOs0vLOqrTzwb+1xLfW1qfF0LbPu+HKubXDs8zQ8sTR0tTQ3rjEus3AqdW5oaO1q3F1aWNr17TMrLv6tcTJ6LzG09ayu82syei8xsSjyr21xNe0zKzEo8q9o6xUQcO709C9q7j3uPbXtMystaW2wLuut9azybWltsC1xNe0zKzA4KOsz+C3tLj5vt1qc6GibHVh0+/R1LXEzNi146OszNix8MnovMbBy9C0t6ijrMq508PG8MC00rKxyL3Pt72x46GjPC9wPg0KPHA+cXVpY2u/8rzc1tC1xNe0zKy7+qOsyse4+b7dPGEgaHJlZj0="/jakesgordon/javascript-state-machine" target="_blank">javascript-state-machine重新设计改写而成,同时sample/statemachine范例也是根据js版demo改写而来。该js库现在是2.2.0版本。基于js版的README.md,结合廖大的lua版重构,我针对状态机的使用做了点说明,如果有不对的地方,感谢指出:)。
推荐大家在理解的时候结合sample/statemachine范例进行理解,注意player设置成竖屏模式,demo里面的按钮在横屏模式下看不见。
sample图示
创建一个状态机
local fsm = StateMachine.new()
-- (注:和demo不同的是,demo采用形式完成的初始化)
fsm:setupState({
initial = &green&,
{name = &warn&,
from = &green&,
to = &yellow&},
{name = &panic&, from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &yellow&},
{name = &clear&, from = &yellow&, to = &green& },
之后我们就可以通过
fsm:doEvent(&start&)-从&none&状态转换到&green&状态fsm:doEvent(&warn&)-从&green&状态转换到&yellow&状态fsm:doEvent(&panic&)-从&green&状态转换到&red&状态fsm:doEvent(&calm&)-从&red&状态转换到&yellow&状态fsm:doEvent(&clear&)-从&yellow&状态转换到&green&状态
fsm:isReady()-返回状态机是否就绪fsm:getState()-返回当前状态fsm:isState(state)-判断当前状态是否是参数state状态fsm:canDoEvent(eventName)-当前状态如果能完成eventName对应的event的状态转换,则返回truefsm:cannotDoEvent(eventName)-当前状态如果不能完成eventName对应的event的状态转换,则返回truefsm:isFinishedState()-当前状态如果是最终状态,则返回truefsm:doEventForce(name, ...)-强制对当前状态进行转换
单一事件的多重from和to状态
如果一个事件允许我们从多个状态(from)转换到同一个状态(to), 我们可以通过用一个集合来构建from状态。如下面的&rest&事件。但是,如果一个事件允许我们从多个状态(from)转换到对应的不同的状态(to),那么我们必须将该事件分开写,如下面的&eat&事件。
local fsm = StateMachine.new()
fsm:setupState({
initial = &hungry&,
{name = &eat&,
from = &hungry&,
to = &satisfied&},
{name = &eat&,
from = &satisfied&,
to = &full&},
{name = &eat&,
from = &full&,
to = &sick&
{name = &rest&, from = {&hungry&, &satisfied&, &full&, &sick&},
to = &hungry&},
在设置了事件events之后,我们可以通过下面两个方法来完成状态转换。
fsm:doEvent(&eat&)fsm:doEvent(&rest&)
rest事件的目的状态永远是hungry状态,而eat事件的目的状态取决于当前所处的状态。
注意1:如果事件可以从任何当前状态开始进行转换,那么我们可以用一个通配符*来替代from状态。如rest事件,我们可以写成{name = &rest&, from = &*&, to = &hungry&}。
注意2:上面例子的rest事件可以拆分写成4个,如下:
{name = &rest&, from = &hungry&,
to = &hungry&},
{name = &rest&, from = &satisfied&, to = &hungry&},
{name = &rest&, from = &full&,
to = &hungry&},
{name = &rest&, from = &sick&,
to = &hungry&}
quick的状态机支持4种特定事件类型的回调:
onbeforeEVNET- 在特定事件EVENT开始前被激活onleaveSTATE - 在离开旧状态STATE时被激活onenterSTATE - 在进入新状态STATE时被激活onafterEVENT - 在特定事件EVENT结束后被激活
注解:编码时候,EVENT/STATE应该被替换为特定的名字
为了便利起见,
onenterSTATE可以简写为onSTATEonafterEVENT可以简写为onEVENT
所以假如要使用简写的话,为了避免onSTATE和onEVENT的STATE/EVENT被替换成具体的名字后名字相同引起问题,to状态和name名字尽量不要相同。比如
-- 角色开火
{name = &fire&,
from = &idle&,
to = &fire&}
--假如使用简写
--onSTATE --- onfire
--onEVENT --- onfire,回调会引起歧义。
--如果不使用简写
--则onenterSTATE --- onenterfire
--onafterEVENT --- onafterfire
另外,我们可以使用5种通用型的回调来捕获所有事件和状态的变化:
onbeforeevent- 在任何事件开始前被激活onleavestate - 在离开任何状态时被激活onenterstate - 在进入任何状态时被激活onafterevent - 在任何事件结束后被激活onchangestate - 当状态发生改变的时候被激活
注解:这里是任何事件、状态, 小写的event、state不能用具体的事件、状态名字替换。
所有的回调都以event为参数,该event为表结构,包含了
name 事件名字from 事件表示的起始状态to 事件表示的目的状态args 额外的参数,用来传递用户自定义的一些变量值
local fsm = StateMachine.new()
fsm = fsm:setupState({
initial = &green&,
{name = &warn&,
from = &green&,
to = &yellow&},
{name = &panic&, from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &yellow&},
{name = &clear&, from = &yellow&, to = &green& },
callbacks = {
onbeforestart = function(event) print(&[FSM] STARTING UP&) end,
= function(event) print(&[FSM] READY&) end,
onbeforewarn
= function(event) print(&[FSM] START
EVENT: warn!&) end,
onbeforepanic = function(event) print(&[FSM] START
EVENT: panic!&) end,
onbeforecalm
= function(event) print(&[FSM] START
EVENT: calm!&) end,
onbeforeclear = function(event) print(&[FSM] START
EVENT: clear!&) end,
= function(event) print(&[FSM] FINISH
EVENT: warn!&) end,
fsm:doEvent(&warn&, &some msg&)
如上例子,fsm:doEvent(&warn&, &some msg&)中的some msg作为额外的参数字段args结合name from to被添加到event,此时
name = &warn&,
from = &green&,
= &yellow&,
args = &some msg&
而event表正是回调函数的参数。
用{name = &clear&, from = &red&, to = &green&}举例,我画个示意图来说明
注意:之前的onbeforeEVENT,这里EVENT就被具体替换为clear,于是是onbeforeclear,而onbeforeevent类似的通用型则不用替换。
onbeforeclear - clear事件执行前的回调onbeforeevent - 任何事件执行前的回调onleavered - 离开红色状态时的回调onleavestate - 离开任何状态时的回调onentergreen - 进入绿色状态时的回调onenterstate - 进入任何状态时的回调onafterclear - clear事件完成之后的回调onafterevent - 任何事件完成之后的回调
3种影响事件响应的方式
在onbeforeEVENT方法中返回false来取消事件在onleaveSTATE方法中返回false来取消事件在onleaveSTATE方法中返回ASYNC来执行异步状态转换
异步状态转换
有时候,我们需要在状态转换的时候执行一些异步性代码来确保不会进入新状态直到代码执行完毕。
举个例子来说,假如要从一个menu状态转换出来,或许我们想让TA淡出?滑出屏幕之外?总之执行完动画再进入game状态。
我们可以在onleavestate或者onleaveSTATE方法里返回StateMachine.ASYNC,这时状态机会被挂起,直到我们使用了event的transition()方法。
onleavered
= function(event)
self:log(&[FSM] LEAVE
STATE: red&)
self:pending(event, 3)
self:performWithDelay(function()
self:pending(event, 2)
self:performWithDelay(function()
self:pending(event, 1)
self:performWithDelay(function()
self.pendingLabel_:setString(&&)
event.transition()
return &async&
提示:如果想取消异步事件,可以使用event的cancel()方法。
初始化选项
状态机的初始化选项一般根据我们游戏需求来决定,quick状态机提供了几个简单的选项。在默认情况下,如果你没指定initial状态,状态机会指定当前状态为none状态,所以需要定义一个能将none状态转换出去的事件。
local fsm = StateMachine.new()
fsm = fsm:setupState({
{name = &startup&, from = &none&,
to = &green& },
{name = &panic&,
from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &green&}
echoInfo(fsm:getState()) -- &none&
fsm:doEvent(&start&)
echoInfo(fsm:getState()) -- &green&
如果我们特别指定了initial状态,那么状态机在初始化的时候会自动创建startup事件,并且被执行。
local fsm = StateMachine.new()
fsm = fsm:setupState({
initial = &green&,
-- 当指定initial状态时,这个startup事件会被自动创建,所以可以不用写这一句 {name = &startup&, from = &none&,
to = &green& },
{name = &panic&,
from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &green&}
echoInfo(fsm:getState()) -- &green&
我们也可以这样指定initial状态:
local fsm = StateMachine.new()
fsm = fsm:setupState({
initial = {state = &green&, event = &init&},
{name = &panic&,
from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &yellow&}
echoInfo(fsm:getState()) -- &green&
如果我们想延缓初始化状态转换事件的执行,我们可以添加defer = true
local fsm = StateMachine.new()
fsm = fsm:setupState({
initial = {state = &green&, event = &init&, defer = true},
{name = &panic&,
from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &green&}
echoInfo(fsm:getState()) -- &none&
fsm:doEvent(&init&)
echoInfo(fsm:getState()) -- &green&
在默认情况下,如果我们尝试着执行一个当前状态不允许转换的事件,状态机会抛出异常。如果选择处理这个异常,我们可以定义一个错误事件处理。在quick中,发生异常的时候StateMachine:onError_(event, error, message)会被调用。
local fsm = StateMachine.new()
fsm:setupState({
initial = &green&,
{name = &warn&,
from = &green&,
to = &yellow&},
{name = &panic&, from = &green&,
to = &red&
{name = &calm&,
from = &red&,
to = &green&},
{name = &clear&, from = &yellow&, to = &green& },
fsm:doEvent(&calm&) -- fsm:onError_会被调用,在当前green状态下不允许执行calm事件
本文如果有写的不对的地方,还请大家指出,交流学习:)
如果朋友们有关于状态机的使用心得,也非常欢迎分享。
暗黑项目实际使用情况:
在main的最后
---------------场景管理
g_sceneManager = require &src.SceneManager&.new()
g_sceneManager:initEventData()
---------------场景管理
cc.Director:getInstance():replaceScene(g_sceneManager.scene)
其中状态机是在initEventData()里初始化的,
function SceneManager:initEventData()
Component.addComponent(self, &ponents.behavior.StateMachine&)
local cfg =
initial = &login&,
events = {},
callbacks =
onenterstate = function(event) return self:onenterstate(event) end,
onleavestate = function(event) return self:onleavestate(event) end,
self.stateList =
{&changeToLogin&, &login&, &LoginScene&},
{&changeToCity&, &city&, &CityScene&},
{&changeToBattle&, &battle&, &BattleScene&},
{&changeToBoss&, &boss&, &BossScene&},
{&changeToLoginLoading&, &loginLoading&, &LoginLoadingScene&},
{&changeToUnionFight&, &unionFight&, &UnionFightScene&},
{&changeToChangeState&, &changeState&, &ChangeStateScene&},
{&changeToChangeFight&, &changeFight&, &ChangeFightScene&},
{&changeToTeam&, &team&, &TeamScene&},
{&changeToJiebao&, &jiebao&, &JiebaoScene&},
{&changeToStory&, &story&, &StoryScene&},
{&changeToGlory&, &glory&, &GloryScene&},
{&changeToQunMo&, &qunmo&, &QunMoScene&},
{&changeToBigUnion&, &bigunion&, &BigUnionScene&},
{&changeToSoulStone&, &soulstone&, &SoulStoneScene&},
{&changeToPaTa&,&pata&,&PaTaScene&},
{&changeToShenShou&,&shenshou&,&ShenShouScene&},
{&changeToQiangDao&,&qiangdao&,&QiangDaoScene&},
for i, state in ipairs(self.stateList) do
local t = {name = state[1], to = state[2]}
table.insert(cfg.events, t)
self[&onenter&..state[2] ] = function(self)
print(&onenter&..state[2])
self.gameScene = import(&src.scene.&..state[3]).new()
self.gameScene:onenter(self.scene)
self[state[1] ] = function(self)
self:doEvent(state[1])
self:setupState(cfg)
Component.addComponent(self, &ponents.behavior.StateMachine&)是将场景管理绑定了状态机,分析底层代码,我们看到
function Component.addComponent(target, name)
if ponents_ then
Component.extend_(target)
target:addComponent(name):exportMethods()
先执行Component.extend_(target)
继续跟进这个函数里面 function Component.extend_(target)
ponents_ = {}
function target:checkComponent(name)
ponents_[name] ~= nil
function target:addComponent(name)
local component = Registry.newObject(name)
ponents_[name] = component component:bind_(self)
return component
function target:removeComponent(name)
local component = ponents_[name]
if component then component:unbind_() end
ponents_[name] = nil
function target:getComponent(name)
ponents_[name]
end 其实这里面啥也没干,只是给target注册了几个接口,紧接着
target:addComponent(name):exportMethods() 就调用了里面的接口,我们先着重分析接口 target:addComponent(name) ,它里面实际上是把StateMachine这个文件加载了进来并进行了初始化 ,
function Registry.newObject(name, ...)
local cls = Registry.classes_[name]
if not cls then
-- auto load
pcall(function()
cls = require(name)
Registry.add(cls, name)
assert(cls ~= nil, string.format(&Registry.newObject() - invalid class \&%s\&&, tostring(name)))
return cls.new(...)
然后将状态机绑定了在场景管理器中
function Component:bind_(target)
self.target_ = target
for _, name in ipairs(self.depends_) do
if not target:checkComponent(name) then
target:addComponent(name)
self:onBind_(target)
target:addComponent(name)它返回的是一个StateMachine对象,然后调用了它的 exportMethods() ,继续跟进去我们发现
function StateMachine:exportMethods()
self:exportMethods_({
&setupState&,
&isReady&,
&getState&,
&isState&,
&canDoEvent&,
&cannotDoEvent&,
&isFinishedState&,
&doEventForce&,
&doEvent&,
return self.target_
function Component:exportMethods_(methods)
self.exportedMethods_ = methods
local target = self.target_
local com = self
for _, key in ipairs(methods) do
if not target[key] then
local m = com[key]
target[key] = function(__, ...)
return m(com, ...)
return self
他其实是把 &setupState&,
&isReady&,
&getState&,
&isState&,
&canDoEvent&,
&cannotDoEvent&,
&isFinishedState&,
&doEventForce&,
&doEvent&, 这些函数接口提供了给它的绑定对象scenemanager使用,使得它可以像使用自己的接口一样的使用, 如SceneManager里面的接口initEventData的最后一行, self:setupState(cfg) ,跟进这个接口去
function StateMachine:setupState(cfg)
assert(type(cfg) == &table&, &StateMachine:ctor() - invalid config&)
-- cfg.initial allow for a simple string,
-- or an table with { state = &foo&, event = &setup&, defer = true|false }
if type(cfg.initial) == &string& then
self.initial_ = {state = cfg.initial}
self.initial_ = clone(cfg.initial)
self.terminal_ = cfg.terminal or cfg.final
self.events_ = cfg.events or {}
self.callbacks_ = cfg.callbacks or {}
self.map_ = {}
self.current_ = &none&
self.inTransition_ = false
if self.initial_ then
self.initial_.event = self.initial_.event or &startup&
self:addEvent_({name = self.initial_.event, from = &none&, to = self.initial_.state})
for _, event in ipairs(self.events_) do
self:addEvent_(event)
if self.initial_ and not self.initial_.defer then
self:doEvent(self.initial_.event)
return self.target_
它里面是初始化了初始状态,即登陆状态,而且添加了其他的状态扫一扫,访问微社区
后使用快捷导航没有帐号?
签到成功!您今天第{todayrank}个签到,签到排名竞争激烈,记得每天都来签到哦!已连续签到:{constant}天,累计签到:{days}天
Cocos2dx游戏开发笔记18:《跑酷》游戏源码解析
阅读&1657 |
再来看一个比较常见的跑酷游戏其实吧我觉得就是有人用《萝莉快跑》的例子改的不过肯定有新东西学习这是骨头见过最好的demo,不用修改一点直接能运行。先报环境 vs2012 和 cocos2dx 2.2 和 win8.1我知道出新版本了好多人整天盼着新版本但。。。1 用python脚本新建一个demo2&(感谢&blog.csdn.net/haomengzhu 的demo)3 拷贝 Classes 和&Resources 到新demo里4 vs打开新的demo,导入现有项,编译,一次ok==============================================================================================================主角类:Player在H文件里定义了玩家状态的枚举:移动降落死亡[cpp]&还有些基本的数据:骑行动画,漂浮动画,漂浮时间,速度等等。还有很多的 inline 方法 和inline虚方法关于inline引用百科的介绍:[html]&[html]&骨头稍微明白一点了,以后慢慢理解吧。在Player.CPP里,有构造方法,create方法。构造方法主要初始化一些值create方法主要是创建CCSprite这个主要对象,一些动画和参数的设置在 init 方法里。主角滑翔方法,也是简单的播放滑翔动画和修改速度。[cpp]&还有void Player::update (float dt) 更新方法:这里会慢慢的加速到速度最大值,然后匀速。然后根据玩家的状态,来调整速度,是掉落、前进或者跳跃等等。速度相关的,很好理解,但是很乱。[cpp]&=======================================================================地图贴砖类:Block&就是左边这种方块,根据一定的顺序,拼成了游戏中的地图。一般使用地图编辑器,编辑好了导入到游戏中。以前笔记遇到过,骨头还没学到那,暂时一放。Block.h 中,有类型枚举、间隔(?)枚举。定义了贴砖的宽度,高度,贴砖图片,和一些基本的动画,如移动,小时,缩放等。[cpp]&关于这种用法,骨头不是很熟悉,&首先看看 &&CC_SYNTHESIZE(int, _type, Type); 的源码[cpp]&应该是自动生成 set get 方法的一个宏方法。第二个CC_SYNTHESIZE_READONLY(bool, _puffing, Puffing); 方法应该也是自动生成 get 方法的宏方法。这样肯定是会减少代码量,但对于骨头这种新手来说,比较乱容易弄混。使用时必须小心。然后 h文件里还剩下几个 inline 内联方法。[cpp]&再来看下&Block.cpp 文件:[cpp]&在贴砖类的析构方法里,使用了这种表达式的宏定义,去看下源码:[cpp]&关于 表达式的宏定义里使用 do{...}while(0),好像还有讲头:等骨头学习一阵在研究。同样的,构造方法里是初始化一些值,create方法里是实例化 Blocl这个CCSprite对象。初始化街区配置信息&void Block::initBlock()&先初始化不同类型的贴砖CCsprite:[cpp]&然后放到两个数组里:[cpp]&接下来是初始化冒烟动画:[cpp]&最后,创建冒烟动画:&[cpp]&=======================================================================游戏逻辑类 GameLayer.cpp主要有以下方法:1 //创建游戏界面把所有的元素加到界面中包括 主角 地图 &云彩 帽子 积分 各种按钮 等等。&[cpp]&在update里,前景、背景等都是按照主角的速度的一定比例来进行向左移动:[cpp]&update了里。整体流程使用状态机,如果是教程,则隐藏部分元素,停止游戏,显示提示,[cpp]&触摸方法里ccTouchesBegan,也是一系列的状态机判断。包括整体游戏状态,包括玩家游戏中状态,包括游戏引导中德下一步。无它这个例子看得比较艹毕竟跟之前的几个demo大同小异只是地图移动那块比较麻烦单纯阅读代码效果不好需要自己敲敲改改加深印象&最近重玩《上古卷轴5》现在攥钱买雪漫城的房子中差距啊骨头比较喜欢这种高自由度的游戏努力把
作者的其他最新博客
评论 ( 个评论)

我要回帖

更多关于 cocos2dx状态机 的文章

 

随机推荐