
For many of us, Super Mario Brothers was the first game to truly fascinate with its gameplay.
The intuitive control of SMB and the great level design from Nintendo made it necessary to spend hours on end in the virtual universe of the plumber and his partner.
In this wonderful tutorial from
Jacob Gundersen, we will create our own platformer; but since the main character will be Koala, we will call our game “Super Koalio Brothers!”;]
Also, to simplify the mechanics, we forget about moving enemies. Instead, we will use spike blocks embedded in the floor. This will allow us to concentrate fully on the heart of the platformer - the physics engine.
Attention! Under the cut an incredible amount of translated text, images, code (code is not translated) and a guide to creating your own physics engine!
This tutorial in advance implies that you are familiar with the basics of Cocos2D programming. Otherwise, I strongly recommend that you first familiarize yourself with a couple of initial lessons on
Ray’s website .
')
Let's start
To get started, download the
starter project for this tutorial. Unzip it, open it in Xcode, run it. Something similar should appear on the emulator screen:

That's right - just a boring blank screen! :] We will fill it in full as you go through the tutorial.
All necessary pictures and sounds have already been added to the start project. Let's run through the contents of the project:
- Game Art. Includes a free package of game artov from Ray Vicki’s wife.
- Level map I drew a level map especially for you, starting from the first level in SMB.
- Great sound effects. After all, tutorial with raywenderlich.com! :]
- Subclass CCLayer. A class named GameLevelLayer that implements most of our physics engine. Although it is now empty as a cork. (Yes, this baby is waiting to be filled!)
- Subclass of CCSprite. A class named Player that contains Koala logic. Right now, our Koala is trying to fly away!
Fundamentals of physics engines
Platformers operate on the basis of physics engines, and in this tutorial we will write our own physics engine.
There are two reasons why we need to write our own engine, and not take the same Box2D or Chipmink:
- Detailed setting. To fully know the Zen platformer, you need to learn how to fully customize your engine.
- Simplicity. Box2D and Chipmunk have a lot of customizable functions that, by and large, are not useful to us. Yes, and there will be resources. And our own engine will eat exactly as much as we allow it.
The physics engine performs two main tasks:

- Simulates movement. The first task of the physics engine is to simulate opposing forces of gravity, movement, jumps and friction.
- Defines collisions. The second task is to determine the collisions between the player and other objects at the level.
For example, during the jump on our Koala acts force directed upwards. After some time, the force of gravity exceeds the force of the jump, which gives us the classical parabolic change of speed.
With the help of collision detection, we will stop our Koala every time she wants to go through the floor under the influence of gravity, and determine when our Koala stepped on spikes (ah!).
Let's see how this works in practice.
Creating a physics engine
In the physics engine that we will create, the Koala will have its variables describing the movements: speed, acceleration and position. Using these variables, each step of our program we will use the following algorithm:
- Is a jump or move action selected?
- If so, apply the force of a jump or movement to a koala.
- Also apply gravity force to koala.
- Calculate the resulting koala velocity.
- Apply the resulting speed to Koala and update its position.
- Check for Koala collisions with other objects.
- If a collision occurs, then either move the Koala to such a distance from the obstacle that collisions no longer occur; or damage poor Koale.

We will go through these actions every step of the program. In our game, gravity constantly causes the Koala to sink lower and lower through the floor, but the definition of collisions each time brings it back to a point above the floor. You can also use this feature to determine if Koala is touching the earth. If not, you can forbid the player to jump when the Koala is in a state of jumping or has just jumped off from an obstacle.
Items 1-5 occur within the Koala object. All the necessary information should be stored inside this object and it is quite logical to allow Koale to update its variables on its own.
However, when it comes to the 6th point - collision definitions - we need to take into account all the features of the level, such as: walls, floor, enemies and other dangers. Collision detection will be carried out every step of the program using GameLevelLayer - remember, this is a subclass of CCLayer, which will perform most physical tasks.
If we allow Koale to update her position with his own hand, then finally Koala will touch the wall or the floor. And GameLevelLayer will bring Koala back. And so again and again - what will make Koala look like it is vibrating. (Too much coffee in the morning, Koalio?)
And so, we will not allow Koale to update their state. Instead, we’ll add a new desiredPosition variable to Koala, which Koala will update. GameLevelLayer will check if the Koala can be moved to the desiredPosition. If so, then GameLevelLayer will update the status of the Koala.
All clear? Let's see how it looks in the code!
Download TMXTiledMap
I assume that you are familiar with how Tile Maps work. If not, then I advise you to read about them in
this tutorial .
Let's take a look at the level. Launch your
Tiled map editor (download if you have not done this before) and open
level1.tmx from your project folder. You will see the following:

