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

上章我们创建了游戏角色,这一章,我们需要把它添加到场景,并创建其他的障碍物和奖励品。

将游戏角色加入场景

本游戏将游戏主角加入游戏场景时,我们要实现如下的一个效果:就是在场景中创建一个游戏主角Player,先把它放在屏幕以外,进入游戏场景的一开始,我们将在屏幕左边以外的地方创建一个游戏主角 Player,然后让它飞入到场景的指定位置,在这段飞的过程中,它不受重力的影响(就不会从天上掉落),且我们让背景不滚动,等到主角到了它该到的地方时我们再让它受重力影响向下落,并且让背景滚动起来。

这章我们要实现的效果如下图所示:

res

PS:gif图本身有卡顿,且角色的动画资源帧数太少(美术太忙,不给做),所以角色飞的很奇怪,下落的也很奇怪,望见谅。

根据以上需求,下面我们就开始动手把把游戏角色加入场景。

首先打开 GameScene.lua 文件,在文件开头加入如下的代码载入 Player 文件:

local Player = require("app.objects.Player")

接着在 ctor 方法中创建一个 Player 对象。

self.player = Player.new()
self.player:setPosition(-20, display.height * 2 / 3)
self:addChild(self.player)
self:playerFlyToScene()

这里 self:playerFlyToScene 方法将使创建的 Player 对象移动到场景指定位置,其定义如下:

function GameScene:playerFlyToScene()

local function startDrop()
self.player:getPhysicsBody():setGravityEnable(true)
self.player:drop()
self.backgroundLayer:startGame()
end

local animation = display.getAnimationCache("flying")
transition.playAnimationForever(self.player, animation)

local action = transition.sequence({
cc.MoveTo:create(4, cc.p(display.cx, display.height * 2 / 3)),
cc.CallFunc:create(startDrop)
})
self.player:runAction(action)
end

在这个函数中,我们先从动画缓存中取得了我们需要的动画,然后让 self.player 主角不停的播放这个动画;接着又创建了一个动作(action)让 self.player 执行。

transition.sequence() 方法能创建一个 Sequence 动作序列对象,Sequence 类型的对象能使一个 Node 顺序执行一批动作,第四章中我们也提到过这个对象。回到我们的函数来看,这里创建了一个先执行 MoveTo 动作,等 MoveTo 动作执行完后再执行 CallFunc 动作的对象。通俗一点说它的作用就是,先移动到屏幕的(display.cx, display.height * 2 / 3)点,在调用 startDrop 方法。

MoveTo 动作应该不难理解,因为第四章我们也讲过它的兄弟 MoveBy。MoveBy 动作能使节点从当前坐标点匀速直线运动到相对偏移了一定向量的位置上。而 MoveTo 则能使节点从当前坐标点匀速直线运动到指定的位置坐标上。它们的移动位置一个是相对的,一个是绝对的,这也是Cocos 引擎中所有以 To,By 为后缀的动作的主要区别。

CallFunc 动作是个函数回调动作,它用来在动作中进行方法的调用,个人觉得它是非常有必要存在的一个动作。因为很多时候,当某些动作完成后,需要执行一些数据或逻辑上的处理(比如,一个敌人播放完死亡动画后需要把它移除并清理内存),这时使用函数回调动作 CallFunc 就是再好不过的。

startDrop 是个闭包函数,在该函数中,将开启主角受重力影响的开关,并且让游戏背景滚动起来。

由于之前我们已经直接让背景滚动起来了,所以这里需要做一些修改,即把更新相关的函数移动到 startGame 方法中。如下,在 BackgroundLayer 中添加 startGame 方法:

function BackgroundLayer:startGame()

self:addNodeEventListener(cc.NODE_ENTER_FRAME_EVENT, handler(self, self.scrollBackgrounds))
self:scheduleUpdate()
end

这样,我们的游戏角色就可以在场景中跑起来了。

当然,要实现我们的目标这里还需要把物理世界的重力设置下,之前设置的是(0,0),所以不管这样物理世界中的刚体都相当于没受到重力影响一样,现在我们把它改设为(0,-98)(默认的就是这么大),这样刚体才能受到重力。

在给 Player 添加物理特性时,我们还需添加如下的代码来设置它。

self:getPhysicsBody():setGravityEnable(false)

setGravityEnable 方法可以屏蔽物理世界中的刚体受到重力的影响。

创建奖励品

接下来我们来创建游戏中的奖励品,也就是心心。

我们新建一个 lua 文件,把它命名为 Heart 并保存到 objects 文件夹中,下面是该类的定义:

