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

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
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

Loading the TMXTiledMap

I’m going to assume you’re familiar with how tile maps work. If you aren’t, you can learn more about them in this tutorial. Currently, Sprite Kit doesn’t include support for TMXTileMaps, but not to worry, you’ll be relying on the JSTileMap open source project for this tutorial, which is already added to your starter project.

Let’s take a look at the level. Start up your Tiled map editor (download it if you don’t have it already) and open level1.tmx from your project directory. You’ll see the following:

A platformer game level made with Tiled

If you look in the sidebar, you’ll see that there are three different layers:

  • hazards: This layer contains the things the Koala needs to avoid touching to stay alive (gulp).
  • walls: This layer contains tiles the Koala cannot move through, which mostly consist of floor tiles.
  • background: This layer contains things that are there for aesthetics only, such as clouds or hills.

Now time to code! Open up GameLevelScene.m and add the following import:

#import "JSTileMap.h"

This imports the header file for the JSTileMap library that adds support for TMX files in Sprite Kit. Next add a private interface with the following private property right before the @implementation:

@interface GameLevelScene()
@property (nonatomic, strong) JSTileMap *map;
@end

This adds a private instance variable for the tile map into your class.

Next you’ll load this map into your layer by loading it in the initWithSize: section. Add the following code to initWithSize: in the if (self = [super initWithSize:size]) {:

self.backgroundColor = [SKColor colorWithRed:.4 green:.4 blue:.95 alpha:1.0];
    
self.map = [JSTileMap mapNamed:@"level1.tmx"];
[self addChild:self.map];

First, you set a background color to the scene, which will be the blue sky. The next two lines of code simply load the tile map (a JSTiledMap) and add it to the layer.

Next, in GameLevelScene.m, add the import for Player.h:

#import "Player.h"

Still in GameLevelScene.m, add the following property to the @interface section:

@property (nonatomic, strong) Player *player;

Then add the Koala to the level with the following code in initWithSize:

self.player = [[Player alloc] initWithImageNamed:@"koalio_stand"];
self.player.position = CGPointMake(100, 50);
self.player.zPosition = 15;
[self.map addChild:self.player];

This code loads the Koala sprite object, gives it a position, and adds it to the map object.

You may be wondering why you added the Koala to the map object instead of the scene. Well, you want to control exactly which TMX layers are in front of and behind the Koala sprite, so the Koala needs to be a child of the map. You want your hero Koalio in front, so you give him a zPosition of 15. Also, this makes it so that if you scroll the tile map, the Koala still stays in the same relative position within the tile map.

OK, let’s check it out! Build and run and you should see the following:

Player and Tilemap

It looks like a game, but Koalio is defying gravity! It’s time to kiss him goodbye and bring him down – with a physics engine :]

The Gravity of Koalio’s Situation

The gravity force is always on!

To build a physics simulation, you could write a complex set of branching logic that takes the Koala’s state into account and decides which forces to apply based on that state. But, this would quickly become very complicated — and it isn’t really how physics works. In the real world, gravity is always pulling things towards the earth. So you’ll add the constant force of gravity to the Koala in every frame.

Other forces don’t just switch on and off. In the real world, a force is applied to an object and the momentum continues to move the object through space until some other force acts on that object to change the momentum.

For example, a vertical force like a jump doesn’t turn gravity off; it momentarily overcomes gravity, but gravity slows the ascent, and ultimately brings the object back to the ground. Similarly, a force that pushes an object is countered by friction, which gradually slows down the object until it stops.

This is how you’ll model your physics engine. You won’t constantly check whether your Koala is on the ground and decide whether to apply gravity; gravity will always be applied in this world.

Playing God

I control all the forces!

The logic of your physics engine will mean that when a force is applied to an object, the object will continue to move until another force counteracts it. When Koalio walks off a ledge, he’ll continue to move down at an accelerating rate until he collides with something else. When you move Koalio, he won’t stop moving as soon as you stop applying force; friction will slow him down gradually until he stops.

As you proceed with your platform game, you’ll see that this logic will make it easier to handle complex situations, such as an ice floor where the Koala doesn’t stop on a dime, or a free-fall over a cliff. This model of cumulative forces will make for a fun, dynamic-feeling game.

It will also make the implementation easier, because you won’t have to constantly query the state of your objects – they will just follow the natural laws of your world and their behavior will emerge from the application of those laws!

Sometimes, you do get to play God! :]

The Law of the Land: CGPoints and Forces

Let’s define a few terms:

  • Velocity describes how fast an object is moving in a given direction.
  • Acceleration is the rate of change in velocity – how an object’s speed and direction change over time.
  • A force is an influence that causes a change in speed or direction.

In a physics simulation, a force applied to an object will accelerate that object to a certain velocity, and that object will continue moving at that velocity, until acted upon by another force. Velocity is a value that persists from one frame to the next and only changes by the application of new forces.

