Program for creating levels in mario play. Super Mario: new levels. Taking Koala's privileges back

Description of the flash game

Super Mario is the favorite game of many players. After all, it was created a very, very long time ago. It has already been passed a large number of times, and it remains one of the most beloved and popular games on the game console Dandy. Subsequently, a lot of Mario games were released, very different. But today, you have the opportunity to play the continuation of the cult series. Now Mario has new levels that urgently require your passage. When you enter the game, you will see a native and familiar character who has been waiting for you to return to the game for a long time. In the world of Mario, too, everything remains the same, various creatures wish him death, but he does not give up and moves forward, while collecting gold coins. There are a lot of dangerous places on the levels where you can lose your life and start over, so go through them with extreme care. New levels are just as interesting as the old ones, because they tell the continuation of the story. After passing them, you will find out what happened to the character and his friends after the events in the first part of the game. Already interested? Then run to play! And help your favorite character cope with the army of enemy humanoids.

Description of the flash game

Want to create your own game in the universe of everyone's favorite Mario game? Then let's get started. This is a full-fledged game editor that will allow you to create your own set of levels, storyline, as well as the final battle with any boss. In other words, create your own game. First, come up with a plot. For example, Mario goes on an adventure again. Color the game locations as you like. It can be forests, deserts, tropical villages and fields. The main thing is that they were colorful and interesting. Then work through game card. Add more obstacles and game objects to make it more fun to play. Don't forget about the enemies. They also need to be placed on the map so that the game is not so easy, the higher the level, the more powerful the enemies. Determine how many points the character will receive for killing a certain monster. A little more and the game will be ready. Now let's move on to the most important thing - the boss. It must be very powerful for the player to work hard to defeat him. You can equip it with a weapon, or additional skills. Next to it, place a few items that can be used in battle, such as rocks or burning torches. In such a game, it will be very interesting to play many fans of Mario!

Welcome to Mario Maker - play online with free super level editor in Russian! Learn how to complete all the levels by yourself and watch how Mario's character changes as the game progresses. A little advice before starting: play in full screen, it's more convenient to control.

Here is a unique edition of the Mario game: one-on-one adventures with the ability to continue the adventure on new maps. Start by choosing one of two modes: " New game' or 'Editor'.

Let's start with main feature Mario Maker games: opportunities to make your own own cards levels using the editor screen as a canvas.

How to make levels in Mario Maker

The editor interface is very user-friendly and intuitive. The playing field is marked with a grid, below it there are buttons for tools, categories of objects and selection of the map size.

The small map of the level is designed to pass full screen without scrolling, medium and large - for complex and long levels.

All game objects are placed in blocks. To put obstacles, bonuses, enemies and other elements of the game on the field, you need to:

  • mark a place for the block with the mouse (or tap if you play on a phone or tablet);
  • click on the button of the required element;
  • use the eraser tool (transparent cell) if you want to delete an item;
  • check the box to finish the level.

Don't forget about bricks or another base to play through, otherwise Mario will fall into the abyss before he even starts playing! Save your map and if you want to start the passage, go from the main menu by clicking on the "Saved Levels" button.

How to play mario rpg

Help the hero get to the end of the level by jumping over pits and obstacles, bypassing enemies and collecting bonuses. All upgrades are obtained by hitting a secret block with a question mark, they usually hang in the air and contain:

  • additional coins;
  • super mushrooms that turn ordinary Mario into Super Mario;
  • fire flowers give the power of fire, increasing running speed and jump height;
  • ice flowers, when plucked, allow you to freeze enemies.

Mario characters

The main character has 4 degrees of transformation:

  • classic Mario - the weakest form of the character, can easily lose a life;
  • Super Mario is twice the size of the classic, can resist the enemy without losing life, but touching the enemy turns into a small form;
  • Fire or Ice Mario is played with the superpowers of fire and ice;
  • an invincible character gains a temporary invincibility enchantment after touching a super star.

By picking a fire or ice flower, Mario changes color and can attack enemies with balls. fireballs bounce and can defeat almost all enemies from a distance. Ice - roll and freeze the enemy.

Now you know how to play Mario. Stayed last secret: At the end of each level there is a flagpole, from which the character removes the flag and completes the level, happily waving. Keep in mind that the higher the place where you hit the flagpole, the more points your hero will receive. Have a good game!

