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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Sprite Kit Tutorial: How to Make a Platform Game Like Super Mario Brothers – Part 1
55 mins
- Getting Started
- The Tao of Physics Engines
- Physics Engineering
- Loading the TMXTiledMap
- The Gravity of Koalio’s Situation
- Playing God
- The Law of the Land: CGPoints and Forces
- Bumps In The Night – Collision Detection
- Heavy Lifting
- I’m Surrounded By Tiles!
- Taking Away Your Koala’s Privileges
- Let’s Resolve Some Collisions!
- Pausing to Consider a Dilemma...
- Back to the Code!
- Where to Go From Here?
Back to the Code!
Returning to the monster checkForAndResolveCollisionsForPlayer:forLayer:
method…
- In section 2 you use the index to determine the position of the tile. You are going to deal with the adjacent tiles individually, moving the Koala by subtracting or adding the width or height of the collision as appropriate. Simple enough. However, once you get to the diagonal tiles, you're going to implement the algorithm described in the previous section.
- In section 3, you determine whether the collision is wide or tall. If it's wide, you resolve vertically.
- Next you create a variable to hold the value of the distance that you need to move the Player to no longer be in a collision state with the tile.
- You'll either be moving the Koala up or down, which you determine next by seeing if the tile index is greater than five (tile indices 6 and 8 are both beneath). Based on that, you know whether you need to add or subtract the collision height from the Koala. The horizontal collision resolution follows the same logic.
- Finally, you set the position of the Koala to the final collision-detection resolved result.
That method is the guts of your collision detection system. It's a basic system, and you may find that if your game moves very quickly or has other goals, you need to alter it to get consistent results. At the end of this article, there are a couple of really great resources with more about how to handle collision detection.
Let's put it to use! You can remove or comment out the log statement in the checkForAndResolveCollisionsForPlayer:forLayer:
//NSLog(@"GID %ld, Tile Coord %@, Tile Rect %@, player rect %@", (long)gid, NSStringFromCGPoint(tileCoord), NSStringFromCGRect(tileRect), NSStringFromCGRect(playerRect));
Build and run. Are you surprised by the results?
Koalio is stopped by the floor, but sinks into it eventually! What gives?
Can you guess what’s been missed? Remember — you are adding the force of gravity to the Koala’s velocity with each frame. This means that Koalio is constantly accelerating downward.
Here's the solution: You are constantly increasing the speed of the Koala's downward trajectory until it is greater than the size of the tile — you’re moving through an entire tile in a single frame, which was a problem discussed earlier.
When you resolve a collision, you also need to reset the velocity of the Koala to zero for that dimension! The Koala has stopped moving, so the velocity value should reflect it.
If you don't do this, you'll get weird behaviors, both moving through tiles as you saw above, and also in situations where your Koala jumps into a low ceiling and he floats against it longer than he should. This is the kind of weirdness you want to avoid in your game.
It was mentioned before that you need a good way to determine when the Koala is on the ground so he can't jump off of thin air. You'll set that flag up now. Add the indicated lines to the checkForAndResolveCollisionsForPlayer:forLayer:
- (void)checkForAndResolveCollisionsForPlayer:(Player *)player forLayer:(TMXLayer *)layer
{
NSInteger indices[8] = {7, 1, 3, 5, 0, 2, 6, 8};
player.onGround = NO; ////Here
for (NSUInteger i = 0; i < 8; i++) {
NSInteger tileIndex = indices[i];
CGRect playerRect = [player collisionBoundingBox];
CGPoint playerCoord = [layer coordForPoint:player.desiredPosition];
NSInteger tileColumn = tileIndex % 3;
NSInteger tileRow = tileIndex / 3;
CGPoint tileCoord = CGPointMake(playerCoord.x + (tileColumn - 1), playerCoord.y + (tileRow - 1));
NSInteger gid = [self tileGIDAtTileCoord:tileCoord forLayer:layer];
if (gid != 0) {
CGRect tileRect = [self tileRectFromTileCoords:tileCoord];
//NSLog(@"GID %ld, Tile Coord %@, Tile Rect %@, player rect %@", (long)gid, NSStringFromCGPoint(tileCoord), NSStringFromCGRect(tileRect), NSStringFromCGRect(playerRect));
//1
if (CGRectIntersectsRect(playerRect, tileRect)) {
CGRect intersection = CGRectIntersection(playerRect, tileRect);
//2
if (tileIndex == 7) {
//tile is directly below Koala
player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height);
player.velocity = CGPointMake(player.velocity.x, 0.0); ////Here
player.onGround = YES; ////Here
} else if (tileIndex == 1) {
//tile is directly above Koala
player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y - intersection.size.height);
} else if (tileIndex == 3) {
//tile is left of Koala
player.desiredPosition = CGPointMake(player.desiredPosition.x + intersection.size.width, player.desiredPosition.y);
} else if (tileIndex == 5) {
//tile is right of Koala
player.desiredPosition = CGPointMake(player.desiredPosition.x - intersection.size.width, player.desiredPosition.y);
//3
} else {
if (intersection.size.width > intersection.size.height) {
//tile is diagonal, but resolving collision vertically
//4
player.velocity = CGPointMake(player.velocity.x, 0.0); ////Here
float intersectionHeight;
if (tileIndex > 4) {
intersectionHeight = intersection.size.height;
player.onGround = YES; ////Here
} else {
intersectionHeight = -intersection.size.height;
}
player.desiredPosition = CGPointMake(player.desiredPosition.x, player.desiredPosition.y + intersection.size.height );
} else {
//tile is diagonal, but resolving horizontally
float intersectionWidth;
if (tileIndex == 6 || tileIndex == 0) {
intersectionWidth = intersection.size.width;
} else {
intersectionWidth = -intersection.size.width;
}
//5
player.desiredPosition = CGPointMake(player.desiredPosition.x + intersectionWidth, player.desiredPosition.y);
}
}
}
}
}
//6
player.position = player.desiredPosition;
}
Each time the Koala has a tile under him (either adjacently or diagonally) you set the player.onGround
to YES
and set his velocity to zero. Also, if the Koala has a tile adjacently above him, you set his velocity to zero. This will make the velocity variable properly reflect the Koala’s actual movement and speed.
You set the onGround
flag to NO
at the beginning of the loop. This way, the only time the onGround
returns a YES
is if you have detected a collision underneath the Koala. You can use this to know when the Koala can and cannot jump. You need to add this property to the Koala class, so do that now.
Add the following property to the header file in Player.h:
@property (nonatomic, assign) BOOL onGround;
Build and run. Is it working as expected? Yes! O frabjous day! Callooh! Callay!
Where to Go From Here?
Congratulations! You've built yourself a working physics engine! If you're through to this point, you can breathe a sigh of relief and pat yourself on the back. That was the hard part – nothing but smooth sailing in Part 2 of this tutorial!
Here's the complete project you have built so far.
In Part 2, you'll make your hero Koalio run and jump. You'll also make the spikes on the floor dangerous, and handling winning/losing the game.
If you want more information about platformers and physics engines, here are a few resources I recommend:
- The Sonic the Hedgehog Wiki has a great section describing how Sonic interacts with solid tiles.
- Perhaps the best guide to implementing platformers, from Higher-Order Fun.
- The creators of N have a great tutorial that goes well beyond the basics of tile-based collision systems.
- Our very own Platformer Game Starter Kit, which is similar to this series but goes into much more detail, to create a complete game with enemies, double jumps, wall slides, multiple levels, and much more.
Let me know how it’s going so far by submitting your comments to the forums!