Sprite Kit tutorial: make a general procedure 2

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

Note 1: This paper from Sprite Kit Tutorial: Making a Universal App: Part 2


Catalog


In the last article, create a basic game program: some lovely mole jump out from inside the hole. And in order to be able to let the program run well in iPhone 3.5 inches, 4 inches of iPhone, iPad and iPad Retina, also spent a lot of space to introduce the UI design and coordinate related knowledge.

This paper will give the mole, add some cute Animation: laugh and beaten when they face, and add a game: can earn points by tapping the mole, will also add some sound effects.

The definition of animation: feasibility

In order to make the game more interesting, we will be in the game to give the mole added two animation. First of all, when the mole jumped out from the hole when laughter is the animation, and then, when you beat them, is a beat expression.

Before starting, let us have a look at the code inside the feasibility of definition of animation.

The mole laughs animated images needed and relevant in this order: mole_laugh1.png, mole_laugh2.png mole_laugh3.png, mole_laugh2.png, mole_laugh3.png, mole_laugh1.png.

We can by hard coding way to configure our animation, as shown in the following code:

1
2
3
4
5
6
7
8
9
[animFrames addObject:
    [SKTexture textureWithImageNamed:@"mole_laugh1.png"]];
[animFrames addObject:
    [SKTexture textureWithImageNamed:@"mole_laugh2.png"]];
[animFrames addObject:
    [SKTexture textureWithImageNamed:@"mole_laugh3.png"]];
[animFrames addObject:
    [SKTexture textureWithImageNamed:@"mole_laugh2.png"]];
// And so on...

However, it is easy for our code. For a simple, here we do not have the above code to the definition of animation, but the use of the attribute list to replace.

The attribute list

If you have not used the attribute list, also never mind. The attribute list is a special file, Xcode can be used to create the file, in accordance with certain type that contains an array, dictionary, strings and numbers, so it is very easy to create, and can conveniently read these in the middle of the codevalue.

Below we try in Xcode. Right click ShackAMole, select "New File..." , then select "iOS\Resource\Property List", then click "Next". The file named "laughAnim.plist", then click the create. Now you can see the laughAnim.plist visual editor in Xcode, as shown below:

SouthSouth

Each attribute list has a root element. This is an array or a dictionary. In this paper we create, let the mole laughs required animation name will contain all the pictures, is an array, so click on the root element column (second Type, the current is Dictionary), it is changed to Array.

Then, click the Root button to the right of the word, to add a new entry array. By default, entry is of type String (exactly what we want). The entry value modify"mole_laugh1.png".

Click the plus button to add a new record, until all the picture name are added, as shown below:

Then add a mole was against file attribute list need pictures, like the above steps, but remember to name the file as a hitAnim.plist file, as shown below:

Here, we in the code to load these pictures. Open the MyScene.h file, and add the corresponding attributes for each animation, as shown in the following code:

1
2
3
// Inside @interface MyScene
@property (strong, nonatomic) SKAction *laughAnimation;
@property (strong, nonatomic) SKAction *hitAnimation;

We use the above two properties recorded for each SKAction, so you can find and reuse convenient in code.

Then add a method in MyScene.m, the code in the method creates and returns a SKAction list properties of incoming according to, as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (SKAction *)animationFromPlist:(NSString *)animPlist
{
    NSString *plistPath = [[NSBundle mainBundle] pathForResource:animPlist ofType:@"plist"]; // 1
    NSArray *animImages = [NSArray arrayWithContentsOfFile:plistPath]; // 2
    NSMutableArray *animFrames = [NSMutableArray array]; // 3
    for (NSString *imageName in animImages) { // 4
        [animFrames addObject:[SKTexture textureWithImageNamed:imageName]]; // 5
    }

    float framesOverOneSecond = 1.0f/(float)[animFrames count];

    return [SKAction animateWithTextures:animFrames timePerFrame:framesOverOneSecond resize:NO restore:YES]; // 6
}

Understand the above code is very important, we each line to have a look.:

  1. Due to the attribute list is included in the project, so it should be in the program "main bundle". The helper method to calculate the property list files in the main bundle in the full path.
  2. This line of code is read attribute list the contents of the file. A method named arrayWithContentsOfFile NSArray, here to pass a file name in the attribute list, can be read into the array. (note, this is possible, because the list of properties of root element is set to NSArray), if it is a dictionary of words, you can use[NSDictionary dictionaryWithContentsOfFile…].
  3. Create an empty array, each frame is used to store the animation.
  4. Loop through the array for each picture name.
  5. For each image texture, and then add it to the array.
  6. According to the texture returns an array of SKAction.

