如果小车是在做直线运动变轨非常容易,只是y坐标的不同而已,但是如果小车做的是圆周运动这个变轨就不那么容易了。当然,你可以有多种方法来解决这个问题,我这里使用的是数学计算的方法,大家看一下如下的示意图。当小车做圆周运动变轨的时候,相对于圆心的夹角是不变的,我们可以在小车是外圈轨道的时候计算出这个夹角,然后根据半径乘以这个角度分别得到x和y方向的坐标,然后重新设置小车到这个坐标处,而小车Rotate的角度却不需要改变,这样变轨就完成了。虽然数学上是这么个意思,但是做起来你就会感觉不是很容易了,因为细节涉及到很多,稍不留神就达不到效果。首先内圈和外圈的圆周运动半径都是确定的,为了获得夹角,我们需要算x方向的距离或者是y方向的距离,这个长度等于小车现在的y坐标和圆心之间的距离。计算出了角度,然后和新的半径相乘得到新的x和y方向的坐标,这个坐标是相对的,需要加上圆心的位置。整体的思路就是这样,接下来看一下代码。
2 | function MainGameScene:mainLogic(layer) |
4 | local track = cc.Sprite:create( "track.png" ) |
5 | track:setPosition(self.size.width/2,self.size.height/2) |
9 | self.track_size = track:getContentSize() |
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) |
15 | self.car_bottomy = car_red:getPositionY() |
16 | self.car_topy = self.track_size.height*0.95 |
17 | self.car_red = car_red |
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) |
24 | self.car_yello = car_yello |
27 | track:addChild(car_red) |
28 | track:addChild(car_yello) |
31 | self.red_switch = false |
32 | self.yello_switch = false |
35 | self:runRedCar(car_red) |
36 | self:runYelloCar(car_yello) |
以上是mainLogic函数,和之前的相比多了控制小车运动在内圈还是外圈的变量switch,红色小车和黄色小车的运动逻辑是不一样的,所以我写在了俩个函数中,然后让他们分别执行自己的动作逻辑。
2 | function MainGameScene:runRedCar(car) |
5 | local function getSequence(spawn1,spawn2,move1,move2) |
9 | local x = car:getPositionX() |
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} |
18 | sequence = {move1,spawn1,move2,spawn2} |
24 | local function getParam() |
26 | local y1 = self.track_size.height*0.445 |
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) |
32 | local switch = self.red_switch |
35 | y1 = self.track_size.height*0.335 |
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) |
44 | return y1,y2,point1,point2 |
48 | local y1,y2,point1,point2 = getParam() |
51 | local rate = math.pi *self.track_size.height*0.3 |
53 | local tm = math.pi *self._radius/rate |
55 | local tm2 = (self.track_size.width*0.5)/rate |
58 | local circle1 = tt.CircleBy:create(tm,{x=0,y=y1},180) |
59 | local spawn1 = cc.Spawn:create(circle1,cc.RotateBy:create(tm,180)) |
61 | local circle2 = tt.CircleBy:create(tm,{x=0,y=y2},180) |
62 | local spawn2 = cc.Spawn:create(circle2,cc.RotateBy:create(tm,180)) |
64 | local move1 = cc.MoveTo:create(tm2,point1) |
66 | local move2 = cc.MoveTo:create(tm2,point2) |
68 | local action_red = cc.RepeatForever:create(cc.Sequence:create(getSequence(spawn1,spawn2,move1,move2))) |
72 | if car:getTag() == 1 then |
74 | if car:getActionByTag(1) ~= nil then |
75 | car:stopActionByTag(1) |
77 | car:runAction(action_red) |
上边的代码是红色小车的运动逻辑,小车有可能运动在里圈轨道上,也有可能运动在外圈的轨道上,所以不同的轨道上执行动作的参数是不同的,比如半径,时间,直线运动的y坐标。所以首先通过switch来判断是在内圈轨道上运动还是在外圈的轨道上运动,然后返回相应的参数。小车在不同路段的运动时间会根据路径的长度来设置,也就是最后小车运动的速率是一样的,这样以后通过改变速率可以来改变游戏的难度。小车做一个完整的动作序列是一整圈,包括底下的直线运动,左半圆周运动,上边的直线运动,右半圆周运动,小车变轨的逻辑是这样的,在变轨位置处重新设置小车的坐标,然后重新设置剩余的运动轨迹,执行变轨以后的运动轨迹。举个例子来说,如果现在小车在左半圆轴运动处变轨,那么它上边的直线运动和右半圆周运动以及下直线运动都要进行调整,然后执行这个调整之后的动作序列。当小车完成了一圈的运动以后,从新执行runRedCar()函数,这个时候小车的动作哪个前哪个后就要根据位置来判断了,所以就有了getSequence函数。上边我说明了整个变轨的逻辑,下面写一下变轨的代码。
1 | function MainGameScene:createLayer() |
2 | local layer = cc.Layer:create() |
11 | self:userInteraction(layer) |
17 | function MainGameScene:userInteraction(layer) |
20 | local function onTouchBegan() |
21 | self:switchTrace(self.car_red) |
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) |
上面的代码很简单,实现了用户的触摸事件,在回调函数onTouchBegan中调用了变轨函数switchTrace,以下是switchTrace涉及到的代码。