Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4399

How to Make a Game Like Mega Jump With Sprite Kit: Part 1/2

$
0
0
Create a game like Mega Jump using Sprite Kit

Make A Game Like Mega Jump Using Sprite Kit

In this two-part tutorial series, you will use Sprite Kit to create a game in the style of Mega Jump. If you’ve somehow missed trying Mega Jump from the App Store, it’s a quite popular vertical jumping game with really nice graphics and addictive gameplay. As you build your game, you’ll learn how to use a number of Sprite Kit’s capabilities, including the physics engine, collision detection, accelerometer control and scene transitions.

If you are new to Sprite Kit, it would be best to work your way through Ray’s Sprite Kit Tutorial for Beginners before starting this tutorial series.

But if you’re comfortable with the basics of Sprite Kit, then—jump right in!

Getting Started

You are going to create a game similar to Mega Jump. Yours will obviously be a truly amazing game and so it needs a truly amazing title. As everyone knows, “uber” is better than “mega,” so let’s call your game “Uber Jump.” ;]

At its heart, a game like Mega Jump is a physics game. The game hurls the player character up the screen and from that point the player must fight against gravity to get as high as they can. Collecting coins boosts the player upward while platforms provide temporary respite along the way.

Uber Jump will take the same model, initially thrusting your player character upward and continuously applying gravity to pull them down. Collecting stars along the way will propel the player further on their journey upward, taking care of the up and down (y-axis) portion of the player’s movement (miss too many stars and you fall down though!). The accelerometer will handle the player’s left and right movement (x-axis).

The result will be a complex, hybrid physics manipulation, as you both apply forces to the player node and also directly change its velocity. It’ll also be lots of fun!

To begin, you need a brand new Sprite Kit Xcode project. Fire up Xcode, select File\New\Project, choose the iOS\Application\SpriteKit Game template and click Next.

01-NewProject

Enter UberJump for the Product Name, iPhone for Devices and then click Next.

02-NewProjectName

Choose somewhere to save the project and click Create. Then build and run the fledgling project, and you will see the following in the Simulator:

03-NewProjectRun

Before getting down to business, let’s do some preliminary setup. There’s an ugly black-text status bar at the top of the screen. You don’t want that in your game—it will just get in the way of the action.

Open ViewController.m and add the following method to the implementation:

- (BOOL) prefersStatusBarHidden
{
  return YES;
}

This is iOS 7’s callback method for a view controller to determine whether or not to show the status bar. You are telling the view controller to hide the status bar.

Build and run again, and—voilà, no more status bar.

uj_screen_nostatusbar

You have one last thing to set up. Since your game is going to use the accelerometer, you need to make sure that tilting the device doesn’t flip the game into landscape mode.

With your project selected in the Project Navigator in Xcode, select the UberJump target, go to the General settings tab and look in the Deployment Info section. Ensure that the only available device orientation is Portrait.

04-NewProjectPortrait

Now you are ready to start adding your game’s components. First up: the pretty pictures.

Importing the Art

Before you get down to adding Sprite Kit nodes to your game, you need some art.

Download the graphics resources for this project, unzip the archive and drag the contents into your Xcode project. Make sure that “Copy items into destination group’s folder (if needed)” is checked and that your UberJump target is selected.

uj_asset_files

I’ve split the artwork into background tiles and game assets.

Background tiles are large tiles that scroll up and down the screen as the player moves:

uj_bg_thumb

The game assets are all of the sprites, such as the player character, platforms and stars:

Player@2x

The game assets are stored in a texture atlas for efficiency. You can learn more about texture atlases in Sprite Kit in this tutorial.

Note: I pieced together the graphics for Uber Jump using the amazing artwork provided by lostgarden.com. This website provides high-quality graphics for devs to use in prototyping their games and is well worth a visit. You can check their exact licensing policy.

Building the Scene

In your project, you can see that the Sprite Kit template has already created the MyScene class for you. This is the Sprite Kit scene that is currently showing the “Hello, World!” message when you run the game. Most of Uber Jump’s action will take place here, so open MyScene.m and replace the contents with the following:

#import "MyScene.h"
 
@implementation MyScene
 
- (id) initWithSize:(CGSize)size
{
  if (self = [super initWithSize:size]) {
    self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
 
  }
  return self;
}
 
@end

Build and run. MyScene now displays a simple white screen. This is the blank canvas onto which you’ll add your game nodes.