To get started, download the starter project for this tutorial. Unpack it, open it in Xcode, run it. Something like this should appear on the emulator screen:

That's right - just a boring blank screen! :] We will completely fill it in as we go through the tutorial
All the necessary pictures and sounds have already been added to the starting project. Let's go through the contents of the project:

  • Game art. Includes a free game art pack from Ray's wife Vicki.
  • Level map. I drew a level map especially for you, starting from the first level in SMB.
  • Great sound effects. After all, a tutorial from raywenderlich.com! :]
  • Subclass of CCLayer. A class named GameLevelLayer that implements b O most of our physics engine. Although now it is empty as a cork. (Yes, this baby is just waiting to be filled!)
  • Subclass of CCSprite. A class named Player that contains the Koala logic. Right now, our Koala is trying to fly away into the distance!

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:
  1. Detailed setting. To fully experience the Zen of platforming, you need to learn how to fully customize your engine.
  2. Simplicity. Box2D and Chipmunk have a lot of custom features that we don't really need. And yes, there will be resources. And our own engine will eat exactly as much as we let it.
The physics engine performs two main tasks:
  1. Simulates movement. The first job of a physics engine is to simulate the counter forces of gravity, movement, jumping, and friction.
  2. Defines collisions. The second task is to detect collisions between the player and other objects in the level.
For example, during a jump, an upward force acts on our Koala. After some time, the force of gravity overcomes the force of the jump, which gives us the classic parabolic change in 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 the spikes (ouch!).
Let's see how this works in practice.

Building a physics engine

In the physics engine that we will create, the Koala will have its own variables that describe the movements: speed, acceleration and position. Using these variables, each step of our program we will use the following algorithm:
  1. Is the jump or move action selected?
  2. If so, apply the power of the jump or movement to the Koala.
  3. Also, apply the force of gravity to the Koala.
  4. Calculate the resulting speed of the Koala.
  5. Apply the obtained speed to the Koala and update its position.
  6. Check for collisions of the Koala with other objects.
  7. If a collision occurs, then either move the Koala to such a distance from the obstacle that collisions no longer occur; or damage the poor Koala.

We will go through these steps every step of the program. In our game, gravity keeps Koala sinking lower and lower through the floor, but collision detection brings her back to a point above the floor each time. You can also use this feature to determine if the Koala is touching the ground. If not, it is possible to prevent the player from jumping when the Koala is in a jump state or has just jumped off some obstacle.
Items 1-5 occur inside the Koala object. All the necessary information should be stored inside this object and it is quite logical to allow Koala to update her variables herself.
However, when it comes to the 6th point - collision detection - we need to take into account all the features of the level, such as walls, floors, enemies and other dangers. Collision detection will be done every step of the program using GameLevelLayer - remember, this is a subclass of CCLayer that will handle most of the physical tasks.
If we allow the Koala to update its position by itself, then eventually the Koala will touch the wall or the floor. And GameLevelLayer will bring Koala back. And so again and again - which will make the Koala look like she is vibrating. (Too much coffee in the morning, Coalio?)
And so, we will not allow Koala to update his state. Instead, we will add a new desiredPosition variable to Koala, which Koala will update. The GameLevelLayer will check if the Koala can be moved to the desiredPosition. If so, the GameLevelLayer will update the state of the Koala.
All clear? Let's see what it looks like in code!

Loading TMXTiledMap

I'm assuming you're familiar with how Tile Maps work. If not, then I advise you to read about them in.
Let's take a look at the level. Launch your Tiled map editor (download if you haven't already) and open level1.tmx from your project folder. You will see the following:

If you look at sidebar, you will see that we have three different layers:

  • hazards: This layer contains the things the Koala must watch out for in order to stay alive.
  • walls: This layer contains cells that the Koala cannot pass through. Basically, these are floor cells.
  • background: This layer contains purely aesthetic things like clouds or mounds.
It's time to code! Open GameLevelLayer.m and add the following after #import but before @implementation:

@interface GameLevelLayer() ( CCTMXTiledMap *map; ) @end
We have added a local variable map of the CCTMXTiledMap class to work with cellular maps in our head class.
Next, we'll place a mesh map on our layer right during the layer's initialization. Add the following to the method init:

