Assignment 10 – Giuseppe

My main inspiration came from my dear friend Youssab William’s midterm project ASCENT. I love the idea of platformers and honestly Youssab inspired me to make something on p5 that has platformer mechanics.

As an act of creative freedom and my usual obsession with ownership, I want to call this project Giuseppe.

soooo here’s the sketch. Use Arrows or WASD to move

 

but wait. this isn’t a platformer. not even close actually. so what happened? we will explore that in this documentation

Before writing a single line, I mapped out what matter.js actually needed to do for this to work.  I created a simple platformer and saw how things will go from there.

Well…yeah.. this wasnt what I expected. I don’t know what I expected hoenstly. Just not something… this bad?

I immediately scrapped the idea after this video. But before pulling out my hair and making yet another 17th sketch trying to think for a concept for this assignment, I noticed that the little red cube sticks on the sides of the platforms. This sparked an idea in me, a parkour-like game that a ninja (later simplified to a cube) jumping from wall to wall to avoid the lava. Just like those old mobile games. Then Giuseppe was born.

The concept is a vertical survival game. You wall-jump up an endless shaft while lava rises beneath you. There are zones. There are coins. There is a freaking lava floor that will inevitably come and get you.

The player body never has velocity set directly except during wall jumps. Everything else is force application. Horizontal movement applies a constant force every frame:

let moveForce = 0.0012;
if (keyIsDown(LEFT_ARROW) || keyIsDown(65))
  Body.applyForce(playerBody, playerBody.position, { x: -moveForce, y: 0 });
if (keyIsDown(RIGHT_ARROW) || keyIsDown(68))
  Body.applyForce(playerBody, playerBody.position, { x: moveForce, y: 0 });

if (playerBody.velocity.x > 5)
  Body.setVelocity(playerBody, { x: 5, y: playerBody.velocity.y });
if (playerBody.velocity.x < -5)
  Body.setVelocity(playerBody, { x: -5, y: playerBody.velocity.y });

The speed cap is load-bearing. Without it the player accelerates forever and becomes impossible to control in about four seconds. The cap is also what makes the movement feel snappy rather than floaty, because the player reaches max speed almost immediately and stays there. Force application without a cap is just a slow velocity set with extra steps

I then entered my usual 3 am flowstate and I reached this position.

[im trying to upload but facing issues so i cant really upload attachments]

Thr wall grip part is the part I’m most proud of, and also the part that took the longest to not be horrible.

The grip has two phases. When the player first touches a wall, the engine fires a counter-force upward that decays linearly to zero over 4000ms. At t=0 the counter-force exactly cancels gravity so the player feels weightless on the wall. By t=4000ms it’s gone and the player slides off. After the grip expires there’s a 500ms push-off phase where a small lateral force nudges the player away from the wall and canjump is set to false, so you can’t wall-jump from a dead grip.

if (contactAge < GRIP_DURATION) {
  let t = contactAge / GRIP_DURATION;
  let counterForce = lerp(0.0012, 0, t);
  Body.applyForce(playerBody, playerBody.position, {
    x: 0,
    y: -counterForce,
  });
} else if (contactAge < GRIP_DURATION + PUSH_DURATION) {
  Body.applyForce(playerBody, playerBody.position, {
    x: -wallSide * 0.0015,
    y: 0,
  });
  canJump = false;
}

The 0.0012 is not random. matter.js default gravity produces a downward acceleration of 0.001 per update tick at default settings. The counter-force matches it exactly so the player hovers. I lied, AI found that value for me when I asked it why the player was either rocketing upward or sliding instantly. The fix was embarrassingly simple once I knew what to look for.

The grip timer also renders as a visual bar on the wall panel. The bar height shrinks as grip time expires and turns red during the push-off phase so you always know exactly how much time you have left. I added that late and it completely changed how readable the game was.

The collision architecture has three events all running simultaneously.

