(三)Lua小游戏别撞车——小车变换轨道

发表于2015-06-27
评论0 1.2k浏览

如果小车是在做直线运动变轨非常容易,只是y坐标的不同而已,但是如果小车做的是圆周运动这个变轨就不那么容易了。当然,你可以有多种方法来解决这个问题,我这里使用的是数学计算的方法,大家看一下如下的示意图。当小车做圆周运动变轨的时候,相对于圆心的夹角是不变的,我们可以在小车是外圈轨道的时候计算出这个夹角,然后根据半径乘以这个角度分别得到x和y方向的坐标,然后重新设置小车到这个坐标处,而小车Rotate的角度却不需要改变,这样变轨就完成了。虽然数学上是这么个意思,但是做起来你就会感觉不是很容易了,因为细节涉及到很多,稍不留神就达不到效果。首先内圈和外圈的圆周运动半径都是确定的,为了获得夹角,我们需要算x方向的距离或者是y方向的距离,这个长度等于小车现在的y坐标和圆心之间的距离。计算出了角度,然后和新的半径相乘得到新的x和y方向的坐标,这个坐标是相对的,需要加上圆心的位置。整体的思路就是这样,接下来看一下代码。

Lua小游戏别撞车——小车变换轨道

1--主逻辑
2function MainGameScene:mainLogic(layer)
3    --添加主逻辑的背景图片,也就是小车运行的轨道
4    local track = cc.Sprite:create("track.png")
5    track:setPosition(self.size.width/2,self.size.height/2)
6    layer:addChild(track)
7 
8    --轨道长宽
9    self.track_size = track:getContentSize()
10 
11    --创建一个红色小车
12    local car_red = cc.Sprite:create("car.png")
13    car_red:setPosition(self.track_size.width*0.45,self.track_size.height*0.063)
14    car_red:setTag(1)
15    self.car_bottomy = car_red:getPositionY()
16    self.car_topy = self.track_size.height*0.95
17    self.car_red = car_red
18 
19    --创建一个黄色小车
20    local car_yello = cc.Sprite:create("car2.png")
21    car_yello:setRotation(180)
22    car_yello:setPosition(car_red:getPositionX()+car_red:getContentSize().width,self.track_size.height*0.063)
23    car_yello:setTag(2)
24    self.car_yello = car_yello
25 
26    --将车添加到轨道上
27    track:addChild(car_red)
28    track:addChild(car_yello)
29 
30    --纪录小车是否变轨的变量
31    self.red_switch = false
32    self.yello_switch = false
33 
34    --让小车动起来
35    self:runRedCar(car_red)
36    self:runYelloCar(car_yello)
37end

以上是mainLogic函数,和之前的相比多了控制小车运动在内圈还是外圈的变量switch,红色小车和黄色小车的运动逻辑是不一样的,所以我写在了俩个函数中,然后让他们分别执行自己的动作逻辑。