CCLayerColor *blueSky = [ initWithColor:ccc4(100, 100, 250, 255)]; ; map = [ initWithTMXFile:@"level1.tmx"]; ;
First, we added a sky blue background (CCLayerColor). The next two lines of code are just loading the map variable (CCTMXTiledMap) and adding it to the layer.

#import "Player.h"
Still in GameLevelLayer.m add the following local variable to the @ interface section:

Player = [ initWithFile:@"koalio_stand.png"]; player.position = ccp(100, 50); ;
This code loads a Koala sprite object, sets its position, and adds it to our map object.
Why add a koala object to the map, you ask, instead of just adding it directly to the layer? Everything is simple. We want to directly control which layer is in front of the Koala and which is behind it. So we make Koala the child of the map, not the main layer. We want the Koala to be in the front, so we give her a Z-order of 15. Also, when we scroll the map, the Koala is still in the same position relative to the map, not the main layer.
Great, let's try! Run your project and you should see the following:

Looks like a game, but Coalio ignores gravity! It's time to bring it down from heaven to earth - with the help of a physics engine:]

The Coalio Gravity Situation


To create a simulation of physics, you can write a complex set of branching logic that would take into account the state of the Koala and apply forces to it, starting from the information received. But this world will immediately become too complicated - and real physics doesn't work that hard. V real world gravity is just constantly pulling objects down. So, we add a constant force of gravity and apply it to Koala every step of the program.
Other powers don't just turn off and on either. In the real world, a force acts on an object until another force is greater than or equal to the first.
For example, the force of a jump does not disable gravity; it surpasses the force of gravity for some time, until gravity again presses the Koala to the ground.
This is how physics is modeled. You don't just decide whether or not to apply gravity to the Koala. Gravity is always there.

We play god


The logic of our engine is that if a force acts on an object, then it will continue to move until another force surpasses the first one. When Koalio jumps off a ledge, he continues to move down with a certain acceleration until he encounters an obstacle in his path. When we move Coalio, it will not stop moving until we stop using the movement force on it; friction will act on Coalio until he stops.
As the physics engine builds, you'll see how such a simple game logic can help you solve complex physics problems like an ice floor or falling off a cliff. This behavioral model allows the game to change dynamically.
Also, such a knight's move will allow us to make the 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: CGPoints and Forces

Let's define the following concepts:
  • Speed describes how fast an object is moving in a particular direction.
  • Acceleration describes how the speed and direction of an object change over time.
  • Power is the influence that causes a change in speed or direction.
In a physics simulation, a 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 along the way. Velocity is a quantity that changes from one frame to the next as new acting forces emerge.
We will represent three things with CGPoint structures: velocity, force/acceleration, and position. There are two reasons for using CGPoint structures:
  1. They are 2D. Velocity, force/acceleration and position are all 2D quantities for a 2D game. You can say that gravity only works in one direction, but what if at some point in the game we urgently need to change the direction of gravity? think about super mario Galaxy!
  2. It's comfortable. Using CGPoint, we can take advantage of the various functions built into Cocos2D. In particular, we will use ccpAdd (addition), ccpSub (subtraction), and ccpMult (float multiplication). All this will make our code much more readable and debugging!
Our Koala object will have a variable speed that will change with the appearance of various forces, including gravity, movement, jumping, friction.
Each step of the game, we will add all the forces together, and the resulting value will be added to the Koala's current speed. As a result, we will get a new current speed. We will reduce it using the frame rate. After that, we will move the Koala.
Let's start with gravity. Let's write a run loop in which we will apply forces. Add to file init method GameLevelLayer.m the following code right before the closing if conditional block:

;
Next, add a new method to the class:

- (void)update:(ccTime)dt ( ; )
Next, open Player.h and change it to look like this:

#import #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 = ) ( 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
  1. Here we have added a new init method to initialize the object and set the speed variable to zero.
  2. Here we have denoted the value of the gravity vector. Every second we speed up Koala's speed by 450 pixels down.
  3. Here we have used ccpMult to reduce the value of the gravity vector to suit the frame rate. ccpMult takes a float and a CGPoint and returns a CGPoint.
  4. Here, once we have calculated the gravity for the current step, we add it to the current speed.
  5. Finally, once we've calculated the speed for one step, we use ccpAdd to update the Koala's position.
