Quick-Cocos2d-x初学者游戏教程8

上章载入 TiledMap 背景后,接下来的这章我们将开始引入物理引擎相关的东西,并且会开始创建我们的游戏角色。游戏地图中各类障碍物和奖励品的创建则会留到下一章。

构建物理世界

首先,物理引擎是干什么的应该不用我说吧?好吧,还是说一下(百度的):物理引擎通过为刚性物体赋予真实的物理属性的方式来计算运动、旋转和碰撞反映。所以用它来模拟真实世界的飞行、掉落等功能是具有得天独厚的优势的,这也是为什么我们的游戏要使用它的原因。

然后,我们要怎样使用物理引擎啦?在之前的 Cocos2d-x 2.x 中,游戏需要选择直接调用 Box2D 或 Chipmunk 的 API 来处理逻辑,这种方法比较复杂,需要开发人员对物理引擎和 Cocos2d-x 都非常了解才能把两者融合得很好。而在现在的 Cocos2d-x 3.x(Quick 3.x同样)版本中封装了一个全新的 Physics 接口,这个接口将 Chipmunk(默认使用 Chipmunk 作为内部物理引擎)和 Box2D 封装到引擎内部,使开发者的上层调用变得更加友好、简单。

物理引擎属较高阶一点的内容,关于它的一些基础概念,比如:刚体、形状、密度、世界等等,可查找 Chipmunk 和 Box2D 相关文档进行脑补。本教程只针对需要使用的API做一定的了解。

封装了 Physics 接口后,物理世界被融入到 Scene 中,即当创建一个场景时,可以指定这个场景是否使用物理引擎。

回到我们的游戏,我们一开始就已经认定了 GameScene 是我们的主游戏场景,所以,它需要是一个使用物理引擎的游戏场景。所以,打开 GameScene 文件,我们需要在创建它的地方把它修改为带物理世界的 scene:

display.newPhysicsScene("GameScene")

之后,你可在 ctor 方法中加入如下的一段代码:

self.world = self:getPhysicsWorld()
self.world:setGravity(cc.p(0, -98.0))
self.world:setDebugDrawMask(cc.PhysicsWorld.DEBUGDRAW_ALL)

其中,self:getPhysicsWorld()用来获取场景绑定的物理世界对象。
获取的 self.world 默认是有带重力的,大小为(0.0f, -98.0f), 但我们也可以通过的setGravity()方法来改变重力大小。
setDebugDrawMask 方法是在调试物理世界时使用的,该方法可以开启调试模,能把物理世界中不可见的body,shape,joint等元素可视化(如下图所示)。当调试结束时,需要把该功能关闭。

bug

创建游戏主角

在第六章中我们曾说过,本游戏教程将创建一个飞行的娃娃角色,所以接下来我们一起来创建一个带物理特性的游戏角色。

在开始之前,我们先在 src/app 中创建一个文件夹 objects 来存放接下来要创建的游戏对象。

接着,新建一个 lua 文件并命名为 Player,然后把他存放入 objects 文件夹中,下面稍稍定义下这个类:

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

function Player:ctor()
end

return Player

添加物理特性

接下来我们来给游戏主角添加物理特性。

引擎封装了 Physics 接口后,Node 就自带了 body 属性,也就是每个 Sprite 都自带了 body 属性。所以我们要创建一个可受重力作用的 Sprite 是非常容易的,下面我们在 Player 的 ctor 中加入如下的一段代码就可以为它绑定一个 body:

local body = cc.PhysicsBody:createBox(self:getContentSize(), cc.PHYSICSBODY_MATERIAL_DEFAULT, cc.p(0,0))
self:setPhysicsBody(body)