If you look at the sidebar, you will see that we have three different layers:
- hazards: This layer contains things Koala must beware of in order to stay alive.
- walls: This layer contains cells that Koala cannot pass through. Basically it is the cell floor.
- background: This layer contains purely aesthetic things, such as clouds or hills.
It's time to code! Open
GameLevelLayer.m and add the following after
#import
, but before
@implementation
:
@interface GameLevelLayer() { CCTMXTiledMap *map; } @end
We added a local map variable of the class CCTMXTiledMap for working with mesh maps in our head class.
Next, we will place a mesh map on our layer right during layer initialization. Add the following to the
init method
: CCLayerColor *blueSky = [[CCLayerColor alloc] initWithColor:ccc4(100, 100, 250, 255)]; [self addChild:blueSky]; map = [[CCTMXTiledMap alloc] initWithTMXFile:@"level1.tmx"]; [self addChild:map];
First, we added a backdrop (CCLayerColor) blue sky color. The next two lines of code are simply loading the map variable (CCTMXTiledMap) and adding it to the layer.
Next, in
GameLevelLayer.m we import Player.h:
#import "Player.h"
Still in
GameLevelLayer.m, add the following local variable to the
@ interface
section:
Player * player;
Next, add Koala to the level with the following code in the
init method:
player = [[Player alloc] initWithFile:@"koalio_stand.png"]; player.position = ccp(100, 50); [map addChild:player z:15];
This code loads the Koala sprite object, sets its position and adds it to the object of our map.
You ask why add a koala object to the map instead of just adding it directly to the layer? It's simple. We want to directly control which layer will be in front of the Koala, and which one will be behind it. So we make Koala a baby card, not a main layer. We want the Koala to be in front, so we give it a Z-order of 15. Similarly, when we scroll the map, the Koala is still at the same position, relative to the map, and not the main layer.
Great, let's try! Run your project and you should see the following:

It looks like a game, but Koalio ignores gravity! It's time to lower it from heaven to earth - with the help of a physics engine:]
Situation with gravity Koalio

To create a simulation of physics, you can write a complex set of branching logic that would take into account the state of Koala and apply forces to it, building on the information received. But this world will immediately become too complicated - a real physics doesn’t work so hard. In the real world, gravity just constantly pulls objects down. So, we add constant gravity force and apply it to Koale every step of the program.
Other forces are also not just turned off and on. In the real world, force acts on an object until another force surpasses or is equal to the first.
For example, the force of a jump does not disable gravity; for some time, it exceeds the force of gravity, until gravity presses Koala to the ground again.
This is how physics is modeled. You do not just decide whether or not to apply the gravitational force to Koale. Gravity is always there.
Playing god

