红色小车和黄色小车的逻辑都已经完成了,本篇博客来做碰撞检测,当红色小车来不及躲避的时候发生碰撞游戏结束,那么如何判断俩辆小车碰撞了呢,我们可以通过俩辆小车的位置来确定,比如当他们的x和y坐标相同的时候就是发生了碰撞,再精确一点就是y坐标相同,x坐标的距离是俩辆小车的宽度之和的一半。甚么时候代表俩辆小车同向运动错开行驶了呢,y坐标有一个差值,x坐标和上边的一样。实际在运用的时候发现在lua中比较数字是否相同是不太好的,因为lua中只有浮点类型,转化为整形可以使用floor或者ceil函数,不过我采用的方法是先判断是否在同一个轨道,在前几篇的博客中当小车变轨的时候我设置了小车的switch变量,通过这个变量的真假来判断小车是在内圈还是外圈。这样y方向的判断我们就用这个变量,而x方向的判断我用的就是数字,判断俩车的距离。

在运用黄色小车的switch变量的时候,发现有bug,所以我们先来修正这个bug,让黄色小车的switch跟随他的内外圈运动而变,修正后的代码如下,是在上一篇博客的基础上改的。

function MainGameScene:runYelloCar(car)

    --返回执行里圈轨道或者外圈轨道的动作
    local function getAction()
        --初始化运动过程中需要的成员变量 代表外圈的轨道
        local y1 = self.track_size.height*0.445
        local y2 = -y1
        local point1 = cc.p(self.track_size.width*0.245,self.car_bottomy)
        local point2 = cc.p(self.track_size.width*0.745,self.car_topy)

        --判断执行里圈的轨道还是外圈的轨道
        local switch = self.yello_switch
        --代表里圈的轨道
        if switch then
            y1 = self.track_size.height*0.335
            y2 = -y1
            point1 = cc.p(self.track_size.width*0.245,self.track_size.height*0.171)
            point2 = cc.p(self.track_size.width*0.745,self.track_size.height*0.844)
        end

        --[[根据速率计算小车需要运动的时间--]]
        local rate = GlobalData.rate
        --圆周运动的时间
        local tm = math.pi*y1/rate
        --直线运动的时间
        local tm2 = (self.track_size.width*0.5)/rate

        --创建动作
        local circle1 = tt.CircleBy:create(tm,{x=0,y=y1},180)
        local spawn1 = cc.Spawn:create(circle1:reverse(),cc.RotateBy:create(tm,-180))
        local move2 = cc.MoveTo:create(tm2,{x=point1.x,y=point2.y})
        local circle2 = tt.CircleBy:create(tm,{x=0,y=y2},180)
        local spawn2 = cc.Spawn:create(circle2:reverse(),cc.RotateBy:create(tm,-180))
        local move1 = cc.MoveTo:create(tm2,{x=point2.x,y=point1.y})

        return tm,tm2,spawn1,move2,spawn2,move1
    end

    local tm,tm2,spawn1,move2,spawn2,move1 = getAction()

    --先将是否变轨的变量保存一下
    local switch= self.yello_switch

    --获得一个随机数
    math.randomseed(os.time())
    local r = math.random(0,100)
    --只有当小车在外部的轨道,并且满足概率的时候才会进入内部的轨道
    if r > 50 and (self.yello_switch == false) then
        --变轨固定在这个点 进入内部的轨道
        local circle1 = cc.Spawn:create(tt.CircleBy:create(tm/2,{x=-30,y=self.track_size.height*0.4},-90),cc.RotateBy:create(tm/2,-90))
        local circle2 = cc.Spawn:create(tt.CircleBy:create(tm/2,{x=-162,y=self.track_size.height*0.0015},-90),cc.RotateBy:create(tm/2,-90))
        --修正bug,增加一个动作,当小车运行完四分之一的轨迹以后就设置代表内外圈轨道的变量switch
        spawn1 = cc.Sequence:create(
        circle1,cc.CallFunc:create(function()self.yello_switch = true end),
        circle2
        )
        --代表里圈轨道
        self.yello_switch = true
        tm,tm2,_,move2,spawn2,_ = getAction()
    end

    r = math.random(0,100)
    if r > 50 and (self.yello_switch == true) then
        --变轨固定在这个点 转到外部的轨道
        local circle1 = cc.Spawn:create(tt.CircleBy:create(tm/2,{x=-25,y=-self.track_size.height*0.4},-90),cc.RotateBy:create(tm/2,-90))
        local circle2 = cc.Spawn:create(tt.CircleBy:create(tm/2,{x=225,y=self.track_size.height*0.02},-90),cc.RotateBy:create(tm/2,-90))
        spawn2 = cc.Sequence:create(
        circle1,cc.CallFunc:create(function()self.yello_switch = false end),
        circle2
        )
        --代表外圈轨道
        self.yello_switch = false
    end

    --执行完毕一圈以后重新执行动作,这样轨道就是随机的
    local callfunc = cc.CallFunc:create(function()
        self:runYelloCar(self.car_yello)
    end)

    --动作序列
    local action_yello = cc.Sequence:create(move1,spawn1,move2,spawn2,callfunc)
    action_yello:setTag(2)

    --使用玩switch来获得运动的轨迹以后,将原始switch还给小车
    self.yello_switch = switch
    car:runAction(action_yello)