beforeUpdate resets canJump, wallSide, and touchingWall to false/zero every single frame before any collision detection runs. This is important because matter.js collision events only fire when contact is active, which means if nothing fires this frame, the flags stay false. No ghost jumps. No lingering wall contact.

collisionActive runs every frame the player overlaps any surface and handles all the grip logic above. It’s also where wallSide gets set, by comparing the player’s x position to the wall body’s x position to determine which side the wall is on.

collisionStart handles orb collection as a one-shot event. Orbs are sensor bodies so they have zero physical response, but they still fire collision events. When an orb-player pair is detected, collectOrb() removes the body from the world, increments the coin count, and spawns a particle burst.

Events.on(physEngine, "collisionStart", function (event) {
  for (let pair of event.pairs) {
    let bodyA = pair.bodyA, bodyB = pair.bodyB;
    if (
      (bodyA.label === "Orb" || bodyB.label === "Orb") &&
      (bodyA.label === "Player" || bodyB.label === "Player")
    ) {
      collectOrb(bodyA.label === "Orb" ? bodyA : bodyB);
    }
  }
});

The game has five zones that unlock at ascending height thresholds. Each zone changes the wall color, background color, and lava color simultaneously. The transition happens in one line: currentZone = newZoneIdx, and a banner fades in with the zone name then dissipates over about 100 frames.

The zone palette design was fun. I wanted each zone to feel like a different biosphere. SURFACE is warm red on near-black. THE CAVERNS goes amber on deep brown. CRYSTAL DEEP goes cold blue on dark navy. THE STORM goes violet on almost-black. THE VOID goes teal on pure void. The lava color shifts with the zone too, so in CRYSTAL DEEP the rising floor is a cold electric blue rather than orange, which is a detail I’m very happy with and nobody will probably notice.

The first working version was a flat canvas with a rectangle and a ground. No camera, no walls, just a box with gravity and a jump. That worked in maybe 30 minutes. Everything after that was a long series of things that almost worked.

The wall detection broke me for a while. The original approach used collisionActive to detect wall contact but I kept getting canJump = true from the ground at the same time as wallSide = 1 from a wall, which meant ground jumps sometimes registered as wall jumps and gave you the kick-off velocity by accident. The fix was the label check: if (surfaceBody.label !== "Wall" && surfaceBody.label !== "Ground") continue gates everything behind identity. The floor has label “Ground” and never sets wallSide. Walls have label “Wall” and never set canJump during push-off. Clean separation.

The camera also gave me problems. My first implementation was camY = CANVAS_H/2 - playerBody.position.y with no lerp, which meant the camera snapped instantly to the player position and the whole canvas strobed. Adding the lerp (camY = lerp(camY, camYTarget, 0.1)) was one line and made the whole thing feel like a proper game. That 0.1 lerp factor took me a while to find. At 0.05 the camera felt detached. At 0.2 it still twitched. 0.1 was the number.

The game is more playable than I expected at this stage. The wall jump interaction teaches itself within about three or four deaths, which is a good sign. Players understand immediately that the walls are grippable and that the lava is not.

The zone system adds something I wasn’t sure would land. The color transitions give the game a sense of depth even though the gameplay is identical in every zone. There’s something psychologically effective about a wall turning violet that makes you feel like you’ve gone somewhere.

Two things I’m not settled about. The lava speed is constant right now and it should probably accelerate as you get deeper into the game. A flat rise rate means the game’s difficulty is almost entirely dependent on your wall jump skill, which is fine but I think a rising speed floor would create more interesting decision moments. The second thing is audio. The game is very silent and I think a low bass rumble that rises in pitch as the lava approaches would communicate danger in a way the red vignette edge alone doesn’t quite reach. The vignette is good. Sound would make it visceral.

I also want to revisit the particle burst on wall jump. Right now it’s an orange burst at the jump origin, which looks fine. I think making it directional, so the particles kick backward from the wall jump direction, would make the jump feel more physically grounded without any extra complexity.

Leave a Reply

Your email address will not be published. Required fields are marked *