In the logic of our engine, it is assumed that if a force acts on an object, it will continue to move until another force exceeds the first one. When Koalio jumps off the ledge, he continues to move downward with a certain acceleration until he encounters an obstacle in his path. When we move a Koalio, it will not stop moving until we stop applying the force of movement on it; friction will act on Koalio until it stops.
As you create a physics engine, you will see how so simple game logic helps to solve complex physical problems, such as: an ice floor or a fall from a cliff. This behavioral model allows the game to change dynamically.
Also such a knight's move will allow us to make implementation easier, since we do not need to constantly ask the state of our object - the object will simply follow the laws of physics from the real world.
Sometimes we need to play god! :]
Laws of Planet Earth: CGPoint's and Forces
Let's denote the following concepts:
- Speed describes how fast an object moves in a certain direction.
- Acceleration describes how the speed and direction of an object changes over time.
- Strength is the influence that causes a change in speed or direction.
In a physical simulation, the force applied to an object will accelerate the object to a certain speed, and the object will move at that speed until it encounters another force on the way. Speed ​​is a value that changes from one frame to the next as new forces appear.
We will present three things using CGPoint structures: speed, force / acceleration and position. There are two reasons for using CGPoint structures:
- They are 2D. Speed, force / acceleration and position are all 2D values ​​for a 2D game. You can say that gravity acts only in one direction, but what if at one point in the game we urgently need to change the direction of gravity? Think of Super Mario Galaxy!
- It's comfortable. Using CGPoint, we can use various functions built into Cocos2D. In particular, we will use ccpAdd (addition), ccpSub (subtraction) and ccpMult (multiplication by a variable of type float). All this will make our code much easier to read and debug!
The object of our Koala will have a variable speed, which will vary according to the appearance of various forces, including gravity, movement, jumps, friction.
Each step of the game, we will add all the forces together, and the resulting value will be added to the current speed of the Koala. As a result, we will receive a new current speed. We will reduce it using the frame rate. After that we will move the Koala.
Please note: if any of the above is misleading, then a wonderful person, Daniel Shiffman, wrote a great tutorial on vectors that fully explains the actions of forces on the structures that we use.
Let's start with gravity. Let's write a run loop in which we will apply forces. Add the following code to the init method of the
GameLevelLayer.m file right before closing the conditional if block:
[self schedule:@selector(update:)];
Next, add a new method to the class:
- (void)update:(ccTime)dt { [player update:dt]; }
Next, open
Player.h and change it to look like this:
#import <Foundation/Foundation.h> #import "cocos2d.h" @interface Player : CCSprite @property (nonatomic, assign) CGPoint velocity; - (void)update:(ccTime)dt; @end
Add the following code to
Player.m :
Click me #import "Player.h" @implementation Player @synthesize velocity = _velocity; // 1 - (id)initWithFile:(NSString *)filename { if (self = [super initWithFile:filename]) { self.velocity = ccp(0.0, 0.0); } return self; } - (void)update:(ccTime)dt { // 2 CGPoint gravity = ccp(0.0, -450.0); // 3 CGPoint gravityStep = ccpMult(gravity, dt); // 4 self.velocity = ccpAdd(self.velocity, gravityStep); CGPoint stepVelocity = ccpMult(self.velocity, dt); // 5 self.position = ccpAdd(self.position, stepVelocity); } @end
Let's go through the code above step by step.
- Here we added a new init method to initialize the object and set the speed variable to zero.
- Here we denote the value of the gravity vector. Every second we accelerate the Koala's speed down 450 pixels.
- Here we used ccpMult in order to reduce the value of the gravitational vector to meet the frame rate. ccpMult gets float and CGPoint and returns CGPoint.
- Here, as soon as we calculate gravity for the current step, we add it to the current speed.
- Finally, when we calculated the speed for one step, we use ccpAdd to update the Koala position.
Congratulations! We are on the direct path to creating our first physics engine! Run your project to see the result!