Congratulations! We are on our way to building our first physics engine! Run your project to see the result!

Whoooooops - Coalio falls through the floor! Let's fix this.

Bumps in the Night - Collision Detection

Collision detection is the backbone of any physics engine. There are many different kinds of collision detection, from the simple use of image frames to complex 3D object collisions. Lucky for us, platforming doesn't require complicated structures.
To determine Koala collisions with objects, we will use TMXTileMap on cells that directly surround Koala. Next, using a few built-in iOS functions, we'll check to see if Koala's sprite intersects any cell's sprite.
The CGRectIntersectsRect and CGRectIntersection functions make these checks very easy. CGRectIntersectsRect checks if two rectangles intersect, and CGRectIntersection returns the intersection rectangle.
First, we need to define the frame of our Koala. Each loaded sprite has a border, which is the size of the texture, and can be accessed with a parameter called boundingBox.
Why define a frame if it's already in the boundingBox? A texture usually has transparent edges around it, which we don't want to take into account when detecting collisions.
Sometimes we don't even need to take into account a couple of pixels around real picture sprite (not transparent). When mario hits a wall, does he touch it a little, or does his nose sink into the block a little?
Let's try. Add to Player.h:

-(CGRect)collisionBoundingBox;
And add to player.m:

- (CGRect)collisionBoundingBox ( return CGRectInset(self.boundingBox, 2, 0); )
CGRectInset shrinks CGRect by the number of pixels from the second and third arguments. In our case, the width of our collision box will be six pixels less - three pixels on each side.

weight lifting

It's time to lift weights. (“Hey, are you calling me fat right now?” Coalio says).
We will need a number of methods in our GameLevelLayer to detect collisions. In particular:
  • A method that returns the coordinates of the eight cells surrounding the current Coalio cell.
  • A method that determines which of the cells is an obstacle (and whether there are any in general). Some cells do not have physical properties (clouds) and Coalio will not collide with them.
  • A method that handles collisions in priority order.
We will create two helper functions that simplify the methods described just above.
  • A method that determines the position of a Coalio cell.
  • A method that receives cell coordinates and returns the cell's 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 at the pixel coordinates 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 height coordinates because Cocos2D/OpenGL system coordinates start from the bottom left corner and system coordinates start from the top left corner. Standards - isn't that cool?
The second method does the opposite. It multiplies the cell coordinate by the cell size and returns the CGRect of the given cell. Again, we need to rotate the height.
Why do we need to add one to the y-coordinate of the height? Remember, cell coordinates start at zero, so cell 20 has a real coordinate of 19. If we don't add one to the height, the point will be 19 * tileHeight.

I'm surrounded by cells!

Now let's move on to the method that determines the cells surrounding the Koala. In this method, we will create an array, which we will return. This array will contain the cell's GID, the cell's coordinates, and the cell's CGRect information.
We organize this array in the order of precedence in which we will define collisions. For example, we want to detect top, left, right, bottom collisions before defining diagonal ones. Also, when we detect a Koala hitting the bottom cell, we set the ground touch flag.
Let's add this method to GameLevelLayer.m:

Click me

