Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 2

Learn how to make a platform game like Super Mario Brothers in this Sprite Kit tutorial! By Jake Gundersen.

Leave a rating/review
Save for later
Share

Learn how to make a game like Super Mario!

Learn how to make a game like Super Mario!

Update 2/2/14: I have updated this tutorial for Sprite Kit to celebrate the launch of the second edition of the Platformer Game Starter Kit, which is fully updated for Sprite Kit. Enjoy!

Welcome back to our 2-part Sprite Kit tutorial series on making a game like Super Mario!

In the first part of the series, you learned how to create a simple, tile-based physics engine that controls how the hero of your game, Koalio, moves around his world.

In this second and final part of the series, you’ll learn how to make Koalio run and jump – the fun part of the game!

You’ll also add collisions with those scary spikey floors, handle winning and losing, and of course add some gratuitous sound effects and music.

This second part is WAY simpler (and shorter) than the first tutorial, a reward for the hard work you put in last time. So turn your coding mojo on, and enjoy!

Moving Koalio Around

The controls you’re going to implement are very simple. There will be forward and jump controls only — much like 1-bit Ninja. If you touch the left half of the screen, Koalio will run forward. Touching the right half of the screen will make Koalio jump.

You heard me right – Koalio can’t move backwards! True Koalas don’t back down from danger.

Since Koalio will be moved forward by the user, rather than by the GameLevelScene, you need some values that you can check in the Player class to update his forward velocity. Add the following properties to the Player class:

In Player.h:

@property (nonatomic, assign) BOOL forwardMarch;
@property (nonatomic, assign) BOOL mightAsWellJump;

Now add the following touch-handling methods to the GameLevelScene.m:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  for (UITouch *touch in touches) {
    CGPoint touchLocation = [touch locationInNode:self];
    if (touchLocation.x > self.size.width / 2.0) {
      self.player.mightAsWellJump = YES;
    } else {
      self.player.forwardMarch = YES;
    }
  }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  for (UITouch *touch in touches) {
    
    float halfWidth = self.size.width / 2.0;
    CGPoint touchLocation = [touch locationInNode:self];
    
    //get previous touch and convert it to node space
    CGPoint previousTouchLocation = [touch previousLocationInNode:self];
    
    if (touchLocation.x > halfWidth && previousTouchLocation.x <= halfWidth) {
      self.player.forwardMarch = NO;
      self.player.mightAsWellJump = YES;
    } else if (previousTouchLocation.x > halfWidth && touchLocation.x <= halfWidth) {
      self.player.forwardMarch = YES;
      self.player.mightAsWellJump = NO;
    }
  }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  
  for (UITouch *touch in touches) {
    CGPoint touchLocation = [touch locationInNode:self];
    if (touchLocation.x < self.size.width / 2.0) {
      self.player.forwardMarch = NO;
    } else {
      self.player.mightAsWellJump = NO;
    }
  }
}

These changes should be pretty straightforward. If the user creates a touch event that has an X-coordinate of less than half the screen's width, you turn on the forwardMarch boolean for the player. Otherwise (if the location of the touch event is greater than that), simply turn on the mightAsWellJump boolean.

touchesMoved is a little more complicated, because you only want to change the boolean values above if the touch crosses mid screen, so you have to calculate the previousTouch location as well. Other than that, you're just checking to see which direction the touch crosses and turning the appropriate boolean on or off. Finally, if the user stops touching the one side of the screen or the other, you want to turn the appropriate boolean value off.

There are a few changes that need to be made to detect touches. First, add this line to initWithSize:

self.userInteractionEnabled = YES;

Now that you are passing the touches through to your player class booleans, you can add some code to the update method so that Koalio can move. Start with the forward movement. Change the update method in Player.m to the following:

- (void)update:(NSTimeInterval)delta
{
  CGPoint gravity = CGPointMake(0.0, -450.0);
  CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
  //1
  CGPoint forwardMove = CGPointMake(800.0, 0.0);
  CGPoint forwardMoveStep = CGPointMultiplyScalar(forwardMove, delta);

  self.velocity = CGPointAdd(self.velocity, gravityStep);
  //2
  self.velocity = CGPointMake(self.velocity.x * 0.9, self.velocity.y);
  //3
  //Jumping code goes here
  if (self.forwardMarch) {
    self.velocity = CGPointAdd(self.velocity, forwardMoveStep);
  }
  //4
  CGPoint minMovement = CGPointMake(0.0, -450);
  CGPoint maxMovement = CGPointMake(120.0, 250.0);
  self.velocity = CGPointMake(Clamp(self.velocity.x, minMovement.x, maxMovement.x), Clamp(self.velocity.y, minMovement.y, maxMovement.y));
  
  CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
  
  self.desiredPosition = CGPointAdd(self.position, velocityStep);
}

Let’s break these new lines down section-by-section:

When the force is removed, you want the player to come to a stop, but not immediately. Here you apply a 0.90 damping; in other words, reducing the overall horizontal force by ten percent each frame.

  1. You add a forwardMove force that will come into play while the user is touching the screen. As a reminder, you are scaling that force (800 points per second) to the appropriate amount for the current frame's time step (delta) in order to have consistent acceleration.
  2. Here you apply a damping force to the horizontal velocity to simulate friction. You're applying physics here just as you did with gravity. In each frame, additional movement force will be applied.
  3. In section three, you check for the boolean (meaning that the screen is being touched) and add the forwardMove force if appropriate.
  4. In section four, you apply the clamping. This limits the player's maximum movement speed in both the horizontal (running top speed), upward (jumping speed) and downward (falling) directions.

    These damping and clamping values put limits on how quickly things happen in the game. It also prevents the buildup of velocity problem that you experienced in the first part of the tutorial.

    You want the player to have a maximum speed and to reach that speed within a second or less. This way your player's movements still feel natural, and provide a level of control. The maximum force that you'll allow is a positive 120 value, which would be one quarter the screen width per second.

    If you want to increase the rate of acceleration of your player, increase the forwardMove variable and the damping value 0.90 respectively. If you want to increase your player’s maximum speed, simply increase the 120 value. You're also capping the jumping velocity at 250 and falling velocity at 450.

Build and run. You should be able to make Koalio run forward by pressing the left half of the screen. Watch that Koala go!

IMG_2644

Jake Gundersen

Contributors

Jake Gundersen

Author

Over 300 content creators. Join our team.