Oooooooooops - Koalio falls through the floor! Let's fix it.
Night Strikes - Collision Detection
Collision detection is the basis of any physics engine. There are many different types of collision detection, from simple use of image frames to complex collisions of 3D objects. Fortunately for us, the platformer does not require complex structures.
To define Koala collisions with objects, we will use TMXTileMap for the cells that directly surround the Koala. Next, using several built-in iOS functions, we will check if the sprite Koalas crosses a sprite of a cell.
The CGRectIntersectsRect and CGRectIntersection functions make such checks very simple. CGRectIntersectsRect checks whether two rectangles intersect, and CGRectIntersection returns the intersection rectangle.
First, we need to define the frame of our Koala. Each downloaded sprite has a frame, which is the size of the texture and which can be accessed using a parameter named boundingBox.
Why define a frame if it already exists in a boundingBox? The texture usually has transparent edges around itself, which we don’t want to take into account when determining collisions.
Sometimes we don’t need to take into account even a couple of pixels around the real image of the sprite (not transparent). When Mario hits a wall, does he touch her a bit, or does his nose sink into the block slightly?
Let's try. Add to
Player.h :
-(CGRect)collisionBoundingBox;
And add to
Player.m :
- (CGRect)collisionBoundingBox { return CGRectInset(self.boundingBox, 2, 0); }
CGRectInset compresses CGRect by the number of pixels from the second and third arguments. In our case, the width of our collision frame will be six pixels less — three pixels on each side.
Weight lifting
It's time to lift weights. (“Hey, did you call me fat now?” Says Koalio).
We will need a number of methods in our GameLevelLayer to determine collisions. In particular:
- The method that returns the coordinates of the eight cells surrounding the current Koalio cell.
- A method that determines which cell is an obstacle (and whether there are any in general). Some cells do not have physical properties (clouds), and Koalio will not encounter them.
- A method that handles collisions in priority order.
We will create two helper functions that will simplify the methods described just above.
- A method that determines the position of a Koalio cell.
- A method that receives cell coordinates and returns a cell rectangle in Cocos2D coordinates.
Add the following code to
GameLevelLayer.m :
- (CGPoint)tileCoordForPosition:(CGPoint)position { float x = floor(position.x / map.tileSize.width); float levelHeightInPixels = map.mapSize.height * map.tileSize.height; float y = floor((levelHeightInPixels - position.y) / map.tileSize.height); return ccp(x, y); } - (CGRect)tileRectFromTileCoords:(CGPoint)tileCoords { float levelHeightInPixels = map.mapSize.height * map.tileSize.height; CGPoint origin = ccp(tileCoords.x * map.tileSize.width, levelHeightInPixels - ((tileCoords.y + 1) * map.tileSize.height)); return CGRectMake(origin.x, origin.y, map.tileSize.width, map.tileSize.height); }
The first method returns us the coordinates of the cell located on the coordinates in pixels that we pass to the method. To get the position of a cell, we simply divide the coordinates by the size of the cells.
We need to invert the elevation coordinates, since the coordinates of the Cocos2D / OpenGL system start at the bottom left corner, and the system coordinates start at the top left corner. Standards - isn't it cool?
The second method does the opposite. It multiplies the cell coordinate by the cell size and returns the CGRect of the cell. Again, we need to expand the height.
Why do we need to add one to the y-coordinate of the height? Remember, the cell coordinates start from zero, so the 20 cell has a real coordinate of 19. If we do not add one to the height, the point will be 19 * tileHeight.
I am surrounded by cells!
We now turn to the method that defines the surrounding Koala cells. In this method we will create an array, which we will return. This array will contain the GID of the cell, the coordinates of the cell and information about the CGRect of this cell.
We organize this array in order of priority, in which we will define collisions. For example, we want to define collisions from above, to the left, to the right, from below, before we determine the diagonal ones. Also, when we determine the collision of the Koala with the lower cell, we set the flag to touch the ground.
Add this method to
GameLevelLayer.m :
Click me - (NSArray *)getSurroundingTilesAtPosition:(CGPoint)position forLayer:(CCTMXLayer *)layer { CGPoint plPos = [self tileCoordForPosition:position];
Pff - a whole cloud of code. Do not worry, we will take a detailed look at it.
But before that, notice that we have three layers on our map.
The presence of different layers allows us to determine the collisions for each layer in different ways.
- Koala and hazards. If a collision occurs, then we kill the Koala (quite brutal, isn't it?).
- Koala and walls. If a collision occurs, we do not allow Koale to move further in this direction. “Stay mare!”
- Koala and backgrounds. If a collision occurs, then we do nothing. Lazy programmer is the best programmer. Well, or how there, the people say?
Of course, there are various ways to identify different collisions with different blocks, but what we have - the layers on the map, is quite effective.
Okay, let's go through the code step by step.
1. To begin with, we get the coordinates of the input cell (which will be the coordinates of the Koala).
2. Next, we create a new array that will return cell information.
3. Next, we start the cycle 9 times - since we have 9 possible movement cells, including the cell in which the koala is already located. The next few lines define the positions of the nine cells and store them in the variable tilePos.
Note: we only need information about eight cells, since we will never have to determine collisions with a cell on which the koala is already located.
We should always catch this case and move the Koala to one of the cells around. If Koalio is inside a solid cell, then more than half of the Koalio sprite has gone inside. He should not move so fast - at least in this game!
To make it easier to operate on these eight cells, simply add a Koalio cell at the beginning, and at the end remove it.
4. In the fourth section, we call the tileGIDAt: method. This method returns the GID of the cell at a specific coordinate. If there is no cell on the received coordinates, the method returns zero. In the following, we will use zero for the “no cell found”.
5. Next, we use an auxiliary method to calculate the CGRect for a cell on the given Cocos2D coordinates. We store the received information in NSDictionary. The method returns an array of the resulting NSDictionary.
6. In the sixth section, we remove the Koala cell from the array and sort the cells in priority order.

Often, in the case of determining collisions with a cell under the Koala, we also define collisions with cells along the diagonal. See the picture on the right. Defining collisions with a cell under the Koala, baked in red, we also determine collisions with block # 2, highlighted in blue.
Our collision detection algorithm will use some assumptions. These assumptions are correct for adjacent, rather than for diagonal cells. So we will try to avoid actions with diagonal cells as much as possible.
And here is a picture that clearly shows us the order of cells in the array before and after sorting. You may notice that the top, bottom, right and left cells are processed first. Knowing the order of the cells, it will be easier to determine when the Koala touches the ground or flies in the clouds.
7. The cycle in section seven allows us to monitor cells in real time. So, we can know for sure that everything is going according to plan.
We are almost ready for the next launch of our game! However, you still need to do a couple of things. We need to add the walls layer as a variable to the GameLevelLayer class so that we can use it.
Inside
GameLevelLayer.m, make the following changes:
Run! But, unfortunately, the game crashes. We see something in the console:

First we get information about cell positions and GID values ​​(although mostly zeros, since the top is empty terrain).
In the end, everything crashes with the error "TMXLayer: invalid position". This happens when a position is passed to the tileGIDat: method that is outside the edges of the map.
We will avoid this error a little later — but first, we are going to change the existing definition of collisions.
We select Koala's privileges back
Up to this point, Koala herself was updating her position. But now we are taking this privilege from her.

If Koala will independently update her position, then in the end she will start jumping like mad! And we don't want that, no?
So Koala requires an additional variable, desiredPosition, with which she will interact with GameLevelLayer.
We want the Koala class to independently calculate its next position. But GameLevelLayer should move the Koala to the desired position only after checking it for validity. The same applies to the collision loop - we don’t want to update the real sprite before all the cells have been checked for collisions.
We need to change a few things. First, add the following to
Player.h @property (nonatomic, assign) CGPoint desiredPosition;
And synthesize added in
Player.m :
@synthesize desiredPosition = _desiredPosition;
Now, change the
collisionBoundingBox method in
Player.m so that it looks like this:
- (CGRect)collisionBoundingBox { CGRect collisionBox = CGRectInset(self.boundingBox, 3, 0); CGPoint diff = ccpSub(self.desiredPosition, self.position); CGRect returnBoundingBox = CGRectOffset(collisionBox, diff.x, diff.y); return returnBoundingBox; }
This piece of code calculates a frame based on the desired position that GameLevelLayer will use to determine collisions.
Note: There are many different ways to calculate collision frames. You can write code similar to the one that already exists in the CCNode class, but our current method is much simpler, despite some non-obviousness.
Next, make the following changes to the update method so that it updates the desiredPosition instead of the current position:
Let's start defining collisions!
The time has come for serious accomplishments. We are going to put it all together. Add the following method to
GameLevelLayer.m :
Click me - (void)checkForAndResolveCollisions:(Player *)p { NSArray *tiles = [self getSurroundingTilesAtPosition:p.position forLayer:walls ];
Fine!
Let's take a look at the code we just wrote.1. First we get a set of cells surrounding the Koala. Next we cycle through each cell in this set. Every time we go through the cell, we check it for collisions. If a collision occurs, we change the desiredPosition of the Koala.2. Inside each loop of the loop, we first get the current Koala frame. Each time a collision is detected, the desiredPosition variable changes its value to one in which collisions no longer occur.3. The next step is to get the GID that we stored in the NSDictionary, which may be null. If the GID is zero, then the current loop ends and we go to the next cell.four.If a cell is in a new position, we need to get its CGRect. It may or may not have a collision. We carry out this process with the following line of code and save it to the tileRect variable. Now, having CGRect Koals and cells, we can check them for collisions.5. To check the cells for collisions, we run CGRectIntersectsRect. If a collision occurs, we get a CGRect that describes the CGRect intersection using the CGRectIntersection () function.Let's stop to think about the dilemma ...
Quite an interesting case. We need to figure out how to correctly identify collisions.You might think that the best way to move a koala is to move it in the opposite direction from a collision. Some physics engines do work on this principle, but we are going to apply the solution better.Think: gravity constantly pulls the Koala down into the cells below it, and these collisions happen all the time. If you imagine a Koala moving forward, then at the same time the Koala still pulls down with gravity. If we solve this problem by simply changing the movement in the opposite direction, the Koala will move up and to the left - and in fact we need something else!Our Koala should move a sufficient distance to still remain above these cells, but continue to move forward at the same pace.
The same problem will occur if Koala slides down the wall. If the player presses the Koala against the wall, then the desired trajectory of the movement of the Koala will be directed diagonally downwards and into the wall. Just turning the direction, we will make Koala move up and away from the wall - again, not at all! We want Koala to remain outside the wall, but still go down at the same pace!
So, we need to decide when to deal with collisions vertically, and when horizontally, and to process both actions mutually exclusive. Some physics engines constantly process the first event first, and then the second one; but we want to make a better decision based on the position of the Koala cell. So, for example, when the cell is right under the Koala, we want the collision determiner to return the Koala up.And what if the cell is diagonal to the Koala position? In this case, we use the CGRect intersection to understand how we should move the Koala. If the width of this rectangle is greater than the height, then Koala should be returned vertically. If the height is greater than the width, then the Koala should shift horizontally.
This process will work correctly as long as the Koala's speed and frame rate are within a certain range. A little later we will learn to avoid cases when the Koala falls too fast and slips down through the cell.Once we have determined how to move the Koala vertically or horizontally, we use the size of the CGRect intersection to determine how much the Koala needs to be removed. We look at the width or height, respectively, and use this value as the Koala offset distance.Why check the cells in a certain order? You always need to first work with adjacent cells, and then with diagonal. After all, if you want to check for a collision cell to the right below the Koala, then the displacement vector will be directed vertically.
However, there is still a chance that the CGRect of the collision will be elongated upwards when the Koala slightly touches the cell.Look at the picture on the right. The blue area extends upward, because the collision rectangle is only a small part of the overall collision. However, if we have already solved the problem with the cell right under the Koala, then we no longer need to determine collisions with the cell below to the right of the Koala. So we go around the problems that have appeared.Back to the code!
Let’s return to the checkForAndResolveCollisions: ... method6. The sixth section allows us to get the index of the current cell. We use cell index to get cell position. We are going to operate on adjacent cells individually, displacing the Koala, subtracting or adding the length or height of the collision. Pretty simple.
However, as soon as it comes to diagonal cells, we are going to apply the algorithm described in the previous section.7. In the seventh section, we determine which area of ​​our collision is: wide or upward? If wide - we work vertically. If the cell index is greater than 5, then we move the Koala up. If the area is extended upwards - we work horizontally. We act according to a similar order of cell indexes. At the end, we assign Koale the resulting position.This method is the brain of our collision detection system.Let's use all the available knowledge in practice! Change the method of the update (still in GameLevelLayer: )
You can also delete or comment out the getSurroundingTilesAtPosition: forLayer block:
Run! Surprised by the result?
Paul stops Koalio, but he immediately sinks into it! Why?
Can you guess what we missed? Remember - every step of the game we add gravity to the speed of the Koala. This means that the Koala is constantly accelerating down.We constantly add speed to the Koala's trajectory down until it becomes the size of a cell — we move through the whole cell in one step, which causes problems (remember, we spoke about this recently).As soon as we detect the collision, we need to reset the speed of the koala in the direction of the cell we are facing! The koala has stopped moving, so the speed must be reckoned with.If we do not do this, then we will have a rather strange behavior of the game. As we noted earlier, we need a way to determine if the koala touches the ground so that the koala cannot jump even higher. We will check this box right now. Add the following lines to checkForAndResolveCollisions:Click me - (void)checkForAndResolveCollisions:(Player *)p { NSArray *tiles = [self getSurroundingTilesAtPosition:p.position forLayer:walls ];
Every time there is a cell under Koala (either adjacent or diagonal), we set the value of the p.onGround variable to YES and reset the speed. Also, if there is an adjacent cell under the Koala, we reset its speed. This will allow us to properly respond to the current Koala speed.We set the onGround variable to NO at the beginning of the cycle. In this case, onGround will have a YES value only when we detect a Koala collision with a cell under it. We can use this feature to determine whether Koala can jump or not at the current time.Add the following code to the heading file (and then synthesize everything you need in the executive) in Player.h : @property (nonatomic, assign) BOOL onGround;
And in Player.m : @synthesize onGround = _onGround;
Run! Does everything work as intended? Yes!
Oh, this great day! Hooray!

What's next?
Congratulations!
You have completely finished your physics engine! If you get to this text, you can breathe a sigh of relief. It was a difficult part - there will be nothing difficult in the second part of the tutorial.And here is the source of the project that we have now completed.In the second part, we will make our Koalio run and jump. We will also make spike blocks in the floor dangerous for our Koala and create screens of win and loss.If you want to gain even more knowledge about the physics engines for platformers, then I advise you to visit the following resources:The Sonic the Hedgehog Wiki is a great explanation of how Sonic interacts with solid cells .Perhaps the bestHigher-Order Fun Platform Tutorial
Translator's Note
I decided to translate this tutorial, inspired by this article .I myself am writing a platformer game on iOS and actively use tutorials on raywenderlich.com . I advise everyone!After a relatively large amount of time spent translating the first part, I thought about translating the second part. Write in the comments if you need. If it is claimed - I will translate.About all inaccuracies and typographical errors found, please write in habraposhta or here in the comments.
We are happy to answer all questions on the tutorial!