1--小车运动
2function MainGameScene:runRedCar(car)
3 
4    --根据小车所在位置的不同,返回不同的动作序列
5    local function getSequence(spawn1,spawn2,move1,move2)
6        --设置动作执行的顺序
7        local sequence
8        --根据小车所在位置的不同,执行不同的动作序列
9        local x = car:getPositionX()
10        --lua中只有浮点数,不能用相等来判断位置是否一样
11        if math.abs(x-self.track_size.width*0.245) < 0.01 and car:getPositionY() < self.track_size.height*0.5 then
12            sequence = {spawn1,move2,spawn2,move1}
13        elseif math.abs(x-self.track_size.width*0.745) < 0.01 and car:getPositionY() > self.track_size.height*0.5 then
14            sequence = {spawn2,move1,spawn1,move2}
15        elseif car:getPositionY() > self.track_size.height*0.5 then
16            sequence = {move2,spawn2,move1,spawn1}
17        else
18            sequence = {move1,spawn1,move2,spawn2}
19        end
20        return sequence
21    end
22 
23    --设置在内圈轨道上执行动作还是在外圈轨道上执行动作
24    local function getParam()
25        --初始化运动过程中需要的成员变量 代表外圈的轨道
26        local y1 = self.track_size.height*0.445
27        local y2 = -y1
28        local point1 = cc.p(self.track_size.width*0.245,self.car_bottomy)
29        local point2 = cc.p(self.track_size.width*0.745,self.car_topy)
30 
31        --决定执行里圈的轨道还是外圈的轨道
32        local switch = self.red_switch
33        --代表里圈的轨道
34        if switch then
35            y1 = self.track_size.height*0.335
36            y2 = -y1
37            point1 = cc.p(self.track_size.width*0.245,self.track_size.height*0.171)
38            point2 = cc.p(self.track_size.width*0.745,self.track_size.height*0.844)
39        end
40 
41        --将本次圆周运动的半径保存下来,变轨的时候会用到
42        self._radius = y1
43 
44        return y1,y2,point1,point2
45    end
46 
47    --根据内外圈轨道的不同,获得不同的运动参数
48    local y1,y2,point1,point2 = getParam()
49 
50    --根据速率计算小车需要运动的时间
51    local rate = math.pi*self.track_size.height*0.3
52    --圆周运动的时间
53    local tm = math.pi*self._radius/rate
54    --直线运动的时间
55    local tm2 = (self.track_size.width*0.5)/rate
56 
57    --创建动作
58    local circle1 = tt.CircleBy:create(tm,{x=0,y=y1},180)
59    local spawn1 = cc.Spawn:create(circle1,cc.RotateBy:create(tm,180))
60    --右圆周运动
61    local circle2 = tt.CircleBy:create(tm,{x=0,y=y2},180)
62    local spawn2 = cc.Spawn:create(circle2,cc.RotateBy:create(tm,180))
63    --下边的直线运动
64    local move1 = cc.MoveTo:create(tm2,point1)
65    --上边的直线运动
66    local move2 = cc.MoveTo:create(tm2,point2)
67    --动作序列
68    local action_red = cc.RepeatForever:create(cc.Sequence:create(getSequence(spawn1,spawn2,move1,move2)))
69    action_red:setTag(1)
70 
71    --执行红车的动作
72    if car:getTag() == 1 then
73        --如果存在正在执行的动作,先停止动作的执行
74        if car:getActionByTag(1) ~= nil then
75            car:stopActionByTag(1)
76        end
77        car:runAction(action_red)
78    end
79end

上边的代码是红色小车的运动逻辑,小车有可能运动在里圈轨道上,也有可能运动在外圈的轨道上,所以不同的轨道上执行动作的参数是不同的,比如半径,时间,直线运动的y坐标。所以首先通过switch来判断是在内圈轨道上运动还是在外圈的轨道上运动,然后返回相应的参数。小车在不同路段的运动时间会根据路径的长度来设置,也就是最后小车运动的速率是一样的,这样以后通过改变速率可以来改变游戏的难度。小车做一个完整的动作序列是一整圈,包括底下的直线运动,左半圆周运动,上边的直线运动,右半圆周运动,小车变轨的逻辑是这样的,在变轨位置处重新设置小车的坐标,然后重新设置剩余的运动轨迹,执行变轨以后的运动轨迹。举个例子来说,如果现在小车在左半圆轴运动处变轨,那么它上边的直线运动和右半圆周运动以及下直线运动都要进行调整,然后执行这个调整之后的动作序列。当小车完成了一圈的运动以后,从新执行runRedCar()函数,这个时候小车的动作哪个前哪个后就要根据位置来判断了,所以就有了getSequence函数。上边我说明了整个变轨的逻辑,下面写一下变轨的代码。

1function MainGameScene:createLayer()
2    local layer = cc.Layer:create()
3 
4    --添加背景图片
5    self:addBg(layer)
6 
7    --运行游戏主逻辑
8    self:mainLogic(layer)
9 
10    --实现与用户的交互
11    self:userInteraction(layer)
12 
13    return layer
14end
15 
16--用户交互
17function MainGameScene:userInteraction(layer)
18 
19    --触摸处理函数
20    local function onTouchBegan()
21        self:switchTrace(self.car_red)
22        return false
23    end
24 
25    --注册触摸
26    local touch_listener = cc.EventListenerTouchOneByOne:create()
27    touch_listener:registernoxssHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN )
28    local eventDispatcher = layer:getEventDispatcher()
29    eventDispatcher:addEventListenerWithSceneGraphPriority(touch_listener,layer)
30end

上面的代码很简单,实现了用户的触摸事件,在回调函数onTouchBegan中调用了变轨函数switchTrace,以下是switchTrace涉及到的代码。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引