有了上篇博客的基础,我们先来实现一下游戏的主要逻辑,让小车做圆周运动,运动的过程中做碰撞检测,在适当的时候响应用户的触摸事件。我们不打算一下子做完所有的工作,第一步我们先来实现一个圆周运动。如下图所示,小车的运动轨迹包括一个圆周运动一个直线运动,直线运动很简单,但是圆周运动在Cocos中并没有提供,这个时候就需要我们自己来实现一个圆周运动了。

Lua小游戏别撞车——实现圆周运动

我不打算在Lua层实现一个圆周运动,因为某些接口函数没有导出来,如果你可以在Lua层实现一个圆周运动,那么请给我提交一个PR,共享出来给大家学习。我使用的方法是在c++层实现圆周运动,然后导出接口给Lua使用。第一步就是在c++层实现一个圆周运动,具体实现我单独写了一篇文章,大家可以看看(自定义圆周运动),最后的源码请参照更新以后的源码。第二步是使用Lua-binding将c++层自定义的圆周运动导出给Lua使用,具体的导出方法请参照博客——绑定自定义类到Runtime(Lua-binding)。绑定的方法其实很简单,主要是明白绑定的原理,只要你成功导出一次,以后导出10分钟就搞定了。如果以上的倆步你都不会操作,没有关系,我把自己导出的桥接函数和圆周运动的源代码都上传了网盘,大家可以下载使用。具体的使用方法同样参照绑定Lua的文章,如果你新建工程没有选择显示c++层代码的选项,可以在IDE中如下操作,这样在工程的frameworks目录下就显示出c++层代码了。

Lua小游戏别撞车——实现圆周运动

好了,现在万事俱备只欠代码了。首先新建一个MainGameScene.lua文件,这个Lua文件作为我们游戏主场景的模块文件。第一步写一个主场景的模块,参照上篇博客这个应该不难吧。

require("Cocos2d")
require("Cocos2dConstants")

local MainGameScene = class("MainGameScene",function()
    return cc.Scene:create()
end
)

function MainGameScene:ctor()
    self.size = cc.Director:getInstance():getWinSize()
end

function MainGameScene:create()
    --new函数的作用是调用了构造函数ctor,然后将表FlashScene的所有元素复制到了一张新的表中,最后返回这张表
    local mainGame = MainGameScene:new()
    local layer = mainGame:createLayer()
    mainGame:addChild(layer)

    return mainGame
end

return MainGameScene

上边的代码是形式,接下来我们在createLayer函数中写游戏的逻辑。

function MainGameScene:createLayer()
    local layer = cc.Layer:create()

    --添加背景图片
    self:addBg(layer)

    --运行游戏主逻辑
    self:mainLogic(layer)

    return layer
end

createLayer函数很简单,创建了一个layer,然后返回这个layer,中间的代码实现的功能分别封装到了不同的函数中去完成。首先是添加背景图片,addBg函数如下。

--添加背景图片
function MainGameScene:addBg(layer)
    --bg
    local bg = cc.Sprite:create("bg.png")
    bg:setPosition(self.size.width/2,self.size.height/2)
    layer:addChild(bg)

    --bg2
    local bg2 = cc.Sprite:create("bg2.png")
    bg2:setPosition(self.size.width/2,self.size.height/2)
    layer:addChild(bg2)
end

代码很简单不用多说,然后是mainLogic函数,在这个函数中我们需要添加小车运行轨道的背景图片,然后将红色和黄色的俩辆小车分别添加到轨道上,最后让他们运动起来。