Mega Jump uses parallaxed layers to produce a neat visual representation of speed. (Eg. things in front move faster than things in the background.) In Uber Jump, you’re going to produce the same effect by creating the following layers in your scene:

  • Background: a slow-moving layer that shows the distant landscape.
  • Midground: faster-moving scenery made up of tree branches.
  • Foreground: the fastest layer, containing the player character, stars and platforms that make up the core of the gameplay.
  • HUD: the top layer that does not move and displays the score labels.

First, add the background node. Open MyScene.m and add the following method:

- (SKNode *) createBackgroundNode
{
  // 1
  // Create the node
  SKNode *backgroundNode = [SKNode node];
 
  // 2
  // Go through images until the entire background is built
  for (int nodeCount = 0; nodeCount < 20; nodeCount++) {
    // 3
    NSString *backgroundImageName = [NSString stringWithFormat:@"Background%02d", nodeCount+1];
    SKSpriteNode *node = [SKSpriteNode spriteNodeWithImageNamed:backgroundImageName];
    // 4
    node.anchorPoint = CGPointMake(0.5f, 0.0f);
    node.position = CGPointMake(160.0f, nodeCount*64.0f);
    // 5
    [backgroundNode addChild:node];
  }
 
  // 6
  // Return the completed background node
  return backgroundNode;
}

Let’s take a closer look at what you’re doing here:

  1. First, you create a new SKNode. SKNodes have no visual content, but do have a position in the scene. This means you can move the node around and its child nodes will move with it.
  2. You have 20 background images to stack to complete the background.
  3. Each child node is made up of an SKSpriteNode with the sequential background image loaded from your resources.
  4. Changing each node’s anchor point to its bottom center makes it easy to stack in 64-point-tall sections.
  5. You add each child node to the background node.
  6. Finally, you return the background node.

Now you can add the background node to your scene. Still in MyScene.m, add the following class extension above the @implementation section:

@interface MyScene ()
{
  // Layered Nodes
  SKNode *_backgroundNode;
  SKNode *_midgroundNode;
  SKNode *_foregroundNode;
  SKNode *_hudNode;
}
@end

You are adding private instance variables for each of the nodes you need for the game. You only need the background node for the moment, but it doesn’t hurt to add the other node declarations now.

To add the background to the scene, insert the following into initWithSize: in MyScene.m, just after the line that sets the background color:

// Create the game nodes
// Background
_backgroundNode = [self createBackgroundNode];
[self addChild:_backgroundNode];

Build and run to see your background node displayed in the scene, as shown below:

05-BackgroundNode

Note: Although you added 20 nodes to the background node, you’ll see that the node count in the scene is only nine nodes if you’re running on a 4″ display or eight nodes if you’re running on a 3.5″ display. Sprite Kit is clever enough to include only the nodes that are actually visible at any given moment in the game.

Adding the Player Node

It’s time for your Uber Jumper to enter the scene. In MyScene.m, add the following method:

- (SKNode *) createPlayer
{
  SKNode *playerNode = [SKNode node];
  [playerNode setPosition:CGPointMake(160.0f, 80.0f)];
 
  SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Player"];
  [playerNode addChild:sprite];
 
  return playerNode;
}

As with the background node you added earlier, you create a new SKNode and add the SKSpriteNode to it as a child. You position the player node so that it is not at the bottom of the scene.

To add the player node to the scene, you first need to create a foreground node. As discussed above, the foreground node will contain the player, the stars and the platforms. That means when you move the foreground node, all the game elements it contains will move together.

Add the following to initWithSize:, just after the line that adds _backgroundNode to the scene:

// Foreground
_foregroundNode = [SKNode node];
[self addChild:_foregroundNode];

Now that you have your foreground node for the gameplay elements, you can add the player node to it.

At the top of MyScene.m, add the following variable to the class extension along with the other variables you added to hold the layer nodes:

// Player
SKNode *_player;

Now add the player node to the scene by inserting the following into initWithSize:, just after the line that adds _foreground to the scene:

// Add the player
_player = [self createPlayer];
[_foregroundNode addChild:_player];

Build and run to see your Uber Jumper ready to start their adventure:

06-ForegroundNode

Adding Gravity and a Physics Body

The Uber Jumper looks a little too comfortable sitting there, so you’re going bring some physics into play and see what happens.

First, your game can’t have physics without gravitation. In MyScene.m, add the following line to initWithSize:, after the line that sets the background color:

// Add some gravity
self.physicsWorld.gravity = CGVectorMake(0.0f, -2.0f);

You add a suitable amount of gravity to the physics world, based on the gameplay you want to achieve. Gravity has no influence along the x-axis, but produces a downward force along the y-axis.

Build and run to see how this gravity affects the player node.

06-ForegroundNode

Hmm… Nothing happened. Why isn’t the gravity affecting the player node? See if you can figure it out yourself before clicking below to see the answer.

Solution Inside: Solution SelectShow>

In createPlayer, add the following just before the return statement at the end:

// 1
playerNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
// 2
playerNode.physicsBody.dynamic = YES;
// 3
playerNode.physicsBody.allowsRotation = NO;
// 4
playerNode.physicsBody.restitution = 1.0f;
playerNode.physicsBody.friction = 0.0f;
playerNode.physicsBody.angularDamping = 0.0f;
playerNode.physicsBody.linearDamping = 0.0f;

The above code defines the player node’s physics body. Take a look at it in detail:

  1. Each physics body needs a shape that the physics engine can use to test for collisions. The most efficient body shape to use in collision detection is a circle (easier to detect if overlaps another circle you see), and fortunately a circle fits your player node very well. The radius of the circle is half the width of the sprite.
  2. Physics bodies can be static or dynamic. Dynamic bodies are influenced by the physics engine and are thus affected by forces and impulses. Static bodies are not, but you can still use them in collision detection. That is, a static body will never move, but things can bump into it. Since you want your player node to be affected by gravity, you set it’s dynamic property to YES.
  3. You want your player node to remain upright at all times and so you disable rotation of the node.
  4. Since you’re handling collisions yourself in this game, you adjust the settings on the player node’s physics body so that it has no friction or damping. However, you set its restitution to 1, which means the physics body will not lose any of its momentum during collisions.

Build and run to see your player sprite fall off the bottom of the screen as gravity draws it relentlessly toward the Earth’s core.

05-BackgroundNode

Perhaps that sounded a little melodramatic, but how is an Uber Jumper supposed to get anywhere in this world?

Tap To Start

Now that gravity is working, you’re going to give the player sprite a fighting chance and make its physics body static until the user actually decides to start playing.

Still inside MyScene.m, find the line in createPlayer that sets the dynamic property on playerNode‘s physics body. Change it to NO, as shown here:

playerNode.physicsBody.dynamic = NO;

You are going to write code to allow the player to start the game by tapping the screen. The player needs to know this, though, so first you need to put some instructions on the screen.

Since the instructions will go in your HUD node, create that layer first. In initWithSize:, just after where you add the foreground node to the scene, insert the following code:

// HUD
_hudNode = [SKNode node];
[self addChild:_hudNode];

The starter project includes an image that tells the player to tap to start the game, so add the following variable to the class extension at the top of MyScene.m:

// Tap To Start node
SKSpriteNode *_tapToStartNode;

To show the node, add the following code to initWithSize:, just after the line that adds the player node:

// Tap to Start
_tapToStartNode = [SKSpriteNode spriteNodeWithImageNamed:@"TapToStart"];
_tapToStartNode.position = CGPointMake(160, 180.0f);
[_hudNode addChild:_tapToStartNode];

Build and run, and you will see the instruction to “Tap to Start” above the player sprite:

07-TapToStart

To respond to touch events and start the game, add the following method to MyScene.m:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  // 1
  // If we're already playing, ignore touches
  if (_player.physicsBody.dynamic) return;
 
  // 2
  // Remove the Tap to Start node
  [_tapToStartNode removeFromParent];
 
  // 3
  // Start the player by putting them into the physics simulation
  _player.physicsBody.dynamic = YES;
  // 4
  [_player.physicsBody applyImpulse:CGVectorMake(0.0f, 20.0f)];
}

Take a look at the method in detail:

  1. You check to see if the player node is already dynamic. If so, you ignore the touch event and return.
  2. You remove the Tap to Start node.
  3. You change the player node’s physics body to dynamic so that the physics engine can influence it.
  4. You give the player node an initial upward impulse to get them started.

Build and run. When you tap the screen, the game removes the Tap to Start node and thrusts the player sprite upward, albeit briefly, before gravity takes over.

Game Objects: Reach for the Stars!

