Sprite Kit Actions (three)

Recommended for you: Get network issues from WhatsUp Gold. Not end users.
Feel the lifetime have never played so many words...


Scale action
China "movement


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.


Now you have a real zombie and some crazy old lady, but the game is still an important missing - meow star people! Remember this game is to let the game player to collect as many cats to dance together with zombies.


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.


In the game, meow star people won't like moving from right to left as the old lady, but occur randomly in a random position and stay there. In order to prevent their sudden appearance let game player feel awkward, we need to let their scale from 0 to 1. This will make them have a "pop up" effect.


To implement this, add the following new method:


Add the following code to achieve this function:



- (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.


Our random positions on the screen put a cat. Note that we set the X and Y axis scaling is 0, this let the cat size to 0, is not visible.


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.


In this section we through the scaleTo:duration: method to create a cat scaling to normal size action. This action is not reversible, so we also need to create a scaling method to scaling back 0 this sequence let the cat appeared, waiting for a period of time, and then disappeared from the screen, finally removed.


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:


We hope that in the game continued placed the cat, it needs the following generation old lady in the initWithSize: method to add the code:



[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.


The old lady with the generating method is like. Simple operation to create a kitten, then wait for the action sequences of a second, then repeat.


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


We need to know a few other variants of China "movement:


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: and scaleXTo:y:duration:, these methods can be individually adjusted X axis conveniently, Y axis scaling.


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!


According to the "scaleBy:duration:" scaling variants can be current scaling based on China ". For example, if the current ratio is 1, then we call it China" 2 times, it will be scaled to the 2x. again if China "2 times, will be scaled to 4x. note that in this example we can't using this method, because of the 0 China" several times is 0


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


ScaleXBy:y:duration: another by scaling variants, difference is that he can be independent of the scaling of X and Y axis ratio.


Rotate action
Rotary motion


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


These cats should be to attract game player to collect them, but they are now just silly to stay there.


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


Let's add some interesting effects, let them on the screen of the swing back and forth ~ (Harlem Yu: choose me choose me choose me~~)


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:


We need to use the rotary motion to achieve this effect. Call rotateByAngle:duration: and specify the angle of rotation. Replace the code in the spawnCat method with the following code:



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.


Now the cat will swing around the turn, full swing needs 1 seconds, so repeat 10 words, just stay for 10 seconds.


Group action
The action group


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.


Now that we know how to use the sequence to perform an action, but how to make two simultaneous actions implemented? For example, a game in which you want the cat in the swing at the same time some miniature.


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.


In order to solve the song multi task problem, we can use the action group. A little like action sequences. Difference lies in the sequence is a one run, while the group is run at the same time.


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


Try, the contents of spawnCat is replaced by the following code:



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.


Duration of action group and group the longest action the same. So if you add a second action and a 10 second, two action will be executed at the same time, a second after the first act was over, but the action group will continue for 9 seconds, until another action is completed.


Collision detection
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.


We have a zombie, a cat, also have the old lady, but we are not their collision detection mechanism


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.


In the Sprite Kit there are many ways to do collision detection, including the ninth chapter will be referred to the built-in physics engine. In this chapter, we will use the most simple method, boundary detection.


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


There are three basic ideas:


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.


We need to build a in the scene all needed for collision detection of cats and the old lady's index list. The easiest way is to create a name for each node. Then the enumerateChildNodesWithName:usingBlock: method can be used to find a set of node name.


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.
Once we've established the index list can iterate over them to detect collision. Each node contains an frame attribute is used to mark their position on the screen.


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.


If we get the cat, the old lady and zombie frame, we can use the built-in CGRectIntersectsRect method to detect collision.


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:


A try. The first step to each node of a name. To create the old lady in the code in the spawnEnemy method to add the following line of code behind:



enemy.name = @"enemy";



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


Similarly, create a kitten in the code in the spawnCat method to add the following code behind:



cat.name = @"cat";



Then add this new method to the file:


Add the following new method:



- (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.


Here we go through all the known as "cat" or "enemy" the child nodes, and because we know their type, we refer to these nodes into the 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.


Then we check whether a cat or a zombie zombie collided with. If they intersect, they are removed 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:


In addition, we need to solve the problem at the old lady. To know that sprite frame is the whole picture, including the transparent parts.


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!


So this means that when the zombies have transparent region at the upper part of the old lady is regarded as a collision. This is so 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".


In order to solve this, we need through the CGRectInset method to shrink the boundary, but this is still not so perfect, it will handle it. We will be in the tenth chapter of the study to a better way.


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


Add the following code to the update: method at the bottom:



[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!


Compile and run the following, now when contact occurs the cat and the old woman and the zombie will be eaten ~ en... Zombie doomsday is coming~


The Sprite Kit game loop, round 2
SpriteKit game cycle 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.


Here I need to point out a process of collision detection and Sprite Kit game loop and action to interrupt related small problems.


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:


The last time we mentioned in the introduction of Sprite Kit game loop, the first call to the update: method, and then something called Sprite, finally Kit will draw on the screen.


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:


As a result, brings in "other things" in this part of the content and action evaluation:


(Sprite Kit game loop: 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!


This leads to the collision detection mechanism problem. Our collision detection at the end of update:. But Sprite Kit is not to evaluate the operation in the update: before the end of collision detection code. Therefore, we will always behind in a frame.


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::


As you are on top of the legend (sequence), handle the collision detection position better should be the end of the movement assessment in Sprite after Kit, while all the sprite in their new location. So the comment out the update: method the method call.



//[self checkCollisions];



And implement didEvaluateActions as follows:


Then call in the didEvaluateActions method:



-(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.


You may not be aware of the difference between these two ways of drawing frame, after all, the speed is too fast so you can't say that what is before or after - but in some games may be relatively easy to detect, so that appropriate modifications or appropriate.


Note: there are no details about what evaluates actions this phase actually done here, the translator can't do too much to explain, if see related content will be complementary.


Sound action
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!


One is the sound effects in this chapter to the last action learning is one of the most interesting!


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.


Use the playSoundFileNamed:waitForCompletion:action method, using Sprite Kit to play a sound effects need only 1 lines of code.value note is exactly which nodes to play the sound is not important, it is usually by the scene itself to call.


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];:


Before we have the sound resources are added to the project, so you only need to write a few lines of code. In the checkCollisions method, then add a line of code to the [cat removeFromParent]:



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


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


Add a line of code and then to [enemy after 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!


So we'll play corresponding music when different collision. Compile and run, let zombie run up and enjoy the scourge of to anger... (can not help but think of the sentence the Avengers in USA captain with the Hulk said…Smash~)


Sharing actions
Public action


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.


In the last section, you may have noticed in the first play a sound a little pause. This is because in the first play a sound when need caused by the sound system initialization. Here we have to introduce the most powerful one Sprite function in Kit to solve this problem public.


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:


The SKAction object does not keep a certain state, which allows us to do this or that thing... For example for an arbitrary number of nodes and the reuse of some action! For example, we create to move the old lady went like this:



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.


But you for every woman have created such an action. The static variables but we can actually create a private used to store the SKAction, then need to use this action to use it in any.


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."


In fact, we can most actions in the game before reconstruction created. This can save a lot of memory consumption, but that we may not need such performance optimization of this level of the game. Our performance is further discussed in the 25 chapter.


But how does this relate to the sound delay?


But why this will cause 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.


The program will load the sound when you first use an action. So in order to prevent the sound delay, you could create an action and when the right time.


Create the following private variables:


Create the following two private variables:



SKAction *_catCollisionSound; 
SKAction *_enemyCollisionSound;


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


These variables will hold a reference to the instance shared sound.


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:


Now in the code for the initWithSize: method is finally added to create sound effects.:



_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.


These are exactly the same as we created in the checkCollisions method of the action, but now we only need to the scene to create them again. This can make your software loading when initializing scene these sound effects.


Finally, find this line in checkCollisions:


This line of code in checkCollisions finally found:



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



Replace the above line with this one:


The above code replaced:



[self runAction:_catCollisionSound];



Also find this line:


This line of code and find:



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



And replace it with this one:


Be replaced:



[self runAction:_enemyCollisionSound];



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


To compile and run the program again, also won't because play sound change card.


Challenges
Dekaron


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.


To ensure the completion of these challenges! As a SpriteKit development you will have been using actions, so before further learning enough practice is necessary.


Challenge 1: The ActionsCatalog demo
Challenge 1: action directory example


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.


This chapter contains the most important Sprite Kit in action, but does not contain all. To help you understand all available actions, I made a demo, we can know that they in the resource file.


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


Your challenge is to browse every action in demo, and then answer the following questions:


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


Which one is used to allow the sprite to move in accordance with the method of constructing a line


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


What method can ignore the transparency, and the transparency of sprite is set to 50%,


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


What is the custom action? What are the advanced application?


Challenge 2: An invincible zombie
Our challenge 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.


Your challenge is to modify the program, when the zombies and the old lady collision will become a temporary invulnerability, rather than eat the old 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:


Should he kept flashing in the zombie invincible duration. You can use the demo custom blink to achieve. Here are some easy to use code:



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
Challenge 3: train


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


This game is called zombie conga, but now is not a conga line!


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!


Your challenge is to correct this problem. To modify the game, when the collision occurred. The zombies with a cat, not to let the cat away, but let the cat to join the team!


Here are the steps to implement this challenge:


Here are a few steps:


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.


Create a float type in the file at the top of the constant to track the kittens per second to a moving point, is set to 120


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.


The zPosition property is set to 100 which will let zombie zombie display on top of all other sprite. Z numbers represent more physical screen more far, the smaller the number closer, the default is 0


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


When zombies and cat collision, don't put the cat is removed from the screen, but to do so many things:


Set the cat's name to "train" (instead of "cat").


The cat's name from "cat" to"train"

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


Call removeAllActions to action of all the action on the cat stopped.


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


Set the zoom ratio of 1, rotation angle is 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.


Run an action, let cat in 0.2 seconds to turn green. If you don't know what the body, can see the previous 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!


The basic idea is to create a new method called moveTrain. usually this method is to let every cat moved to a cat's position, this creates a conga team effect (zombie snake…)


Use the following template:


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.


You need to calculate a to D value by mathematical methods to create a;, and then create an appropriate action to the e


5. Call moveTrain at the end of update:.


Finally, call the moveTrain method in update: method

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Chester at November 29, 2013 - 12:47 PM