--主逻辑
function MainGameScene:mainLogic(layer)
    --添加主逻辑的背景图片,也就是小车运行的轨道
    local track = cc.Sprite:create("track.png")
    track:setPosition(self.size.width/2,self.size.height/2)
    layer:addChild(track)

    --轨道长宽
    self.track_size = track:getContentSize()

    --创建一个红色小车
    local car_red = cc.Sprite:create("car.png")
    car_red:setPosition(self.track_size.width*0.45,self.track_size.height*0.063)
    car_red:setTag(1)
    self.car_bottomy = car_red:getPositionY()
    self.car_topy = self.track_size.height*0.95

    --创建一个黄色小车
    local car_yello = cc.Sprite:create("car2.png")
    car_yello:setPosition(car_red:getPositionX()+car_red:getContentSize().width,self.track_size.height*0.063)
    car_yello:setTag(2)

    --将车添加到轨道上
    track:addChild(car_red)
    track:addChild(car_yello)
    --让小车动起来
    self:runCar(car_red)
    self:runCar(car_yello)

end

这里将轨道的大小作为本模块的一个成员保存了起来,因为接下来在其他的函数中我们会使用这个变量,同时我们也保存了上下俩条轨道的Y坐标,这里涉及到的坐标全部使用的是相对于轨道背景的大小,所以最后将小车添加到了轨道上,而不是层上。最后来看一下小车是如何运动的吧。

--小车运动起来
function MainGameScene:runCar(car)
    --小车运动的时间
    local tm = 1

    --创建第一个动作
    local circle1 = tt.CircleBy:create(tm,{x=0,y=self.track_size.height*0.445},180)
    local circle2 = tt.CircleBy:create(tm,{x=0,y=-self.track_size.height*0.445},180)
    local action1 = cc.Sequence:create(
        cc.MoveTo:create(tm,cc.p(self.track_size.width*0.245,self.car_bottomy)),
        cc.Spawn:create(circle1,cc.RotateBy:create(tm,180)),
        cc.MoveTo:create(tm,cc.p(self.track_size.width*0.745,self.car_topy)),
        cc.Spawn:create(circle2,cc.RotateBy:create(tm,180))
    )
    local action_red = cc.RepeatForever:create(action1)
    action_red:setTag(1)

    --创建第二个动作
    local action2 = cc.Sequence:create(
        cc.MoveTo:create(tm,cc.p(self.track_size.width*0.745,self.car_bottomy)),
        cc.Spawn:create(circle1:reverse(),cc.RotateBy:create(tm,-180)),
        cc.MoveTo:create(tm,cc.p(self.track_size.width*0.245,self.car_topy)),
        cc.Spawn:create(circle2:reverse(),cc.RotateBy:create(tm,-180))
    )
    local action_yello = cc.RepeatForever:create(action2)
    action_yello:setTag(2)

    --如果存在正在执行的动作,先停止动作的执行
    if car:getActionByTag(1) ~= nil then
        car:stopActionByTag(1)
    end
    --满足条件执行动作1
    if car:getTag() == 1 then
        car:runAction(action_red)
    end

    --如果正在执行动作2,停止正在执行的动作
    if car:getActionByTag(2) ~= nil then
        car:stopActionByTag(2)
    end
    if car:getTag() == 2 then
        car:runAction(action_yello)
    end
end

红色小车和黄色小车的运行轨迹不同,所以创建了俩个动作系列。这里主要说明一下圆周运动,圆周运动是我自己导出的,我起了一个模块名叫做tt,所以我要访问这个接口的时候使用的就是tt模块名。使用create创建圆周运动的时候第一个参数是时间,第二个参数是圆心的位置,这个位置是相对于当前执行动作的节点的位置,是相对的,不是绝对的,最后一个参数是做圆周运动一共转过的度数,这里是180。创建第二个动作的时候因为是和小车1的动作相反,所以使用了reverse函数,最后不断的repeat,这样小车会不断的运行下去。我们在创建小车和动作的时候还给他们设置了tag,这个是为了方便以后对他们的操作。这样,整个代码就写完了,本篇博客我们简单的使用c++层导出的接口来让小车运动了起来,其他的逻辑并没有实现。在整个模块中,我把每一个功能都写到了各自不同的函数中,这样整个代码看起来会比较清晰,最后的源代码大家到github上下载吧。