To give your player something to do—as well as some upward momentum—it’s “high” time for you to add stars to your game. Stars have a key role in Uber Jump: They are what the player needs to grab to move higher into the level.

Initially, you will add one star to the game and implement it fully. Then in Part Two of this tutorial, you’ll complete the level.

In Uber Jump, as in Mega Jump, when the player sprite goes a certain distance higher than a star, platform or any other game object, the game will remove that object from the scene. Since star and platform nodes share this functionality, it makes sense to create a subclass of SKNode for all game objects.

Create a new Objective-C class called GameObjectNode and make it a subclass of SKNode.

08-NewClassGameObjectNode

GameObjectNode will provide the following functionality:

  • It will remove the game object from the scene if the player node has passed it by more than a set distance.
  • It will handle collisions between the player node and the object. This method will return a BOOL that informs the scene whether the collision with the game object has resulted in a need to update the HUD—for example, if the player has scored points.

Add the following method declarations to GameObjectNode.h:

// Called when a player physics body collides with the game object's physics body
- (BOOL) collisionWithPlayer:(SKNode *)player;
 
// Called every frame to see if the game object should be removed from the scene
- (void) checkNodeRemoval:(CGFloat)playerY;

You’ll call collisionWithPlayer: whenever the player node collides with this object, and you’ll call checkNodeRemoval: every frame to give the node a chance to remove itself.

Add the following definitions for these methods to GameObjectNode.m:

- (BOOL) collisionWithPlayer:(SKNode *)player
{
  return NO;
}
 
- (void) checkNodeRemoval:(CGFloat)playerY
{
  if (playerY > self.position.y + 300.0f) {
    [self removeFromParent];
  }
}

In the GameObjectNode class, collisionWithPlayer: is simply a stub. You will define the full method in each of your game object subclasses.

checkNodeRemoval: checks to see if the player node has traveled more than 300 points beyond this node. If so, then the method removes the node from its parent node and thus, removes it from the scene.

The Star Class

Now that you have a base class for the interactive game nodes, you can create a class for your stars. Create a new Objective-C class called StarNode and make it a subclass of GameObjectNode.

09-NewClassStarNode

In StarNode.m, add the following implementation of collisionWithPlayer: for the star:

- (BOOL) collisionWithPlayer:(SKNode *)player
{
  // Boost the player up
  player.physicsBody.velocity = CGVectorMake(player.physicsBody.velocity.dx, 400.0f);
 
  // Remove this star
  [self removeFromParent];
 
  // The HUD needs updating to show the new stars and score
  return YES;
}

Collision with a star boosts the player node up the y-axis. You may be thinking, “Why am I not using a force or impulse in the physics engine to do this?”

If you were to apply the star boost as a force or impulse, it wouldn’t always have the same effect. For example, if the player node were moving down the screen when it collided with the star, then the force would have a much weaker effect on the player than if the player were already moving up the screen.

The following diagram shows a very simplified visualization of this:

10-ForcesAndBounce

A solution to this problem is to change the player node’s velocity directly. The player node’s velocity is obviously made up of an x-axis speed and a y-axis speed.

The x-axis speed needs to stay the same, since it is only affected by the accelerometer, which you’ll implement later. In the method above, you set the y-axis velocity to 400 on collision—a fixed amount so that the collision has the same effect no matter what the player node is doing when it collides with the star.

Now add your new star class to the scene. Open MyScene.m and import your StarNode class header:

#import "StarNode.h"

Now add the following method to MyScene:

- (StarNode *) createStarAtPosition:(CGPoint)position
{
  // 1
  StarNode *node = [StarNode node];
  [node setPosition:position];
  [node setName:@"NODE_STAR"];
 
  // 2
  SKSpriteNode *sprite;
  sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Star"];
  [node addChild:sprite];
 
  // 3
  node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
 
  // 4
  node.physicsBody.dynamic = NO;
 
  return node;
}

The code above should all look familiar by now:

  1. You instantiate your StarNode and set its position.
  2. You then assign the star’s graphic using an SKSpriteNode.
  3. You’ve given the node a circular physics body – you use it for collision detection with other objects in the game.
  4. Finally, you make the physics body static, because you don’t want gravity or any other physics simulation to influence the stars.

Now add the following code to initWithSize:, just before where you create the player node. You want the stars to be behind the player in the foreground node and so you need to add them before you add the player node.

// Add a star
StarNode *star = [self createStarAtPosition:CGPointMake(160, 220)];
[_foregroundNode addChild:star];

