sprite Kit Actions(三)

来源:互联网 发布:网络综合布线收费准则 编辑:程序博客网 时间:2024/06/06 03:08

感觉一辈子都没打过这么多字了...


Scale action

缩放动作


You now have an animated zombie and some crazy cat ladies, but the game is missing one very important element – cats! Remember that the purpose of the game is for the player to gather as many cats as he can into the zombie’s conga line.


现在你有了一个可以动的僵尸和一些疯老太太,但是这个游戏仍然有一个重要的缺失 — 喵星人!要记得这个游戏是让玩家收集尽可能多的猫来跟僵尸一起跳舞.


In Zombie Conga, the cats won’t move from right to left like the cat ladies do – instead, they will appear at a random location on the screen and stay stationary. Rather than have them appear instantly, which would be jarring, you’ll have them start at a scale of 0 and grow to a scale of 1 over time. This will make the cats appear to “pop in” to the game.


在游戏中,喵星人不会像老太太一样从右往左移动,而是随机出现在一个随机的位置然后待在那里.为了防止他们突然出现而让玩家感到突兀,我们需要让他们的比例从0到1进行缩放.这会让它们有一种”弹出”式的效果.


To implement this, add the following new method:


添加下列代码来实现该功能:


- (void)spawnCat {    // 1    SKSpriteNode *cat = [SKSpriteNode spriteNodeWithImageNamed:@"cat"];    cat.position = CGPointMake(ScalarRandomRange(0, self.size.width),             ScalarRandomRange(0, self.size.height));    cat.xScale = 0;    cat.yScale = 0;     [self addChild:cat];    // 2    SKAction *appear = [SKAction scaleTo:1.0 duration:0.5];     SKAction *wait = [SKAction waitForDuration:10.0];    SKAction *disappear = [SKAction scaleTo:0.0 duration:0.5];     SKAction *removeFromParent = [SKAction removeFromParent];     [cat runAction:[SKAction sequence:@[appear, wait, disappear, removeFromParent]]];}


1. You create a cat at a random spot on the screen. Note that you set the xScale

and yScale to 0, which makes the cat 0 size – effectively invisible.


我们在屏幕上的随机位置放了一只猫.注意我们设置x和y轴的缩放比例都是0,这让猫的大小变成了0,也就是不可见状态.


2. You create an action to scale the cat up to normal size by calling the scaleTo:duration: constructor. This action is not reversible, so you also create a similar action to scale the cat back down to 0. The sequence is the cat appears, waits for a bit, disappears, and is then removed from the parent.


这一段我们通过scaleTo:duration:方法来创建了一个把猫缩放到正常大小的动作.这个动作是不可逆的,所以我们还需要创建一个缩放方法来把它缩放回0.这个序列让猫出现,等待一段时间,然后消失,最后从屏幕移除.


You want the cats to spawn continuously from the start of the game, so add the following inside initWithSize:, just after the line that spawns the enemies:


我们希望在游戏中持续的放置小猫,那就需要在initWithSize:方法生成老太太的代码下面添加:


[self runAction:[SKAction repeatActionForever: [SKAction sequence:@[[SKAction                performSelector:@selector(spawnCat) onTarget:self],                [SKAction waitForDuration:1.0]]]]];


This is very similar to how you spawned the enemies. You simply run a sequence that calls spawnCat, waits for one second and then repeats.


这跟生成老太太的方法非常相似.简单的运行一个创建小猫,然后等待一秒的动作序列,然后重复执行.


You should be aware of a few other variants of the scale action:


我们需要知道缩放动作的其他几个变种:


scaleXTo:duration:, scaleYTo:duration:, and scaleXTo:y:duration:: These allow you to scale just the x-axis or y-axis of a node independently, which you can use to stretch or squash a node.


scaleXTo:duration:,scaleYTo:duration:和scaleXTo:y:duration:,这些方法可以方便的单独调整x轴,y轴的缩放比例.


scaleBy:duration:: The “by” variant of scaling, which multiples the passed-in scale by the current node’s scale. For example, if the current scale of a node is 1.0, and you scale it by 2.0, it is now at 2x. If you scale it by 2.0 again, it is now at 4x. Note that you could not use scaleBy:duration: in the previous example, because anything multiplied by 0 is still 0!


scaleBy:duration:这个”按倍缩放”的变种可以基于当前的缩放比例来进行缩放.举个例子,如果当前比例是1.0,然后我们把它缩放2.0倍,那么就会被缩放到2x.如果再次缩放2.0倍,就会被缩放为4x.需要注意的是在这个例子里我们不能用这个方法,因为0缩放几倍都是0.


• scaleXBy:y:duration:: Another “by” variant, but this one allows you to scale x and y independently.


scaleXBy:y:duration:另一个按倍缩放的变种,区别在于他可以独立的缩放x和y轴比例.


Rotate action

旋转动作


The cats in this game should be appealing to the player to try to pick up, but right now they’re just sitting motionless.


这些猫应该是要吸引玩家来收集他们的,但是他们现在只是傻傻的待在那里.


Let’s give them some charm by making them wiggle back and forth while they sit.


让我们来添加一些有意思的效果,让他们在屏幕上的时候来回的摇摆~(哈林:选我选我选我~~)


To do that, you’ll need the rotate action. To use it, you call the rotateByAngle:duration: constructor, passing in the angle (in radians) by which to rotate. Replace the list of actions in spawnCat with the following:


我们需要用到旋转动作来实现这一效果.调用rotateByAngle:duration:并指定要旋转的角度.用下列代码来替换spawnCat方法中的代码:


cat.zRotation = -M_PI / 16;SKAction *appear = [SKAction scaleTo:1.0 duration:0.5];SKAction *leftWiggle = [SKAction rotateByAngle:M_PI / 8 duration:0.5]; SKAction *rightWiggle = [leftWiggle reversedAction];SKAction *fullWiggle =[SKAction sequence: @[leftWiggle, rightWiggle]];SKAction *wiggleWait = [SKAction repeatAction:fullWiggle count:10];//SKAction *wait = [SKAction waitForDuration:10.0];SKAction *disappear = [SKAction scaleTo:0.0 duration:0.5]; SKAction *removeFromParent = [SKAction removeFromParent]; [cat runAction:[SKAction sequence:@[appear, , disappear, removeFromParent]]];


Now the cat has wiggled left and right and is back to its start position. This “full wiggle” takes one second total, so in wiggleWait you repeat this 10 times to have a 10-second wiggle duration.


现在小猫会左右摇摆了,依次完整的摆动需要耗时1秒钟,所以重复10词,正好停留10秒钟.


Group action

动作组


So far you know how to run actions one after another in sequence, but what if you want to run two actions at the exact same time? For example, in Zombie Conga you want to make the cat wiggle and scale up and down slightly as he’s wiggling.


现在我们知道了如何利用序列来执行一个一个的动作,但是怎么让两个动作同时执行?举个例子,游戏中你希望猫在摇摆的同时进行些微缩放.


For this sort of multitasking, you can use something called the group action. It works in a similar way to the sequence action, where you pass in a list of actions. However, instead of running them one at a time, a group action runs them all at once.


为了解决之歌多任务问题,我们可以使用动作组.有点类似动作序列.区别在于序列是一个一个运行,而组是同时运行.


Let’s try this out. Replace the list of actions in spawnCat with the following:


试一下,把spawnCat中的内容替换成下面的代码:


SKAction *appear = [SKAction scaleTo:1.0 duration:0.5];SKAction *leftWiggle = [SKAction rotateByAngle:M_PI / 8 duration:0.5]; SKAction *rightWiggle = [leftWiggle reversedAction];SKAction *fullWiggle =[SKAction sequence: @[leftWiggle, rightWiggle]];//SKAction *wait = [SKAction waitForDuration:10.0];//SKAction *wiggleWait =// [SKAction repeatAction:fullWiggle count:10];SKAction *scaleUp = [SKAction scaleBy:1.2 duration:0.25];SKAction *scaleDown = [scaleUp reversedAction];SKAction *fullScale = [SKAction sequence:@[scaleUp, scaleDown, scaleUp, scaleDown]];SKAction *group = [SKAction group:@[fullScale, fullWiggle]];SKAction *groupWait = [SKAction repeatAction:group count:10];SKAction *disappear = [SKAction scaleTo:0.0 duration:0.5]; SKAction *removeFromParent = [SKAction removeFromParent]; [cat runAction:[SKAction sequence:@[appear, , disappear, removeFromParent]]];


The duration of a group action is equal to the longest duration of any of the actions it contains. So if you add one action that takes one second, and another that takes 10 seconds, both actions will begin to run at the same time, and after one second the first action will be complete. The group action will continue to execute for nine more seconds until the other action is complete.


动作组的持续时间与组中最长的动作的持续时间相同.所以如果你添加了一个1秒的动作和一个10秒的动作,两个动作会同时执行,1秒之后第一个动作就结束了,但是动作组会继续执行9秒,直到另一个动作完成.


Collision detection

碰撞检测


You’ve got a zombie, you’ve got cats, you’ve even got crazy cat ladies – but what you don’t have is a way to detect when they collide.


我们有了僵尸,有了猫,也有了老太太,但是我们没有检测他们碰撞的机制


There are multiple ways to detect collisions in Sprite Kit, including using the built-in physics engine, as you’ll learn in Chapter 9, “Intermediate Physics”. In this chapter, you’ll take the simplest and easiest approach: bounding box collision detection.


在Sprite Kit中有很多种办法来做碰撞检测,包括第9章将要讲到的内置的物理引擎.在这一章我们将会使用最简单的实现方法,边界检测.


There are three basic ideas you’ll use to implement this:


这里有三个基本思路:


You need a way of getting all of the cats and crazy cat ladies in a scene into lists so that you can check for collisions one-by-one. An easy way to do this is to give nodes a name when you create them. Then you can use the enumerateChildNodesWithName:usingBlock: method on the scene to find all of the nodes with a certain name.


我们需要建立一个在场景中所有需要进行碰撞检测的猫和老太太的索引列表.最简单的办法是在创建的时候给每个节点一个名字.然后就可以通过enumerateChildNodesWithName:usingBlock:方法来找到一个拥有既定名字的节点.


2. Once you have the lists of cats and cat ladies, you can loop through them to check for collisions. Each node has a frame property that gives you a rectangle representing where the node is onscreen.

一旦我们建立了这个索引列表就可以循环遍历他们来进行碰撞检测了.每一个节点都包含一个frame属性用来标记他们在屏幕上的位置.


3. If you have the frame for either a cat lady or a cat, and the frame for the zombie, you can use the built-in method CGRectIntersectsRect to see if they collide.


如果我们得到了猫,老太太和僵尸的frame,我们就可以使用内建的CGRectIntersectsRect方法来进行碰撞检测.


Let’s give this a shot. First you need to set the name for each node. Inside spawnEnemy, right after creating the enemy sprite, add this line:


一起来试一下.第一步给每一个节点一个名称.在spawnEnemy方法中创建老太太的代码后边添加下面这行代码:


enemy.name = @"enemy";


Similarly, inside spawnCat, right after creating the cat sprite, add this line:


同样的 ,在spawnCat方法中创建小猫的代码后边添加下边的代码:


cat.name = @"cat";


Then add this new method to the file:


添加下面这个新的方法:


- (void)checkCollisions {    [self enumerateChildNodesWithName:@“cat" usingBlock:^(SKNode *node, BOOL *stop){        SKSpriteNode *cat = (SKSpriteNode *)node;        if (CGRectIntersectsRect(cat.frame, _zombie.frame))         {            [cat removeFromParent];         }    }];    [self enumerateChildNodesWithName:@“enemy" usingBlock:^(SKNode *node, BOOL *stop){        SKSpriteNode *enemy = (SKSpriteNode *)node;        CGRect smallerFrame = CGRectInset(enemy.frame, 20, 20);         if (CGRectIntersectsRect(smallerFrame, _zombie.frame))         {            [enemy removeFromParent];         }    }]; }


Here you enumerate through any child of the scene that has the name “cat” or “enemy” and cast it to an SKSpriteNode, since you know it is a sprite node if it has that name.


这里我们遍历了所有叫做”cat”或者”enemy”的子节点,同时由于我们知道他们的类型,我们把这些节点转换成了SKSpriteNode.


You then check if the frame of the cat or enemy intersects with the frame of the zombie. If there is an intersection, you simply remove the cat or enemy from the scene.


然后我们检查了猫或者僵尸是否与僵尸相撞.如果他们相交了,就把他们从场景里面移除.


Also, notice that you do a little trick for the cat lady. Remember that the frame of a sprite is the entire image of the sprite, including transparent space:


另外,我们需要解决这个老太太的一点问题.要知道sprite的frame是整个图片,包括了透明的部分.


So that means that transparent space at the top of the cat lady image would “count” as a hit if the zombie went into that area. Totally unfair!


所以这意味着当僵尸碰到了老太太上部的透明区域时也会视为发生碰撞.这太不公平了!


To resolve this, you shrink the bounding box a little bit by using the CGRectInset method. It’s still not perfect, but it’s a start. You’ll learn a better way to do this in Chapter 10, “Advanced Physics”.


为了解决这个,我们需要通过CGRectInset方法来收缩边界,但是这仍然不是那么完善,姑且这么处理吧.我们在第10章的时候将会学习到更好的办法.


Add the following call to this method at the end of update::


添加下面的代码到update:方法的底部:


[self checkCollisions];


Build and run, and now when you collide with the cats or enemies they disappear from the scene. It’s your first small step toward the zombie apocalypse!


编译运行以下,现在当猫和老太太与僵尸发生接触的时候就会被吃掉了~恩...僵尸末日就要到了~


The Sprite Kit game loop, round 2

SpriteKit游戏循环2


There’s a slight problem with the way you’re doing the collision detection here that I should point out, which is related to how Sprite Kit’s game loop and actions interrelate.


这里我需要指出一个处理碰撞检测时与Sprite Kit游戏循环和动作打断相关的的小问题.


The last time you saw the Sprite Kit game loop, you saw that the update: method gets called, then some “other stuff” occurs, and finally Sprite Kit renders the screen:


上一次我们在介绍Sprite Kit的游戏循环时提到,首先调用update:方法,然后一些其他的东西被调用,最后Sprite Kit才会绘制屏幕.


Well, it turns out that one of the things in the “other stuff” section is evaluating the actions that you’ve been learning about in this chapter:


于是乎,引出了在”其他东西”这部分中的一些内容 — 动作评估:


(Sprite Kit游戏循环:update: —> SKScene evaluates actions —> -didEvaluateActions —> other stuff —> SpriteKit renders screen)


This leads to the problem with the way you’re currently doing collision detection. You check for collisions at the end of the update: loop, but Sprite Kit doesn’t evaluate the actions until after this update: loop. Therefore, your collision detection code is always one frame behind!


这就引出了目前碰撞检测机制中的问题.我们在update:结束的时候进行碰撞检测.但是Sprite Kit在update:结束之前是不会对动作进行评估的.因此,我们的碰撞检测代码永远会落后一帧.


As you can see in the updated event loop diagram, a much better place to perform the collision detection would be after Sprite Kit evaluates the actions and all the sprites are in their new spots. So comment out the call at the end of update::


就像你在上面的图例(序列),更好的处理碰撞检测的位置应该在Sprite Kit对动作评估结束之后,同时所有的sprite都在他们的新位置之后.所以注释掉update:方法最后的方法调用.


//[self checkCollisions];


And implement didEvaluateActions as follows:


然后在didEvaluateActions方法中重新调用:


-(void)didEvaluateActions {    [self checkCollisions];}


You probably won’t notice much difference in this case because the frame rate is so fast that it is hard to tell it was behind – but in some games this may be more noticable so it’s good to do things properly.


你可能并不能察觉到这两种方式的区别,毕竟绘制帧的速度实在太快了所以你没办法说看出究竟实在之前还是之后 — 但是在有些游戏中可能就会比较容易察觉到,所以先这样适当的修改还是适当的.


注:书中没有细讲关于evaluates actions这个阶段究竟做了什么,译者在这里也无法做过多解释,如果之后看到相关的内容会进行相关补充.


Sound action

音效动作


The last type of action you’ll learn about in this chapter also happens to be one of the most fun – the one that plays sound effects!


本章要学习的最后一个动作也是最有意思的一个就是声音效果!


Using the playSoundFileNamed:waitForCompletion: action, playing a sound effect with Sprite Kit takes just one line of code. Note that the node on which you run this action doesn’t matter, so typically you’ll just run it as an action on the scene itself.


使用playSoundFileNamed:waitForCompletion:action方法,使用Sprite Kit播放声音特效只需要1行代码.值得注意的是究竟是用哪个节点来播放声音并不重要,所以通常来说都是由场景本身来调用的.


You’ve already added the sounds to your project earlier, so you just need to write the code. Inside checkCollisions, add the following line just after [cat removeFromParent];:


之前我们已经把声音资源添加到了项目中,所以只需要写几行代码.在checkCollisions方法中,添加一行代码到[cat removeFromParent]之后:


[self runAction:[SKAction playSoundFileNamed:@"hitCat.wav" waitForCompletion:NO]];


Then add this line just after [enemy removeFromParent];:


然后添加一行代码到[enemy removeFromParent];之后:


[self runAction:[SKAction playSoundFileNamed:@"hitCatLady.wav" waitForCompletion:NO]];


Here you play the appropriate sound action for each type of collision. Build and run, move the zombie around and enjoy the sounds of the smash-up!


这样我们就会在不同碰撞的时候播放对应的音乐.编译运行,让僵尸跑起来然后享受天灾的怒吼把…(不禁想起了复仇者联盟中美国队长跟绿巨人说的那句话…Smash~)


Sharing actions

公用动作


In the previous section, you may have noticed a slight pause the first time the sound plays. This can occur when the sound system is initialized the first time it is used. The solution to this problem also demonstrates one of the most powerful features of Sprite Kits actions: sharing.


在上一节中,你可能注意到了在第一次播放声音的时候会有一点点停顿.这是因为在第一次播放声音时需要对声音系统初始化造成的.这里我们要介绍一个Sprite Kit中最强大的功能来解决这个问题 — 公用.


The SKAction object does not actually maintain any state itself, and that allows you to do something cool – reuse actions on any number of nodes simultaneously! For example, the action you create to move the cat ladies across the screen looks something like this:


SKAction对象并不会自己保持某一种状态,这就让我们可以做一些这样或者那样的事了...比方说让任意数量的节点同时重用一些动作!举个例子,我们创建的用来移动老太太的动作是这样的:


SKAction *actionMove = [SKAction moveToX:-enemy.size.width/2 duration:2.0];


But you create this action for every cat lady. Instead, you could create a private or static SKAction variable, store this action in it, and then use that variable wherever you are currently using actionMove.


但是你为每一个老太太都创建了这样一个动作.但是我们实际上可以创建一个私有的静态变量用来储存这个SKAction,然后在任意需要使用这个动作的时候使用它.


In fact, you could modify Zombie Conga so it reuses most of the actions you’ve created so far. Doing so would reduce the amount of memory your system uses, but that’s a performance improvement you probably don’t need to make in such a simple game. You’ll learn more about things like this in Chapter 25, “Performance.”


实际上,我们可以重构游戏中之前创建的大部分动作.这样可以节省大量的内存消耗,但是这可能这种级别的游戏中我们并不需要用到这样的性能优化.我们在25章中将进一步探讨关于性能的问题.


But how does this relate to the sound delay?


但是为什么这会造成音效延迟?


The application is loading the sound the first time you create an action that uses it. So to prevent the sound delay, you can create the actions in advance and then use them when necessary.


程序会在你第一次使用一个动作的时候载入音效.所以为了防止音效延迟,你可以预先创建一个动作然后当合适的时候使用.


Create the following private variables:


创建以下两个私有变量:


SKAction *_catCollisionSound; SKAction *_enemyCollisionSound;


These variables will hold shared instances of the sound actions you want to run.


这些变量将会持有共享的音效实例的引用.


Now create the sound actions by adding the following lines at the end of initWithSize:, just after the line that runs the action that calls spawnCat:


现在在initWithSize:方法的最后添加创建声音特效的代码:


_catCollisionSound = [SKAction playSoundFileNamed:@"hitCat.wav" waitForCompletion:NO];_enemyCollisionSound =[SKAction playSoundFileNamed:@“hitCatLady.wav" waitForCompletion:NO];


These are the same actions you create in checkCollisions, but now you create them just once for the scene. That means your app will load these sounds as soon as the scene is initialized.


这些是与我们在checkCollisions方法中创建的动作完全一样,但是现在我们只需要为这个场景创建他们一次.这就可以让你的软件在初始化场景的时候加载这些音效.


Finally, find this line in checkCollisions:


最后找到checkCollisions中的这行代码:


[self runAction:[SKAction playSoundFileNamed:@"hitCat.wav" waitForCompletion:NO]];


Replace the above line with this one:


把上边的代码替换成为:


[self runAction:_catCollisionSound];


Also find this line:


然后找到这行代码:


[self runAction:[SKAction playSoundFileNamed:@"hitCatLady.wav" waitForCompletion:NO]];


And replace it with this one:


替换成为:


[self runAction:_enemyCollisionSound];


Build and run again. You should no longer experience any pauses before the sound effects play.


重新编译运行,程序再也不会因为播放音效而变卡了.


Challenges

挑战


Be sure to do these challenges. As a Sprite Kit developer you will be using actions all the time, so it’s important to get some practice with them before moving further.


确保完成这些挑战!作为一个SpriteKit开发这你将会一直在使用actions,所以再深入学习之前进行足够的练习是必要的.


Challenge 1: The ActionsCatalog demo

挑战1:动作目录示例


This chapter covers the most important actions in Sprite Kit, but it doesn’t cover all of them. To help you get a good understanding of all the actions that are available to you, I’ve created a little demo called ActionsCatalog, which you can find in the resources for this chapter.


这一章包含了Sprite Kit中最重要的动作,但是并没有包含全部.为了帮助你更好的了解所有可用的actions,我做了一个demo,可以在资源文件中知道他们.


Your challenge is to flip through each of these demos, then take a look at the code to answer the following questions:


你的挑战是浏览demo中的每一个动作,然后回答下列问题:


What action constructor would you use to make a sprite follow a certain pre-defined path?


使用哪一个构造方法来让sprite按照一个既定路线移动


2. What action constructor would you use to make a sprite 50% transparent, regardless of what its current transparency settings are?


使用哪个构造方法可以忽略当前的透明度,而把sprite的透明度设置为50%,


3. What are “custom actions” and how do they work at a high level?


自定义动作是什么?有什么高级应用?


Challenge 2: An invincible zombie

挑战2:无敌僵尸


Your challenge is to modify the game to do just this. When the zombie collides with a cat lady, he should become temporarily invincible instead of destroying the cat lady.


你的挑战是修改程序,当僵尸与老太太发生碰撞的时候会变成一个临时的无敌状态,而不是吃掉这个老太太.


While the zombie is invincible, he should blink. To do this, you can use the custom blink action that is included in ActionsCatalog. Here’s the code for your convenience:


在僵尸的无敌持续时间内他应该保持闪烁.你可以用demo中的自定义闪烁动作来实现.这里有些方便使用的代码:


float blinkTimes = 10; float blinkDuration = 3.0; SKAction *blinkAction = [SKAction customActionWithDuration:blinkDuration actionBlock:    ^(SKNode *node, CGFloat elapsedTime) {    float slice = blinkDuration / blinkTimes;     float remainder = fmodf(elapsedTime, slice);     node.hidden = remainder > slice / 2;}];


Challenge 3: The conga train

挑战3:拍火车


This game is called Zombie Conga, but there’s no conga line to be seen just yet!


这个游戏叫做僵尸康茄舞,但是现在并没有一个康茄舞的队伍!


Your challenge is to fix that. You’ll modify the game so that when the zombie collides with a cat, instead of disappearing, the cat joins your conga line!


你的挑战就是修正这一问题.修改游戏,当僵尸与一只猫发生了碰撞,不是让猫消失,而是让猫加入这个队伍!


Here are the steps to implement this challenge:


这里有几个实现步骤:


1.Create a constant float variable to keep track of the cat’s move points per second at the top of the file. Set it to 120.0.


在文件顶部创建一个float类型的常量来追踪小猫们每秒要移动的点,设置为120.0


2. Set the zombie’s zPosition to 100. This makes the zombie appear on top of the other sprites. Larger z values are “out of the screen” and smaller values are “into the screen”, and the default value is 0.


设置僵尸的zPosition属性为100.这会让僵尸显示在所有其他sprite的上面.越大的z数字代表的理屏幕越远,数字越小则越近,默认为0.


3. When the zombie collides with a cat, don’t remove the cat from the scene. Instead, do the following:


当僵尸与猫发生碰撞时,不要把猫从屏幕上移除,而是做这么几件事:


Set the cat’s name to “train” (instead of “cat”).


把猫的名字由”cat”改为”train”


b. Stop all actions currently running on the cat by calling removeAllActions. 


调用removeAllActions把所有作用于这只猫的动作停止.


c. Set the scale to 1 and rotation of the cat to 0.


设置缩放比例为1,旋转角度为0.


d. Run an action to make the cat turn green over 0.2 seconds. If you’re not sure what action to use for this, check out ActionsCatalog.


运行一个动作,让cat在0.2秒的时间内变为绿色.如果不知道用身什么,可以参见之前的demo.


4. Make a new method called moveTrain. The basic idea for this method is that every so often, you make each cat move toward where the previous cat currently is. This creates a conga line effect!


创建一个新的方法叫做moveTrain.通常这个方法的基本想法是让每一只猫移动到上一只猫的位置,这就创建了一个康茄舞队伍的效果(僵尸贪吃蛇…)


Use the following template:


使用下面的模板:


-(void)moveTrain {    __block CGPoint targetPosition = _zombie.position;     [self enumerateChildNodesWithName:@“train" usingBlock:^(SKNode *node, BOOL *stop)        {            if (!node.hasActions)             {                float actionDuration = 0.3;                 CGPoint offset = // a                CGPoint direction = // b                CGPoint amountToMovePerSec = // c                 CGPoint amountToMove = // d                 SKAction *moveAction = // e                [node runAction:moveAction];             }            targetPosition = node.position;         }];}


You need to fill in a through d by using the math utility functions you created last chapter, and e by creating the appropriate actions.


你需要通过上一张创建的数学方法计算给a到d的值,然后创建一个适当的动作给e


5. Call moveTrain at the end of update:.


在update:方法的最后调用moveTrain方法


原创粉丝点击