end

接下来就完成碰撞检测的逻辑,代码注释写的很清楚了,大家看注释吧!

--碰撞检测
function MainGameScene:collision(layer)
    --是否播放yes的声音
    local isYes = false

    local collistion_update = function()
        --获得俩辆小车的位置,宽度
        local red_x,red_y = self.car_red:getPosition()
        local yello_x,yello_y = self.car_yello:getPosition()
        local red_width = self.car_red:getContentSize().width
        local yello_width = self.car_yello:getContentSize().width
        --碰撞检测的条件
        if self.red_switch == self.yello_switch and math.abs(red_x-yello_x)<(red_width+yello_width)/2 then
            --停止小车运动,停止游戏
            self.car_red:stopAllActions()
            self.car_yello:stopAllActions()
            self.play = false
            --播放声音
            SoundDeal:playEffect(EffectType.Crash)
            cc.Director:getInstance():getScheduler():unscheduleScriptEntry(self.schedule_update)
            self:gameOver(layer)
        --没有发生碰撞检测俩车错过去,记录一下
        elseif self.red_switch ~= self.yello_switch and math.abs(red_x-yello_x)<(red_width+yello_width)/2 then
            --满足播放yes的条件
            isYes = true
        --在俩车相错的这段时间如果任然没有发生碰撞检测则真的是错过去了
        elseif math.abs(red_x-yello_x)>(red_width+yello_width)/2 and isYes then
            isYes = false
            --设置全局的分数数据
            GlobalData:setScore()
            --改变UI
            self.score:setString(GlobalData.score)
            self.score:setScale(0.1)
            self.score:runAction(cc.EaseElasticOut:create(cc.ScaleTo:create(0.2,1)))
        end
    end
    --每帧都进行碰撞检测
    self.schedule_update = cc.Director:getInstance():getScheduler():scheduleScriptFunc(collistion_update,0,false)
end

接下来就是调用了,在调用之前完成一个开始按钮,当用户点击了开始按钮的时候小车运动,在这个过程中不断的进行碰撞检测。

function MainGameScene:setButton(imageName,layer)
    local play = cc.MenuItemImage:create(imageName,"","")
    play:setPosition(self.size.width/2,self.size.height/2)
    play:setScale(0.8)
    local menu = cc.Menu:create(play)
    menu:setPosition(cc.p(0,0))
    layer:addChild(menu,3)

    --callback
    local play_callback = function()
        --播放音效
        SoundDeal:playEffect(EffectType.Start)
        play:runAction(cc.Sequence:create(cc.ScaleTo:create(0.05,1.3),cc.RemoveSelf:create()))
        self:runRedCar(self.car_red)
        self:runYelloCar(self.car_yello)
        self.play = true
        self:collision(layer)
    end
    play:registerScriptTapHandler(play_callback)
    --判断游戏是否开始的变量
    self.play = false
end

代码中涉及到的对声音和数据的处理先不用管,下篇博客再添加。上边的代码有一个新的变量是play,这个变量是用来控制游戏是否在进行的,如果游戏没有开始,用户的触摸事件也是无效的,所以我们需要在触摸处理的回调函数中对这个变量进行判断,什么位置添加我想大家都应该很清楚吧。

游戏结束以后的处理正如代码中看到的那样,调用了一个gameOver函数,下面是这个函数的实现,涉及到数据和声音的部分不用管。

function MainGameScene:gameOver(layer)
    self:setButton("retry.png",layer)
    --清除数据
    GlobalData:reset()
    self.score:setString("0")
    --重新设置小车的位置
    self:resetCar()
end

Lua小游戏别撞车——碰撞检测