You’re going to be representing three things with CGPoint structures: velocity (speed), force/acceleration (change in speed), and position. There are two reasons for using CGPoint structures:

  1. They’re 2D. Velocity, force/acceleration, and position are all 2D values for a 2D game. “What?” you might say. “Gravity only acts in one dimension!” However, you could easily imagine a game with changing gravity where you’d need the second dimension. Think Super Mario Galaxy!
  2. It’s convenient. By using CGPoints, you can rely on the various well established functions that deal with CGPoints. In this tutorial, you’ll be using the SKTUtils library that was built by the Tutorial Team for our book iOS Games By Tutorials. You’ll be making heavy use of functions such as CGPointAdd (add two points), CGPointSubtract (subtract them), and CGPointMultiplyScalar (multiply a point by a float to scale it up or down). This will make your code much easier to write — and debug!

Your Koala object will have a velocity variable that will be acted upon in each frame by a number of forces, including gravity, forward/jumping forces supplied by the user, and friction, which will slow and stop the Koala.

In each frame, you’ll add all these forces together, and the accumulated force that results will be added to the previous velocity of the Koala object. That will give you the current velocity. The current velocity will be scaled down to match the fractional time amount between each frame, and finally, that scaled value will be used to move the Koala’s position for that frame.

Note: If any of this still sounds confusing, Daniel Shiffman wrote an excellent tutorial on vectors that explains the accumulation of forces structure that you’ll be using. The tutorial is designed for Processing, a language for creative designers similar to Java, but the concepts are applicable in any programming language. It’s a great and accessible read and I highly recommend you check it out!

Let’s start with gravity. Set up the run loop, where you’ll be applying these forces. In the GameLevelScene.m, add a new property that will help you keep track of the time that passes in between each frame, in the @interface section:

@property (nonatomic, assign) NSTimeInterval previousUpdateTime;

Then, add the following method:

//1
- (void)update:(NSTimeInterval)currentTime
{
  //2
  NSTimeInterval delta = currentTime - self.previousUpdateTime;
  //3
  if (delta > 0.02) {
    delta = 0.02;
  }
  //4
  self.previousUpdateTime = currentTime;
  //5
  [self.player update:delta];
}
  1. First, the update method is built into every SKScene object and all you need to do is implement it and every frame you will get called before the scene is rendered. It provides an NSTimerInterval value that represents the current timestamp of your program.
  2. By subtracting the current time from the last time stamp, you will get the delta time, or the interval, since the last time the method was called. This time interval will be used to scale movement and other forces (like gravity) in order to achieve smooth, consistent animations.
  3. Sometimes delta may spike. This occurs at the beginning of the game (for the first few frames as things are still being loaded into memory) and occasionally when something else happens on the device (like when a system notification comes in). By capping it at .02, you reduce the chance of getting a time step that is too large (which can result in the physics engine behaving in unexpected ways, like Koalio moving through an entire tile).
  4. Here you set the previousUpdateTime to the currentTime which will be used next frame to determine delta.
  5. Finally, you call a method on Player called update. You’ll implement that next.

Next open up Player.h and add modify it to look like the following:

#import <SpriteKit/SpriteKit.h>

@interface Player : SKSpriteNode
@property (nonatomic, assign) CGPoint velocity;
- (void)update:(NSTimeInterval)delta;
@end

Next add the implementation to Player.m:

#import "Player.h"
//1
#import "SKTUtils.h"

@implementation Player
//2
- (instancetype)initWithImageNamed:(NSString *)name {
  if (self == [super initWithImageNamed:name]) {
    self.velocity = CGPointMake(0.0, 0.0);
  }
  return self;
}

- (void)update:(NSTimeInterval)delta {
  //3
  CGPoint gravity = CGPointMake(0.0, -450.0);
  //4
  CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
  //5
  self.velocity = CGPointAdd(self.velocity, gravityStep);
  CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
  //6
  self.position = CGPointAdd(self.position, velocityStep);
}

@end

Let’s go through the above code section by section.

  1. First, you import SKUtils. You’ll need those CGPoint convenience methods frequently here.
  2. Next, you create a new initWithImageNamed method and initialize the velocity variable to 0.0.
  3. Here you declared the value of the gravity vector (vector meaning the change in position). For each second in time, you’re accelerating the velocity of the Koala 450 points towards the floor. If the Koala starts from a standstill, at the one second mark he’ll be moving at 450 points/second, at two seconds he’ll be moving at 900 points/second, and so forth. Clear enough!
  4. Here, you used the CGPointMulitplyScalar to scale the acceleration down to the size of the current time step. Recall that CGPointMulitplyScalar multiplies a CGPoint’s values by a float value, and returns the CGPoint result. Even when you’re faced with a variable frame rate, you’ll still get consistent acceleration.
  5. Here once you’ve calculated the gravity for the current step, you add it to your current velocity. With the new velocity you’ve calculated, you then got the velocity for a single timestep. Again, you’re doing these calculations in order to get consistent velocity, no matter what the frame rate is.
  6. Finally, with the velocity you calculated for this single step, you use the CGPointAdd function to get the updated position for the Koala.

Congratulations! You are well on your way to writing your first physics engine! Build and run now to see the result!

Gravity

Whoops — Koalio is falling through the floor! Let’s fix that up.

Jake Gundersen

Contributors

Jake Gundersen

Author

Over 300 content creators. Join our team.