Next, the tail in the init method for each animation call this helper method, as shown in the code:

1
2
self.laughAnimation = [self animationFromPlist:@"laughAnim"];
self.hitAnimation = [self animationFromPlist:@"hitAnim"];

The final step: the use of animation (let the mole laughs). Modify the popMole method, as shown in the following code:

1
2
3
4
5
6
7
8
9
10
11
- (void)popMole:(SKSpriteNode *)mole
{
    SKAction *easeMoveUp = [SKAction moveToY:mole.position.y + mole.size.height duration:0.2f];
  easeMoveUp.timingMode = SKActionTimingEaseInEaseOut;
  SKAction *easeMoveDown = [SKAction moveToY:mole.position.y duration:0.2f];
  easeMoveDown.timingMode = SKActionTimingEaseInEaseOut;


    SKAction *sequence = [SKAction sequence:@[easeMoveUp, self.laughAnimation, easeMoveDown]];
    [mole runAction:sequence];
}

The above code before the only difference is that the laughAnimation action instead of pop down before the second delay. LaughAnimation action will use the laughAnim.plist texture, attention has been put before the restore is set to YES, so after the animation finished playing, he will return to normal expression.

Now compile and run the application, you can see the mole jumped out, and smiled! As shown below:

Below we have a look how to stop the animation and began to beat their smile,.

Add the game logic

We are now ready to add gameplay for the game, the game logic. The basic idea is to have a certain number of moles appears, when the game player hit the emergence of mole, will obtain the corresponding points, game player will try to get the most points.

Therefore, we need to record scores, and the scores on the screen. When the show finished, we will make prompt to the user.

First, open the MyScene.h file, and will add these instance variables to write action.:

1
2
3
4
@property (strong, nonatomic) SKLabelNode *scoreLabel;
@property (nonatomic) NSInteger score;
@property (nonatomic) NSInteger totalSpawns;
@property (nonatomic) BOOL gameOver;

There is a show for the fraction of label, a record of the current score variables, a record has how many moles pop out, and the game is over.

Then, add the following code to the initWithSize: file in the MyScene.m file tail:

1
2
3
4
5
6
7
8
9
10
// Add score label
float margin = 10;

self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
self.scoreLabel.text = @"Score: 0";
self.scoreLabel.fontSize = [self convertFontSize:14];
self.scoreLabel.zPosition = 4;
self.scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
self.scoreLabel.position = CGPointMake(margin, margin);
[self addChild:self.scoreLabel];

The above code creates a score display label. Label is located in the lower left corner of the screen, and the distance from the bottom left corner of the 10 point. And the property of horizontalAlignmentMode label is set to SKLabelHorizontalAlignmentModeLeft, so that label from the left aligned text.

In addition, here and not directly to the label set the font size, but first through a helper function to the font size conversion. This is because the font in the iPad and iPad retina size bigger. The following is the implementation of the convertFontSize method:

1
2
3
4
5
6
7
8
- (float)convertFontSize:(float)fontSize
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return fontSize * 2;
    } else {
        return fontSize;
    }
}

As shown in the code above, if iPad and iPad retina, then the font size to two times the original, otherwise intact.

Then, we need to add a touch detection code, used to determine whether a blow to a mole. But before we start, we need to add a flag to the mole, the mole to know whether you can click (tappable). Only when the mole laughs can click, and when it moves or under it is not clickable, also known as the "safety".

We can create a subclass of SKSpriteNode to record the flag, but we only need to store a piece of information, so we can use the userData attribute in SKSpriteNode instead of. As follows, again will popMole modification:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- (void)popMole:(SKSpriteNode *)mole
{
    if (self.totalSpawns > 50) return;
    self.totalSpawns++;

    // Reset texture of mole sprite
    mole.texture = self.moleTexture;

  SKAction *easeMoveUp = [SKAction moveToY:mole.position.y + mole.size.height duration:0.2f];
    easeMoveUp.timingMode = SKActionTimingEaseInEaseOut;
    SKAction *easeMoveDown = [SKAction moveToY:mole.position.y duration:0.2f];
    easeMoveDown.timingMode = SKActionTimingEaseInEaseOut;

    SKAction *setTappable = [SKAction runBlock:^{
        [mole.userData setObject:@1 forKey:@"tappable"];
    }];

    SKAction *unsetTappable = [SKAction runBlock:^{
        [mole.userData setObject:@0 forKey:@"tappable"];
    }];


    SKAction *sequence = [SKAction sequence:@[easeMoveUp, setTappable, self.laughAnimation, unsetTappable, easeMoveDown]];
    [mole runAction:sequence completion:^{
        [mole removeAllActions];
    }];
}