这里调用cc.PhysicsBody::createBox()方法创建了一个矩形的 body,createBox 方法有三个参数,分别是:

  • 参数1为 cc.size 类型,它表示矩形 body 的尺寸大小。
  • 参数2为 cc.PhysicsMaterial 类型,表示物理材质的属性,默认情况下为 cc.PHYSICSBODY_MATERIAL_DEFAULT。 该参数也可自定义,方法如下:
    cc.PhysicsMaterial(density, restitution, friction)

    • density:表示密度
    • restitution:表示反弹力
    • friction:表示摩擦力
  • 参数3为 cc.p 类型,它也是一个可选参数,表示 body 与中心点的偏移量,默认下为cc.p(0,0)

与 createBox 方法类似的还有 createCircle(radius, material, offset),该方法可以创建一个圆形的 body,除第一个参数为半径外,其余两参数与 createBox 方法一样。

body是有很多属性的,当我们有需要时,再调用对应的方法。

添加动画

我们的游戏主角有各种不同的游戏状态(飞行、下落、死亡),所以接下来我们就来实现它的这部分功能。

这里我们通过帧动画实现游戏角色的运动,帧动画的原理是将连续的帧图像(如下图)在渲染的时候通过逐帧或插值的方式播放出来而形成的动态效果。就像翻动小人书一样。

fly

引擎中,帧动画的具体内容是依靠精灵显示出来的,为了显示动态图片,我们需要不停切换精灵显示的内容,通过把静态的精灵变为动画播放器从而实现动画效果。帧动画由帧组成,每一帧都是一个纹理,我们可以使用一个纹理序列来创建动画。

Quick 框架中,我们可以这样来播放一个动画:

-- 创建一个包含flying1.png到flying4.png的4个图像帧对象的数组
local frames = display.newFrames("flying%d.png", 1, 4)
-- 以包含图像帧的数组创建一个动画 Animation 对象
local animation = display.newAnimation(frames, 0.3 / 4)
-- 在显示对象上循环播放动画,并返回 Action 动画动作对象。
transition.playAnimationForever(self, animation)

因为我们的游戏对象有不只一种的动画,所以在本教程中,我们可以先把这些动画都添加到动画缓存,这样在需要播放相应的动画的时候,我们就可以从缓存中直接获取动画来播放了,而不用再浪费时间重新创建动画。

所以,请在 Player.lua 文件中添加如下的一段函数:

function Player:addAnimationCache()
local animations = {"flying", "drop", "die"}
local animationFrameNum = {4, 3, 4}

for i = 1, #animations do
-- 1
local frames = display.newFrames( animations[i] .. "%d.png", 1, animationFrameNum[i])
-- 2
local animation = display.newAnimation(frames, 0.3 / 4)
-- 3
display.setAnimationCache(animations[i], animation)
end
end

animations,animationFrameNum分别表示角色的三种动画和三种动画分别有的帧总数。

遍历animations时,下面一一解释下函数的作用:

  1. 创建一个包含animations[i]1.png到animations[i]animationFrameNum[i].png的图像帧对象的数组,如i = 1,就是创建一个包含flying1.png到flying4.png的图像帧对象的数组。其中..是字符串连接操作符,它可以用来连接两个字符串。当其中一个为其它类型时,它会把该类型也转为字符串。
  2. 以包含图像帧的数组创建一个动画 Animation 对象,参数 0.3 / 4 表示 0.3 秒播放完 4 桢。
  3. 将2中创建好的 animation 对象以指定的名称(animations[i])加入到动画缓存中,以便后续反复使用。也就是我们在 AnimationCache 中可以通过animations = {"flying", "drop", "die"}这三种动画的名称来查找制定的 animation 对象。

把动画载入缓存后,我们就可以写下对应的函数去执行动画了。如下:

function Player:flying()
transition.stopTarget(self)
transition.playAnimationForever(self, display.getAnimationCache("flying"))
end

function Player:drop()
transition.stopTarget(self)
transition.playAnimationForever(self, display.getAnimationCache("drop"))
end

function Player:die()
transition.stopTarget(self)
transition.playAnimationForever(self, display.getAnimationCache("die"))
end

最后在创建 Player 对象后,我们就可以调用以上相应的方法来播放指定的动画了。

转载请注明出自:http://shannn.com/archives/422

标签: none

?>