Prototyping Platforming

Max Novak
7 min readOct 5, 2023

--

After making a few prototype-esque games, I wanted to combine some of my previous works into something more whole. So, I decided to make a small little platformer called Jumpy McJumperson (because why not).

I started off poking around Itch.io again and found some cool hill pixel art and a little hooded guy that seemed like they would make for a good start. With these art assets, I put together a scene framework similar to what I built previously. Because I wanted this to feel more like a full game, I spent time building out an opening scene where: you are prompted with “click to start”; the character walks down the hill; the trees and ground fade in; & then the character walks into the scene.

It was really fun making some simple animations that felt like polish on a game. These intro scenes were a big jump from my first game, where everything just starts on the screen and there is no load or prompt. From there I started setting up player movement. At first I just focused on left and right movement. I had done character movement previously, but this time I wanted the background to move with the player to create perspective. A technique used in games is parallax scrolling and it creates the illusion of depth. It does this by having different layers so things can move can move slower the farther they are from the camera. The hill art had 6 layers which allowed for the background to move at 6 different speeds, which creates a great scene. Pixi also comes with a very useful tool called TilingSprites, which enable this kind of scrolling by having a single sprite loop when you reach the edge of it.

Notice how the first few layers loop, but the gif restarts before we loop through the back layers because of the different scroll speeds (SO COOL!)

After that I put the character on the screen and added key bindings for their motion. This meant that as the player ran left and right the background would also move with regards to them, so it looked like they were moving. While it is cool to have the parallax effect always on, I wanted it to only apply if the player was near the edges of the screen. This way it felt like you were moving within a space and then the camera would sometimes need to pan with you. To do this I put checks in place for the characters position and would only apply the background animations if they were up against these walls.

After that I hit my biggest task for this game, jumping! Now if you have movement left and right, why would jumping be harder? Well, the hard part wasn’t the movement up it was really the stopping and falling down. Without really thinking most people know what a jump looks like. When you start you quickly move up; slow down until you hit the apex; and then slowly start speeding up until you land. There is way more physics there, but I don’t wanna think about it and I’m sure you don’t wanna read my bad explanations of it!

To handle this I started looking into Javascript physics libraries and found Matter.js. After poking around a bit, I started to realize something slightly annoying. It appeared that the best way to use Matter with Pixi was to create a version of the game objects twice, once in Pixi and once in Matter. When you want to move something, you would apply the movement in Matter and then take the resulting locations of the objects and apply them to things in Pixi. This meant a huge duplication of code, which felt silly. Especially since Matter could do its own rendering, which meant why use Pixi at all? Rather than rebuild everything I decided to go with “bad” (or just very simple) physics to move past this blocker. I just had the character move upwards at a flat speed and once they hit a certain height I had them fall back down at the same speed. This made for weird up and down movement, but it let me move forward.

Something else I realized with jumping is that I couldn’t just loop the animation like I had been for everything else. Unlike running or idling you can’t just play the animation over and over until the player stops moving. Jumping has two distinct motions related to it: going up & coming down. Luckily this is pretty easy to handle, but it meant I would need to toggle on and off the .loop property of my AnimatedSprite. All of the player’s other animations were ones I wanted to loop: idling, running, & walking. However, for jumping you just want to play the jump animation once and the fall animation once. Which meant whenever the player hit jump, not only did I apply a new animation I also turned off looping until they landed.

Previously I wrote about how I ran into bugs when I called .play too much when switching animations. When working on this project I discovered a very useful property on KeyboardEvents called repeat. It returns a boolean saying if the key is being held down or if ’tis a new key press. This meant I could streamline some of the animation actions from what I did last time. This also was very helpful for jumping. Unlike running left or right I wanted the player to have to press jump each time they wanted to jump. And with the repeat property I could make sure that they had to press the key again.

// First check the key, then that they aren't moving vertically
// and finally that they aren't holding the key down.
if (isJump(e.key) && this.playerVelocityY === 0 && !e.repeat) {
this.player.textures = playerAnimations.playerJumpUp;
this.player.animationSpeed = 0.2;
this.playerVelocityY = -runSpeed;
this.player.loop = false;
// This play will only ever play once because of the repeat check
this.player.play();
}

As a quick aside, something that I still had some issues with in Pixi was typing. A big reason I was working in Typescript was it allows for easier and safer work within Javascript. A best practice in Pixi is to load all of you assets into the cache before you let the player start the game. This is done so that when the player starts playing they don’t have weird interactions while something is loading. However, whenever I pulled assets from the cache like this:

Lost typing “any” where it should have been a “string[]”, which is an array of pointers for the animation’s textures

the types would always be any(which is Typescripts way of saying untyped). I never found a good solution to this. I could just cast the types to what I wanted them to be, but that has its own set or problems. I feel like there should be a better way to do this, especially since for static sprites I can just call Sprite.from("image_name_in_cache"). But either way, this wasn’t a blocker to moving forward, just a small Pixi/Typescript complaint.

Now that I had all of my player behaviors, I started thinking about designing a level. I grabbed some assets for creating gaps in the ground and platforms to stand on. But then I hit an interesting problem, how do I build the map for a level? I had a couple of thoughts about what to do, but all of them required building out way more infrastructure. For example, I could build out a JSON object that would hold where the ground was, holes were, and platforms in the air. This JSON would let me quickly put together levels, but it also would mean majorly overhauling what I already had for the level structure (close to a full rebuild) on top of this level parser. That wasn’t exactly what I wanted to do next for this. Another option I had was to create a player camera that could follow them. This would allow me to create the level, which exists off of the viewable canvas and I could use the existing coordinate system. This approach is very common in tools like Unity, where the whole level can be designed and then the camera just follows along saying when things get rendered. I found some forum posts talking through approaches like this, but they would also all entail a big rebuild. And with these realizations I figured this was the right time to take a step back and reflect on what I wanted to do next.

Why I stopped where I did? — A lot of the struggles I was running into with this game are things I can get for free in other systems. Working on this project I started to realize I was hitting not limitations of Pixi, but more limitations of using a renderer to make a game instead of an actual engine. Were these limitations that I had looked for when choosing to work in Pixi? Absolutely! I wanted something code first, which would let me get into building things quick and easy. But as I’ve worked on these projects (especially this one) I began to realize that I actually wanted some of the bells and whistles that come with a full engine. While I didn’t finish building out the full levels of this game, I still wanted to talk through how I got to choosing to switch tools and have what is playable as a sort of record of my journey.

What’s next? I started doing some research into some game engines. I have had my eye on Godot and Löve2D. Godot is an engine I have been interested in since playing The Case of the Golden Idol. And Löve2D was something I recently heard about from my friend Randy who recommended Challacade as a cool developer to check out. I know that Unity is usually the go to for accessible game engines, but with the recent kerfuffle around pricing I kinda wanted to steer away. Next time I’ll be back with games made in either Godot or Löve2D or maybe both?

Check out the game here and the code here and hopefully I’ll have some other small games to write about soon!

--

--

Max Novak

Software Engineer that likes to futz about with games