Skip to main content
← Make a Platformer

Lesson 3 of 4

Platforms and Collisions

Replace the single floor with a list of platforms. Then write the overlap test that every 2D game uses.

What you will learn

  • A list of objects as level data
  • Rectangle overlap
  • One-way platforms
  • forEach

Before you start

Gravity and Jumping. The ground check becomes a real collision function.

What this stage does

There are now four platforms above the ground. Jump onto them. Try jumping up through one from below. You pass through it and land on top. Then walk into the side of one. You pass through that too.

Can you still reach the top?

A level only works if the player can jump high enough to clear it. Change these numbers. See which platforms go out of reach.

0.6
15

Turn this down until the higher platforms are out of reach.

The level is just a list

The ground is no longer a single number. It is one item in a list. To add a platform, you add one line to that list. You write no new code for it.

const platforms = [
    { x:   0, y:   0, width: 600, height: 50 },  // the ground
    { x: 200, y: 140, width: 100, height: 10 },
    { x: 400, y: 190, width: 100, height: 10 },
    { x: 600, y: 240, width: 100, height: 10 },
    { x: 800, y:   0, width: 300, height: 50 },
];

The ground sits at y: 0. That is the bottom of the world. A platform's y is how high it sits above the floor. The platform at y: 240 is 240 pixels up. A higher platform has a bigger number.

Do two rectangles overlap?

Most 2D games use a function like this one. These four lines do most of the work. The function asks the same question twice. It asks once about the left-right direction and once about the up-down direction.

function rectsCollide(a, b) {
    return (
        a.x < b.x + b.width &&    // is A's left inside B's right edge?
        a.x + a.width > b.x &&    // is A's right inside B's left edge?
        a.y < b.y + b.height &&   // is A's bottom inside B's top edge?
        a.y + a.height > b.y      // is A's top inside B's bottom edge?
    );
}

Landing on top, and only on top

Overlapping is not enough. If every overlap counted as landing, the player would jump to the top of a platform they walked into from the side. So the code adds a second test. That test has two parts.

  • player.velocityY <= 0 means the player is falling, not rising.
  • Subtract velocityY from the player's feet. That tells you where the feet were on the previous frame. If the feet were above the platform then, the player is landing on it now.
function handleCollisions() {
    platforms.forEach((platform) => {
        if (!rectsCollide(player, platform)) return;

        const platformTop = platform.y + platform.height;

        if (player.velocityY <= 0 &&
            player.y - player.velocityY >= platformTop) {
            player.y = platformTop;
            player.velocityY = 0;
            player.isJumping = false;
        }
    });
}

The result is a one-way platform. You can jump up through it and land on top. Most platform games work this way.

Build it step by step

Step 3 has a bug in it on purpose. That bug is the reason for step 4.

  1. Turn the floor into data

    Replace groundTop with a platforms list. At first the list holds only the ground. Draw the platforms in draw() with a forEach loop. Use the same drawRect. The game should look and play exactly as it did before.

  2. Write rectsCollide and test it alone

    Write the function. Then call it from the console with two rectangles you can check on paper. Get the function right on its own. Only then use it in the game.

    rectsCollide({x:0, y:0, width:10, height:10},
                 {x:5, y:5, width:10, height:10})   // true
    
    rectsCollide({x:0, y:0, width:10, height:10},
                 {x:50, y:0, width:10, height:10})  // false
  3. Land on any overlap

    Write handleCollisions() with only the rectsCollide check inside. Stop the player on any overlap. Call it after gravity. Then add the higher platforms and play. Walk into the side of one. You jump to its roof. That is the bug.

  4. Add the falling test

    Add the velocityY <= 0 part and the previous-frame check. Walk into a platform's side again. Now you pass through it. You can also jump up through it and land on top.

  5. Design a level

    Edit the platforms list. Build a jump that takes practice to land. This is the reward for turning the level into data.

Try these changes

Make these changes in the files beside this guide. Each one only uses what this lesson has already covered. Tick each one when it works.

Check yourself

Three quick questions on this lesson. Get them all right to finish the lesson and earn a pack.

Loading your answers

Teacher's versionHow to run this lesson, what to check, and any answers. Students do not need this.

How to teach it

  • Teach rectsCollide on paper first. Draw two rectangles on a grid. Read each of the four lines out loud against the drawing.
  • Insist on step 2. Students should test the function in the console, away from the game. That is the habit worth building. A wrong collision test inside a running game is very hard to debug.
  • Do not skip the broken version in step 3. Popping to the roof of a platform is a bug the student can see. They can describe it and then fix it. If you explain the falling test first, you waste that.
  • Give real time to step 5. Some students do not enjoy the code. Level design is where those students start enjoying the project.