Build and run the game. Tap to start and watch the player sprite collide with the star.

11-FirstStar

The Uber Jumper bonks its head on the star. That wasn’t the plan! Why do you think that happened? Take a guess.

Solution Inside: Solution SelectShow>

Collision Handling and Bit Masks

To handle collisions between the player and star nodes, you need to capture the collision event and call the GameObjectNode method collisionWithPlayer:.

This is a good time to take a look at collision bit masks in Sprite Kit.

When setting up collision information for your physics bodies, there are three bit mask properties you can use to define the way the physics body interacts with other physics bodies in your game:

  • categoryBitMask defines the collision categories to which a physics body belongs. [TODO: Can a physics body belong to more than one category? If not, make “categories” singular.]
  • collisionBitMask identifies the collision categories with which this physics body should collide. Here, “collide” means to bump into each other as physical objects would. For example, in a third-person shooter you may want your player sprite to collide with enemy sprites, but pass through other player sprites.
  • contactTestBitMask tells Sprite Kit that you would like to be informed when this physics body makes contact with a physics body belonging to one of the categories you specify. For example, in your game you want Sprite Kit to tell you when the player sprite touches a star or a platform. [TODO: I think we need to distinguish between “makes contact with” and “collides with” – that the categories in this bit mask don’t have to be collision categories.] Using the correct combination of settings for the contactTestBitMask and the collisionBitMask, you can tell Sprite Kit to let objects pass through each other but still notify you when that happens so you can trigger events.

First, define your categories. Open MyScene.m and add the following typedef above the @interface section:

typedef NS_OPTIONS(uint32_t, CollisionCategory) {
  CollisionCategoryPlayer   = 0x1 << 0,
  CollisionCategoryStar     = 0x1 << 1,
  CollisionCategoryPlatform = 0x1 << 2,
};

To set up the player node’s collision behavior, add the following code to the bottom of createPlayer, just before the return statement:

// 1
playerNode.physicsBody.usesPreciseCollisionDetection = YES;
// 2
playerNode.physicsBody.categoryBitMask = CollisionCategoryPlayer;
// 3
playerNode.physicsBody.collisionBitMask = 0;
// 4
playerNode.physicsBody.contactTestBitMask = CollisionCategoryStar | CollisionCategoryPlatform;

Let’s take a closer look at this code block:

  1. Since this is a fast-moving game, you ask Sprite Kit to use precise collision detection for the player node’s physics body. After all, the gameplay for Uber Jump is all about the player node’s collisions, so you’d like it to be as accurate as possible! (it costs few more cpu cycles, but the fun is considerably more!)
  2. This defines the physics body’s category bit mask. It belongs to the CollisionCategoryPlayer category.
  3. By setting collisionBitMask to zero, you’re telling Sprite Kit that you don’t want its physics engine to simulate any collisions for the player node. That’s because you’re going to handle those collisions yourself!
  4. Here you tell Sprite Kit that you want to be informed when the player node touches any stars or platforms.

Now set up the star node. Add the following code to the bottom of createStarAtPosition:, just before the return statement:

node.physicsBody.categoryBitMask = CollisionCategoryStar;
node.physicsBody.collisionBitMask = 0;

This is similar to your setup for the player node. You assign the star’s category and clear its collisionBitMask so it won’t collide with anything. In this case, though, you don’t set its contactTestBitMask, which means Sprite Kit won’t notify you when something touches the star. You already instructed Sprite Kit to send notifications when the player node touches the star, which is the only contact with the star that you care about, so there’s no need to send notifications from the star’s side.

Sprite Kit sends notifications for the node contacts you’ve registered by calling didBeginContact: on its SKContactDelegate. Set the scene itself as the delegate for the physics world by adding the SKContactDelegate protocol to the MyScene class extension in MyScene.m. It should now look like this:

@interface MyScene () <SKPhysicsContactDelegate>

Now register the scene to receive contact notifications by adding the following line to initWithSize:, just after the line that sets the gravity:

// Set contact delegate
self.physicsWorld.contactDelegate = self;

Finally, add the following method to MyScene.m to handle collision events:

- (void) didBeginContact:(SKPhysicsContact *)contact
{
  // 1
  BOOL updateHUD = NO;
 
  // 2
  SKNode *other = (contact.bodyA.node != _player) ? contact.bodyA.node : contact.bodyB.node;
 
  // 3
  updateHUD = [(GameObjectNode *)other collisionWithPlayer:_player];
 
  // Update the HUD if necessary
  if (updateHUD) {
    // 4 TODO: Update HUD in Part 2
  }
}

Let’s take a closer look at this code:

  1. You initialize the updateHUD flag, which you’ll use at the end of the method to determine whether or not to update the HUD for collisions that result in points.
  2. SKPhysicsContact does not guarantee which physics body will be in bodyA and bodyB. You know that all collisions in this game will be between the player node and a GameObjectNode, so this line figures out which one is not the player node.
  3. Once you’ve identified which object is not the player, you call the GameObjectNode’s collisionWithPlayer: method.
  4. This is where you will update the HUD, if required. You’ll implement the HUD in Part Two, so there’s nothing but a comment here for now.

Build and run the game. Tap to start. Upon collision, the star provides the player sprite with a sizable boost and then removes itself from the scene. Good work!

12-FirstStarCollision

That was a lot of heavy lifting there! Take a well-deserved break before moving on. In the next section, you’re going to add a new star type—the uber star—as well as a cool sound effect.

Note: If you want to know more about detecting contacts and collisions in Sprite Kit feel free to check out “iOS Games by Tutorials“, which contains 3 solid chapters on Sprite Kit physics.

Multiple Star Types

Uber Jump will have two different kinds of stars: those worth a single point and special stars worth five points. Each star-type will have its own graphic. To identify the star type, you’ll have an enumeration in the StarNode class.

At the top of StarNode.h, add the following enumeration:

typedef NS_ENUM(int, StarType) {
  STAR_NORMAL,
  STAR_SPECIAL,
};

To store the star type, add the following property to the StarNode interface in StarNode.h:

@property (nonatomic, assign) StarType starType;

You now need to specify a star type when creating a star, so in MyScene.m, add starType as a parameter to the createStarAtPosition: method signature so that it looks like this:

- (StarNode *) createStarAtPosition:(CGPoint)position ofType:(StarType)type

Inside createStarAtPosition:ofType:, replace the three lines of code that create and add the SKSpriteNode with the following:

[node setStarType:type];
SKSpriteNode *sprite;
if (type == STAR_SPECIAL) {
  sprite = [SKSpriteNode spriteNodeWithImageNamed:@"StarSpecial"];
} else {
  sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Star"];
}
[node addChild:sprite];

First, you set the star type. Then, when you create the sprite, you check to see which star type it is so you can get the correct graphic.

All that remains is to specify a star type when you create the star. In initWithSize: in MyScene.m, find the line where you call createStarAtPosition: and replace it with this:

StarNode *star = [self createStarAtPosition:CGPointMake(160, 220) ofType:STAR_SPECIAL];

You specify that you would like this star to be a STAR_SPECIAL type.

Build and run. Your star is now pink! Later, you’ll add a scoring system and the differences in star types will become more apparent.

13-FirstStarSpecial

Ping! Adding a Sound Effect

It would be nice if, when the player collides with the star, they got an audible cue. Download the star sound effect here and drag it into your Xcode project. Make sure that “Copy items into destination group’s folder (if needed)” is checked and that your UberJump target is selected.

To add sound effects to your StarNode class, you need to import the AVFoundation library, so at the top of StarNode.m, add the following line:

@import AVFoundation;

In Sprite Kit, you use SKActions to play sounds. At the top of StarNode.m, add the following class extension:

@interface StarNode ()
{
  SKAction *_starSound;
}
@end

Now all that remains is to instantiate the sound action and run it when the player node collides with the star.

Add the following init method to StarNode.m:

- (id) init
{
  if (self = [super init]) {
    // Sound for when a star is collected
    _starSound = [SKAction playSoundFileNamed:@"StarPing.wav" waitForCompletion:NO];
  }
 
  return self;
}

This instantiates the SKAction with a sound file. You set up the action here so it will be ready to play when you need it. If you were to create it when you wanted to play it, the user would notice a delay in the game as the app loaded the sound resource.

Add the following line to collisionWithPlayer: in StarNode.m, just before the line that removes the node from its parent:

// Play sound
[self.parent runAction:_starSound];

This runs the sound SKAction using the star node’s parent node. You use the parent node rather than the star node itself because the very next line removes the star node from its parent. Running the action on the star itself wouldn’t work because the node would be gone before the action had a chance to run.