We have done the following modification:

Now, the mole has a flag can say whether it can be hit. Then we can add the touchesBegan: method. The following code to the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self];

    SKNode *node = [self nodeAtPoint:touchLocation];
    if ([node.name isEqualToString:@"Mole"]) {
        SKSpriteNode *mole = (SKSpriteNode *)node;

        if (![[mole.userData objectForKey:@"tappable"] boolValue]) return;

        self.score += 10;

        [mole.userData setObject:@0 forKey:@"tappable"];
        [mole removeAllActions];

        SKAction *easeMoveDown = [SKAction moveToY:(mole.position.y - mole.size.height) duration:0.2f];
        easeMoveDown.timingMode = SKActionTimingEaseInEaseOut;

        // Slow down the animation by half
        easeMoveDown.speed = 0.5;

        SKAction *sequence = [SKAction sequence:@[self.hitAnimation, easeMoveDown]];
        [mole runAction:sequence];
    }
}

The touchesBegan: method above the first obtain the location of the touch, and then find the touch location for SKNode, if the node name is Mole, it will further determine the mole tappable.

If the mole was hit, the mole is set to not be hit, and the fraction. Then stop all running action, and play was hit by the animation, animation after finished playing, immediately took the mole back into the hole.

The final step: add some code to score updates, and check a level condition, as shown in the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if (self.gameOver) return;

if (self.totalSpawns >= 50) {

    SKLabelNode *gameOverLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
    gameOverLabel.text = @"Level Complete!";
    gameOverLabel.fontSize = 48;
    gameOverLabel.zPosition = 4;
    gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame),
                                         CGRectGetMidY(self.frame));

    [gameOverLabel setScale:0.1];

    [self addChild:gameOverLabel];
    [gameOverLabel runAction:[SKAction scaleTo:1.0 duration:0.5]];

    self.gameOver = YES;
    return;
}

[self.scoreLabel setText:[NSString stringWithFormat:@"Score: %d", self.score]];

Fix! Compile and run the application, should be able to hit the mole, and see the fractional increase in! As shown below:

Adding sound

In order to make the programs more interesting, we add sound effects to the game. First come here to downloadSound. A file compression, and the sound resources drag to the WhackAMole file in. Make sure you check on the Copy items into destination group 's folder, and then click Finish.

Add the following declaration statements to the top of the MyScene.h file:

1
#import <AVFoundation/AVFoundation.h>

Then the following attribute is added to @end.:

1
2
3
@property (strong, nonatomic) AVAudioPlayer *audioPlayer;
@property (strong, nonatomic) SKAction *laughSound;
@property (strong, nonatomic) SKAction *owSound;

Then make the following changes in the MyScene.m file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Add at the bottom of your initWithSize: method
// Preload whack sound effect
self.laughSound = [SKAction playSoundFileNamed:@"laugh.caf" waitForCompletion:NO];
self.owSound = [SKAction playSoundFileNamed:@"ow.caf" waitForCompletion:NO];

NSURL *url = [[NSBundle mainBundle] URLForResource:@"whack" withExtension:@"caf"];
NSError *error = nil;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];

if (!self.audioPlayer) {
    NSLog(@"Error creating player: %@", error);
}

[self.audioPlayer play];

// Add at bottom of popMole method, change the sequence action to:
SKAction *sequence = [SKAction sequence:@[easeMoveUp, setTappable, self.laughSound, self.laughAnimation, unsetTappable, easeMoveDown]];

// Add inside touchesBegan: method, change the sequence action to:
SKAction *sequence = [SKAction sequence:@[self.owSound, self.hitAnimation, easeMoveDown]];

Fix! Compile and run the application and try it!

Decide on what path to follow

This code works inHere.

So far, about how to make a general program introduced to this end!

If you want to learn more Sprite Kit content, can have a look this book: iOS Games by Tutorials. This book will tell you need to know the content -- from physical characteristics, the magnetic attached map, and the particle system, and even make their own level editor.

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

Posted by Selma at December 09, 2013 - 10:23 AM