local Heart = class("Heart", function()
return display.newSprite("image/heart.png")
end)

local MATERIAL_DEFAULT = cc.PhysicsMaterial(0.0, 0.0, 0.0)

function Heart:ctor(x, y)

local heartBody = cc.PhysicsBody:createCircle(self:getContentSize().width / 2,
MATERIAL_DEFAULT)

self:setPhysicsBody(heartBody)
self:getPhysicsBody():setGravityEnable(false)

self:setPosition(x, y)
end

return Heart

根据我们前面章节的讲解,这个类现在所涉及到的所有知识点大家应该都很容易理解吧,所以我就不再多说了。只需要注意的一点是:这里我们在创建刚体时把它的密度,反弹力、摩擦力都设为0是为了在碰撞的时候不发生任何物理形变。

接下来我们来把心心加入游戏场景。

将奖励品载入场景

因为游戏中的奖励品/障碍物都是在地图上,所以我们回到 BackgroundLayer 文件,在这里把心心加入背景。

在 BackgroundLayer 中添加如下的函数:

function BackgroundLayer:addHeart()
local objects = self.map:getObjectGroup("heart"):getObjects() -- 1
-- 2
local dict = nil
local i = 0
local len = table.getn(objects)
-- 3
for i = 0, len-1, 1 do
dict = objects[i + 1]
-- 4
if dict == nil then
break
end
-- 5
local key = "x"
local x = dict["x"]
key = "y"
local y = dict["y"]
-- 6
local coinSprite1 = Heart.new(x, y)
self.map:addChild(coinSprite1)
end
end

addHeart方法的作用是遍历 TiledMap 中的 heart 对象层,取得该对象层中所有对象的坐标,并在该坐标上创建一个个心心对象。下面我们具体的讲解下代码,当然,可能结合下图要更容易理解点。

object

  1. getObjectGroup 方法从地图中获取到指定的对象层(也就是个 ObjectGroup 对象组对象),对象组 ObjectGroup 中包含了多个对象,所以我们可以通过 getObjects 方法从 ObjectGroup 中获得所有的对象。objects 在这里就相当于一个存放了多个对象的数组。
  2. dict 是个临时变量,用它来存储单个的对象;table.getn 方法能得到数组的长度。
  3. 遍历 objects 数组。
  4. 如果对象 dict 为空,则跳出 for 循环。
  5. 取出相应的属性值,即对象坐标。因为对象组中的对象在 TMX 文件中是以键值对的形式存在的,所以我们可以通过它的 key 得到相应的 value。
  6. 在获取到的坐标上创建 Heart 对象,并把它添加到 TiledMap 背景层上。这样创建的心心才能跟随着背景层的滚动而滚动。

之后,在程序中调用 addHeart,我们就可以创建出一系列的奖励品了。

添加物理世界边界

现在,我们的场景里已经有角色,有心心,此时运行游戏你会发现,当角色可以受重力影响之后,它会一直往下掉落,直至落出屏幕之外,显然这是不行的,所以我们必须在要在物理场景中添加一些能阻止刚体下落或飞出屏幕的边界。

所以,我们在 BackgroundLayer 的 ctor 方法中加入了如下的一段代码:

local width = self.map:getContentSize().width
local height1 = self.map:getContentSize().height * 9 / 10
local height2 = self.map:getContentSize().height * 3 / 16

local sky = display.newNode()
local bodyTop = cc.PhysicsBody:createEdgeSegment(cc.p(0, height1), cc.p(width, height1))
sky:setPhysicsBody(bodyTop)
self:addChild(sky)

local ground = display.newNode()
local bodyBottom = cc.PhysicsBody:createEdgeSegment(cc.p(0, height2), cc.p(width, height2))
ground:setPhysicsBody(bodyBottom)
self:addChild(ground)

这里我们创建了两根和 TiledMap 背景一样长的边界线。
createEdgeSegment 方法能创建一个不受重力约束的自由线条,它有四个参数,分别表示:

  • 参数1为 cc.p 类型,表示线条的起点;
  • 参数2也为 cc.p 类型,表示线条的终点;
  • 参数3为 cc.PhysicsMaterial 类型,表示物理材质的属性,默认情况下为 cc.PHYSICSBODY_MATERIAL_DEFAULT;
  • 参数4为 number 类型,表示线条粗细。

与之类似的函数还有:createEdgeBox,createEdgePolygon,createEdgeChain。它们都能创建不受重力约束的边界。具体的参数可跳转到它们的定义查看,这里我就不多说了。

资源已更新(plist文件重新打过包),请点击下载

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

标签: none

?>