Build and run. As the player sprite collides with the star, you’ll hear a delightful, twinkly ping. :]

Game Objects: Platforms

In the final section of the first part of this tutorial, you’re going to add a platform to the scene. You’ll represent platforms with a new subclass of GameObjectNode so that you get all the associated goodness from that class. As with the stars, you’ll have two types of platforms: one that is indestructible and one that disappears as soon as the player sprite has jumped off of it.

Start by creating a new Objective-C class called PlatformNode and make it a subclass of GameObjectNode.

14-NewClassPlatformNode

In PlatformNode.h, add the following enumeration above the @interface section to define the two platform types:

typedef NS_ENUM(int, PlatformType) {
  PLATFORM_NORMAL,
  PLATFORM_BREAK,
};

Still in PlatformNode.h, add a property for the platform type to the interface:

@property (nonatomic, assign) PlatformType platformType;

In PlatformNode.m, add the following implementation of collisionWithPlayer::

- (BOOL) collisionWithPlayer:(SKNode *)player
{
  // 1
  // Only bounce the player if he's falling
  if (player.physicsBody.velocity.dy < 0) {
    // 2
    player.physicsBody.velocity = CGVectorMake(player.physicsBody.velocity.dx, 250.0f);
 
    // 3
    // Remove if it is a Break type platform
    if (_platformType == PLATFORM_BREAK) {
      [self removeFromParent];
    }
  }
 
  // 4
  // No stars for platforms
  return NO;
}

Let’s take a closer look at this code:

  1. As is common in this type of game, the player node should only bounce if it hits a platform while falling, which is indicated by a negative dy value in its velocity. This check also ensures the player node doesn’t collide with platforms while moving up the screen.
  2. You give the player node a vertical boost to make it bounce off the platform. You accomplish this the same way you did for the star, but with a less powerful boost. You want stars to be important, right?
  3. If this is a PLATFORM_BREAK-type platform, then you remove the platform from the scene.
  4. Finally, the Uber Jumper doesn’t get points from bouncing on or off platforms so there is no need to refresh the HUD.

To add a platform to your scene, open MyScene.m and import the PlatformNode header at the top, like so:

#import "PlatformNode.h"

Now add the following method:

- (PlatformNode *) createPlatformAtPosition:(CGPoint)position ofType:(PlatformType)type
{
  // 1
  PlatformNode *node = [PlatformNode node];
  [node setPosition:position];
  [node setName:@"NODE_PLATFORM"];
  [node setPlatformType:type];
 
  // 2
  SKSpriteNode *sprite;
  if (type == PLATFORM_BREAK) {
    sprite = [SKSpriteNode spriteNodeWithImageNamed:@"PlatformBreak"];
  } else {
    sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Platform"];
  }
  [node addChild:sprite];
 
  // 3
  node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];
  node.physicsBody.dynamic = NO;
  node.physicsBody.categoryBitMask = CollisionCategoryPlatform;
  node.physicsBody.collisionBitMask = 0;
 
  return node;
}

This method is very similar to createStarAtPosition:ofType:, but note these main points:

  1. You instantiate the PlatformNode and set its position, name and type.
  2. You choose the correct graphic for the SKSpriteNode based on the platform type.
  3. You set up the platform’s physics, including its collision category.

Now to add the platform, insert the following code in initWithSize:, just before the line that creates the star:

// Add a platform
PlatformNode *platform = [self createPlatformAtPosition:CGPointMake(160, 320) ofType:PLATFORM_NORMAL];
[_foregroundNode addChild:platform];

Build and run the game. Tap to start and watch as the player sprite gets a boost from the star and then bounces on the platform!

15-FirstPlatform

Where to Go From Here?

Well done! As you’ve seen, creating a game like Mega Jump is not that hard at all. You’ve got all the basics of Uber Jump in place and are on your way to building a great game.

Here is the project with everything you’ve done so far in this tutorial series.

In Part Two of the series, you’ll implement the accelerometer for movement on the x-axis. You’ll also load an entire level from a property list and add a scoring system. That’s a lot to look forward to, right? ;]

Please post a comment below if you have any questions, thoughts or suggestions for future tutorials!

How to Make a Game Like Mega Jump With Sprite Kit: Part 1/2 is a post from: Ray Wenderlich

The post How to Make a Game Like Mega Jump With Sprite Kit: Part 1/2 appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4399

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>