Edit: Current list of collision system posts:
Collision System Part 2A: Solid CollisionSolid collision refers to the floors, walls and ceilings that Entities collide against when moving around the level.
Solid Collision SourcesThere are two sources of solid collision shapes:
The Grid: the tiles that comprise the level. Each tile in the tileset has
collision information defined. A tile can either be solid on all four sides, top-only only (can jump through the platform from beneath), or empty.
Entities: Each entity can also provide a solid collision shape - for example breakable blocks. Like the Grid tiles, these shapes can provide collision on all four sides, or top-only. The debug display shows that as well as their Body (orange rectangle), each breakable block has a solid collision shape (blue rectangle):
The Grid's solid collision is not drawn in the debug view; but you can imagine each tile that forms the rocky floor having a blue rectangle around it too.
Overview of a single game frameEach frame a number of things happen to the Entities in a set order, which make up the process of moving the Entity and colliding it against the solid collision.
For each Entity
UpdatePreMovement
For each Entity
UpdateMovementY
For each Entity
UpdateMovementX
For each Entity
UpdatePostMovement
The main thing to note is that each function is called on *every* Entity before moving onto the next function. For example UpdateMovementY has happened for every Entity before UpdateMovementX happens.
I'll talk about it more below, but the core idea of the collision algorithm is that movement and collision on the X and Y axis are handled entirely separately. This allows for a lot of simplification of code.
UpdatePreMovementVarious things happen in this function, but the most important is that forces are applied to the Entity, affecting its velocity. Gravity is the most common example.
(The velocity of an Entity is a 2D vector containing the velocity for the X axis (horizontal) and Y axis (vertical). If the X velocity is 10.0 then the entity moves to the right 10.0 tiles per second.)
The force of gravity will increase the Y velocity of an Entity, causing it to accelerate downwards towards the floor (or a death pit).
Other forces can be applied during UpdatePreMovement, for example for the Leilani Entity only, if the player is holding the "Right" input then Leilani may accelerate on the X axis to simulate walking.
UpdateMovementYThis function is responsible for actually applying the Y component of the velocity to the Entity's position, and then resolving collisions with floors/ceilings that occur as a result of this.
Let's look at a diagram example of how this happens when Leilani jumps upwards into those breakable blocks.
The initial state: Leilani is just below the blocks. The green arrow indicates the direction of her velocity (the arrow doesn't correctly represent the magnitude of her velocity, it's just a rough indication that she's moving upwards.)
Applying the velocity to Leilani's position moves her upwards. We calculate the position that she should be in at the end of this frame, let's call this the Target position, which is represented by the red dotted outline. The Current position is the orange rectangle.
We need to prevent Leilani from intersecting with the solid collision above her. This check is done by taking three positions along the top of her Body.
For each of the three positions along the top of the Body at the Current position, imagine a line that stretches up to the corresponding position on the Body at the Target position.
Since the end position of each of these arrows is inside a solid collision shape, we know that there's an object in the way that Leilani needs to collide with. Since Leilani is moving upwards, we know that we're looking for a ceiling. So the position of the ceiling must be the bottom of the solid collision shape.
This is one of the times where handling collision only on one axis at a time makes handling the collisions very simple! Even if Leilani had some X velocity in this example, we can entirely ignore it. UpdateMovementY is only concerned with movement on a single axis. If the entity is moving upwards, it'll collide with ceilings. If the entity is moving downards, it'll collide with floors.
So we now know where the ceiling is. In this case, all three of the collision checks have found a ceiling at the same height above us:
Side note: It would also be possible for the collision checks to find objects at differing heights:
In this case we would only pay attention to the ceiling that's nearest to Leilani, and ignore the others.
The final result of this is that we position Leilani against the ceiling, so she doesn't clip into it!
The green arrow also shows that her velocity is now pointing downwards, so she will bounce off the ceiling in a satisfying way. The resulting velocity of a collision can depend on various factors, depending on the gameplay state or properties of the entity involved. In the case of the Leilani jumping against a ceiling, her Y velocity is set to a fixed value, so she will always bounce off ceilings at the same speed regardless of how fast she was moving upwards. This feels quite satisfying and also predictable.
(You may have noticed that I didn't deal with the part of this sequence where the act of jumping into the breakable blocks smashes them - this will be covered in a later part of the devlog.)
UpdateMovementXThis is the same as UpdateMovementY, but for the X axis. The logic and methods of checking for collisions are essentially the same.
Let's use this opportunity to show how it looks when Leilani is moving at a diagonal.
Again, the initial state, this time with Leilani moving both upwards and to the right:
As before, UpdateMovementY calculates her Target position above her:
And then detects the ceiling, preventing her from clipping into it:
Note that Leilani bounced off the ceiling which caused her Y velocity to now point down. But the X component of her velocity is the same as before so she also continues to move to the right.
UpdateMovementX calculates a new Target position:
And then detects the walls, moving Leilani against the walls:
When Leilani collides with a wall and is not rolling, her X velocity is entirely cancelled out, as you can see in the image - her velocity is now pointing directly downwards, so next frame she'll simple fall downwards from this corner. As with ceiling collisions, the behaviour of how the velocity is altered by a collision can vary, for example if the entity we're dealing with was an enemy that had been stunned and kicked by Leilani, then it would rebound off the wall instead.
UpdatePostMovementThis function mainly does visual / cosmetic things. The main example is that now the entity has finished all its movement and collision, UpdatePostMovement will update the sprite so that it displays the appropriate animation and is in the correct position.
I think that's enough for now... Part 2B will cover some additional intricacies that can happen at this stage of collisions.