- (NSArray *)getSurroundingTilesAtPosition:(CGPoint)position forLayer:(CCTMXLayer *)layer ( CGPoint plPos = ; //1 NSMutableArray *gids = ; //2 for (int i = 0; i< 9; i++) { //3 int c = i % 3; int r = (int)(i / 3); CGPoint tilePos = ccp(plPos.x + (c - 1), plPos.y + (r - 1)); int tgid = ; //4 CGRect tileRect = ; //5 NSDictionary *tileDict = , @"gid", , @"x", , @"y", ,@"tilePos", nil]; ; } ; atIndex:6]; ; ; ; //6 for (NSDictionary *d in gids) { NSLog(@"%@", d); } //7 return (NSArray *)gids; }


Pff - a whole cloud of code. Don't worry, we'll go through it in detail.
But before that, notice that we have three layers on our map.
Having different layers allows us to define collisions differently for each layer.
  • Koala and hazards. If there was a collision, then we kill the Koala (brutal enough, right?).
  • Koala and walls. If there was a collision, then we do not allow the Koala to move further in this direction. "Stop, mare!"
  • Koala and backgrounds. If there is a collision, then we do nothing. A lazy programmer is a better programmer. Or, as the people say?
Of course, there are different ways to define different collisions with different blocks, but what we have - 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 cell for input (which will be the coordinates of the Koala).
2. Next, we create a new array that will return information about the cell.
3. Next, we run the loop 9 times - since we have 9 possible move cells, including the cell that the koala is already in. The next few lines define the positions of nine cells and store them in the tilePos variable.

Note: we only need information about eight cells, since we never have to detect collisions with a cell that the koala is already on.
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 Koalio's sprite is inside. He shouldn't be moving that fast - at least not in this game!
To make it easier to operate on these eight cells, we simply add a Koalio cell at the beginning and delete it at the end.

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 at the received coordinates, the method returns zero. Next, we will use zero in the value "no cell found".
5. Next, we use a helper method to calculate the CGRect for a cell given Cocos2D coordinates. We save the received information in NSDictionary. The method returns an array of the received 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 detecting collisions with a cell under the Koala, we also define collisions with cells diagonally. See picture on the right. By detecting collisions with the cell under the Koala, highlighted in red, we are also detecting collisions with block #2, highlighted in blue.
Our collision detection algorithm will use some assumptions. These assumptions hold true for adjoining rather than diagonal cells. So we will try to avoid dealing with diagonal cells as much as possible.
And here is a picture that clearly shows us the order of the cells in the array before and after sorting. You can notice that the top, bottom, right, and left cells are processed first. Knowing the order of the cells, it will be easier for you to determine when the Koala is touching the ground or flying in the clouds.

7. The loop in section seven allows us to monitor the 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, there are still a couple of things to do. 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:

// Add to @interface CCTMXLayer *walls; // Add to the init method, after the map is added to the layer walls = ; // add to the update method;
Run! But unfortunately the game crashes. We see something like this in the console:

First, we get information about the positions of the cells and the GID values ​​​​(although mostly zeros, since there is empty area above).
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'll avoid this error in a bit - but first, we're going to change the existing collision definition.

Taking Koala's privileges back

Up to this point, the Koala itself updated its position. But now we're taking that privilege away from her.

If the Koala will update its position on its own, then in the end it will start jumping like crazy! But we don't want that, do we?
So Koala requires an additional desiredPosition variable to interact with the GameLevelLayer.
We want the Koala class to calculate its next position on its own. But the GameLevelLayer should move the Koala to the desired position only after checking it for validity. The same applies to the collision detection loop - we don't want to update the real sprite before all 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 the added in player.m:

@synthesize desiredPosition = _desiredPosition;
Now, change the method collisionBoundingBox v 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 the border based on the desired position, which the GameLevelLayer will use to detect collisions.

Note: There are many various ways collision frames calculation. You can write code similar to what's already in the CCNode class, but our current way is much simpler, despite being somewhat non-obvious.
Next, make the following changes to the update method so that it updates the desiredPosition instead of the current position:

// Replace "self.position = ccpAdd(self.position, stepVelocity);" to: self.desiredPosition = ccpAdd(self.position, stepVelocity);

Let's start detecting collisions!

It's time for big things to happen. We're going to put everything together. Add the following method to GameLevelLayer.m:

Click me

- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *tiles = ; //1 for (NSDictionary *dic in tiles) ( CGRect pRect = ; //2 int gid = [ intValue]; //3 if (gid) ( CGRect tileRect = CGRectMake([ floatValue], [ floatValue], map.tileSize.width, map.tileSize.height); //4 if (CGRectIntersectsRect(pRect, tileRect)) ( CGRect intersection = CGRectIntersection(pRect, tileRect); //5 int tileIndx = ; //6 if (tileIndx == 0) ( //Cell directly below Koala p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height); ) else if (tileIndx == 1) ( //Cell directly above Koala p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - intersection.size.height); ) else if (tileIndx == 2) ( //Cell to the left of Koala p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y); ) else if (tileIndx == 3) ( //Cell to the right of Koala p.desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y ); ) else ( if (intersection.size.width > intersection.size.height) ( //7 //The cell is diagonal, but solve the problem vertically float intersectionHeight; if (tileIndx > 5) ( intersectionHeight = intersection.size.height; ) else ( intersectionHeight = -intersection.size.height; ) p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection.size.height); ) else ( //The cell is diagonal, but solve the problem horizontally float resolutionWidth ; if (tileIndx == 6 || tileIndx == 4) ( resolutionWidth = intersection.size.width; ) else ( resolutionWidth = -intersection.size.width; ) p.desiredPosition = ccp(p.desiredPosition.x , p. desiredPosition.y + resolutionWidth); ) ) ) ) ) p.position = p.desiredPosition; //7)


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 loop through each cell from this set. Every time we iterate over a cell, we check it for collisions. If a collision occurs, we change the Koala's desiredPosition.
2. Within each loop of the loop, we first get the Koala's current frame. Each time a collision is detected, the variable desiredPosition changes its value to such that the collision no longer occurs.
3. The next step is to get the GID we stored in the NSDictionary, which can be null. If GID is zero, then the current loop ends and we move on to the next cell.
4. If in new position there is a cell, we need to get it 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 that we have Koala's CGRect and cells, we can test them for collision.
5. To test the cells for collision, we run CGRectIntersectsRect. If a collision occurs, we will get a CGRect describing the CGRect of the intersection using the CGRectIntersection() function.

Let's think about the dilemma...

Quite an interesting case. We need to figure out how to properly detect collisions.
You might think that The best way move Koala - move it in the opposite direction from the collision. Some physics engines do work this way, but we're going to apply a better solution.
Consider: Gravity is constantly pulling Koala down into the cells below her, and these collisions happen all the time. If you imagine a Koala moving forward, at the same time, the Koala is still being pulled down by gravity. If we solve this problem by simply changing the movement in the opposite direction, then the Koala will move up and to the left - and we need something else!
Our Koala should move far enough to still stay above those cells, but keep moving forward at the same pace.

The same problem will happen if the Koala slides down the wall. If the player presses the Koala against the wall, the desired trajectory of the Koala's movement will be directed diagonally down and into the wall. By simply reversing the direction, we will make the Koala move up and away from the wall - again, not at all! What we want is for the Koala to stay out of 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 handle both actions in a mutually exclusive way. Some physics engines constantly process first the first event and then the second; but we want to make a better decision based on the position of the Koala cell. So, for example, when the cell is directly below the Koala, we want the collision detector to return the Koala to the top.
What if the cell is diagonal to Koala's position? In this case, we use the CGRect of the intersection to figure out how we should move the Koala. If the width of this rectangle is greater than the height, then you need to return the Koala vertically. If the height is greater than the width, then the Koala should move horizontally.

This process will work correctly as long as Koala's speed and frame rate are within certain limits. A little later, we will learn how to avoid cases when the Koala falls too quickly and slips through the cell down.
Once we have determined whether to move the Koala vertically or horizontally, we use the CGRect size of the intersection to determine how much to move the Koala. We look at the width or height, respectively, and use this value as the displacement distance of the Koala.
Why check cells in a specific order? You always need to work with adjoining cells first, and then with diagonal ones. After all, if you want to check for a collision in the cell to the lower right of the Koala, then the displacement vector will be directed vertically.

However, there is still a chance that the CGRect of the collision will be pulled up when the Koala just barely touches the cell.
Look at the picture on the right. The blue area is pulled up because the collision rectangle is only a small part of the overall collision. However, if we have already solved the problem with the cell directly below the Koala, then we no longer need to detect collisions with the cell below to the right of the Koala. This is how we get around the problems.

Back to code!

Back to the monstrous method...

6. The sixth section allows us to get the index of the current cell. We use the cell index to get the cell position. We're going to operate on adjacent cells individually by moving the Koala, subtracting or adding the length or height of the collision. Pretty simple. However, once it comes to diagonal cells, we are going to apply the algorithm described in the previous section.
7. In the seventh section, we determine whether our collision area is wide or stretched upwards? If wide - we work vertically. If the cell index is greater than 5, then move Koala up. If the area is stretched upwards, we work horizontally. We act on a similar principle of the order of cell indices. At the end, we assign the received position to the Koala.

This method is the brain of our collision detection system.

Let's use all the available knowledge in practice! Change method update(still in GameLevelLayer:)

// Replace ";" on the: ;
You can also delete or comment out the block getSurroundingTilesAtPosition:forLayer:

/* for (NSDictionary *d in gids) ( NSLog(@"%@", d); ) //8 */
Let's launch! Surprised by the result?

Paul stops Coalio, but he immediately drowns in him! Why?
Can you guess what we missed? Remember - every step of the game we add the force of gravity to the Koala's speed. This means that the Koala is constantly accelerating downward.
We are constantly adding speed to Koala's downward trajectory until it is the size of a cell - we are moving through the whole cell in one step, which is what causes problems (remember, we talked about this a while ago).
As soon as we detect a collision, we need to reset the speed of the koala in the direction of the cell we collided with! The koala has stopped moving, so the speed must be taken into account.
If we do not implement this, then we will have a rather strange behavior of the game. As we noted earlier, we need a way to detect if the Koala is touching the ground so that the Koala can't jump even higher. We will check this box right now. Add the following lines to checkForAndResolveCollisions:

Click me

- (void)checkForAndResolveCollisions:(Player *)p ( NSArray *tiles = ; //1 p.onGround = NO; //////Here for (NSDictionary *dic in tiles) ( CGRect pRect = ; //3 int gid = [ intValue]; //4 if (gid) ( CGRect tileRect = CGRectMake([ floatValue], [ floatValue], map.tileSize.width, map.tileSize.height); //5 if (CGRectIntersectsRect(pRect, tileRect )) ( CGRect intersection = CGRectIntersection(pRect, tileRect); int tileIndx = ; if (tileIndx == 0) ( //cell under Koala p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y + intersection. size.height); p.velocity = ccp(p.velocity.x, 0.0); //////Here p.onGround = YES; //////Here ) else if (tileIndx == 1) ( //cell above Koala p.desiredPosition = ccp(p.desiredPosition.x, p.desiredPosition.y - intersection.size.height); p.velocity = ccp(p.velocity.x, 0.0); ///// /Here ) else if (tileIndx == 2) ( //left cell p.desiredPosition = ccp(p.desiredPosition.x + intersection.size.width, p.desiredPosition.y); ) else if (ti leIndx == 3) ( //cell on the right p.desiredPosition = ccp(p.desiredPosition.x - intersection.size.width, p.desiredPosition.y); ) else ( if (intersection.size.width > intersection.size.height) ( //tile is diagonal, but resolving collision vertially p.velocity = ccp(p.velocity.x, 0.0); //////Here float resolutionHeight; if (tileIndx > 5) ( resolutionHeight = intersection.size.height; p.onGround = YES; //////Here ) else ( resolutionHeight = -intersection.size.height; ) p.desiredPosition = ccp( p.desiredPosition.x, p.desiredPosition.y + resolutionHeight); ) else ( float resolutionWidth; if (tileIndx == 6 || tileIndx == 4) ( resolutionWidth = intersection.size.width; ) else ( resolutionWidth = -intersection .size.width; ) p.desiredPosition = ccp(p.desiredPosition.x + resolutionWidth, p.desiredPosition.y); ) ) ) ) ) p.position = p.desiredPosition; //eight )


Every time there is a cell (either adjacent or diagonal) below the Koala, we set p.onGround to YES and reset the speed. Also, if there is an adjacent cell under the Koala, we reset its speed to zero. This will allow us to correctly respond to the Koala's current speed.
We set the onGround variable to NO at the beginning of the loop. In this case, onGround will only have a value of YES when we detect that the Koala has collided with the cell below it. We can use this feature to determine if the Koala can jump or not at the current time.
Add the following code to the header file (and then synthesize everything you need in the executable) to Player.h:

@property (nonatomic, assign) BOOL onGround;
And in player.m:

@synthesize onGround = _onGround;
Let's launch! Is everything working as intended? Yes! Oh what a great day! Hooray!

What's next?

Congratulations! You have completely finished your physics engine! If you have reached this text, you can breathe a sigh of relief. This was the difficult part - there will be nothing difficult in the second part of the tutorial.
And here are the source codes of the project that we have now completed.
In the second part, we will make our Koalio run and jump. We will also make the spiked blocks in the floor dangerous for our Koala and create win and lose screens.
If you want to get even more knowledge about platformer physics engines, 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.
Possibly the best guide to creating platform games from Higher-Order Fun.
tutorial Add tags