Sprite Kit Tutorial: Making a Common Program 2

Original Link: http://www.cnblogs.com/riasky/p/3459276.html

Note 1: This text was translated from Sprite Kit Tutorial: Making a Universal App: Part 2

 

Catalog

 

In the last article And created a basic game program: some cute moles jump out of their holes.It also takes a lot of space to introduce UI design and coordinate system knowledge to make the program run well on 3.5 inch, 4 inch, iPad and iPad Retina of the iPhone.

In this article, we'll add some cute animations to the mole: the smiling and beating expressions, and add a way to earn points by tapping the mole, as well as some sound effects.

Definition of animation: feasibility

To make the game more interesting, we'll add two animations to the mole.First, it's an animation of laughter when the mole jumps out of the hole, then it's a beaten expression when you hit them.

Before we get started, let's see the feasibility of defining an animation in your code.

The pictures needed for the mole laughing animation are in the following order: mole_laugh1.png, mole_laugh2.png mole_laugh3.png, mole_laugh2.png, mole_laugh3.png, mole_laugh1.png.

We can hard-code our animations 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's easy to let our code grow dramatically.For brevity, instead of using the code above to define the animation, use a list of attributes instead.

Attribute List

It's okay if you haven't had an attribute list before.A list of attributes is a special file that can be created with Xcode and contains arrays, dictionaries, strings, and numbers in a format that is easy to create and easily readable in the middle of the code.

Let's try it in Xcode.Right-click ShackAMole and select "New File..."Then select iOS\ResourceProperty List and click Next.Name the file laughAnim.plist and click Create.You can now see the visual editing interface of laughAnim.plist in Xcode, as shown in the following figure:

south

Each attribute list has a root element.Usually this is an array or a dictionary.In this file we created, the name of all the pictures that will contain the animation needed to make the mole laugh is an array, so click on the second column of the root element (Type, currently Dictionary) and change it to Array.

Next, click the plus button to the right of the Root word to add a new entry to the array.By default, entry is of type String (which happens to be what we want). Modify the value of entry to "mole_laugh1.png".

Continue clicking the plus button to add a new record until all the picture names are added, as shown in the following image:

Next, add an attribute list file for the picture the mole needs to be hit, as in the previous steps, but remember to name the file hitAnim.plist, as shown below:

Next, let's load the pictures into the code.Open the MyScene.h file and add properties for each animation action as shown in the following code:

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

We record each SKAction with these two attributes so that it can be easily found and reused in our code.

Then add a method in MyScene.m, where the code creates and returns a SKAction based on the list of attributes passed in, as follows:

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
}

It's important to understand the code above. Let's go through it line by line:

  1. Since the list of properties is contained in the project, it should be in the main bundle of the program.This helper method calculates the full path of the property list file in the main bundle.
  2. This line of code reads the contents of the property list file.NSArray has a method called arrayWithContentsOfFile, which is passed in to read the contents of the property list into the array.(Note that this is possible because the root element in the attribute list is set to NSArray), if it is a dictionary, you can use [NSDictionary dictionary with Contents OfFile...].
  3. Create an empty array to store each frame of the animation.
  4. Loop through the array to get each picture name.
  5. Get the texture of each picture and add it to the array.
  6. Returns a SKAction based on a texture array.

Next, call the helper method above for each animation at the end of the init method, as shown in the following code:

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

Last step: Use animation (to make the mole laugh).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 only difference with the previous code is that the laughAnimation action replaces the one-second delay before pop down.LaughAnimation action uses textures from laughAnim.plist, noting that restore was previously set to YES, so when the animation is finished, the mole will return to its normal expression.

Now compile and run the program, you can see the mole jump out and laugh!As shown in the following figure:

Let's see how to stop the snoring animations and start beating them.

Add Game Logic

Now we're ready to add play to the game, which is game logic.The basic idea is that there will be a certain number of moles. When a player hits these moles, he will get the corresponding points, and the player will try to get the most points.

So we need to record the scores and show them on the screen.When the mole is displayed, we will alert the user.

Open the MyScene.h file first and add the following instance variables after the previously written action s:

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

There is a label to show the score, a variable to record the current score, a record of how many moles have been popped up, and whether the game is over.

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

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 code above creates a label for the score display.The label is in the lower left corner of the screen and is 10 point s away from the lower left corner.Set the horizontalAlignmentMode l property of label to SKLabelHorizontalAlignmentModeLeft, which aligns label text from the left.

In addition, instead of setting the font size directly for label, the font size is first converted through a helper function.This is because the font size on the iPad and the iPad retina is a little larger.The following is an 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 it's an iPad and an iPad retina, then double the font size or leave it as it is.

Next, we need to add code for touch detection to determine if a mole has been hit.But before we start, we need to add a flag to the mole to see if it can tappable at the moment.A mole can only click when it laughs, but it cannot click when it moves or is underneath, which is called "safe".

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

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

The main modifications are as follows:

  • If 50 moles are shown, return immediately, that is, 50 is the maximum limit in the game.
  • At the beginning of the function, reset the picture of the mole ("mole_1.png").This is done because if the mole was beaten the last time it was shown, it still shows the beaten image, so you need to reset it before showing it here.
  • Run an action that runs a block of code before the mole laughs.The block sets the key value named tappable in the userData dictionary to 1, which means the mole can be hit.
  • Similarly, when the mole laughs, run the same action: set the tappable value to 0.

Now the mole has a flag to indicate whether it can be hit or not.Then we can add touchesBegan: method.Add 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 first gets the location of the touch, and then finds the SKNode for the touch location. If the name of the node is Mole, the tappable of the mole is further determined.

If a mole is hit, the mole is set not to be hit anymore and the score is increased.Then stop all running action s and play the hit animation. When the animation is finished, the mole is immediately put back into the hole.

Last step: Add some code to update the score and do a check of the level completion criteria, as shown in the following 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]];

Done!Compile and run the program, you should be able to hit the mole and see the score increase!As shown in the following figure:

Add Sound

To make the program more interesting, let's add sound effects to this game.Download here first Sound effect Come on.Press out the file and drag the sound resource into the WhackAMole file.Make sure Copy items into destination group's folder is checked and click Finish.

Add the following statement to the top of the MyScene.h file:

1
#import <AVFoundation/AVFoundation.h>

Next, add the following properties before @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]];

Done!Try compiling and running the program!

Decide on what path to follow

The code project for this article is Here.

This concludes the introduction to how to make a generic program!

If you want to learn more about Sprite Kit, you can read this book: iOS Games by Tutorials .This book tells you what you need to know - from physical properties to magnetic maps, particle systems, and even making your own level editor.


Reprinted at: https://www.cnblogs.com/riasky/p/3459276.html

Keywords: Attribute xcode iOS

Added by Das Capitolin on Sat, 20 Jul 2019 19:14:49 +0300