Since December 2021, I’ve been working on a little game called flutter.runner.
It’s like Flappy Bird, but also like Temple Run, but like Mario. It’s still unfinished and there’s a lot more I want to do but I wanted to break down the process I took to make it, before it is published.
Goals
I was spurred to create this game through a competition put on by Haste. The constraints of an arcade-like leaderboard meant that I had to 1) create a game that was addictive enough to keep somebody playing multiple runs (‘inserting quarters’) but 2) had some kind of natural score ceiling and ending so that they couldn’t play forever.
I settled on an endless runner and set a few constraints - I didn’t want the amount of obstacles to multiply, as I personally find that frustrating. Instead, I would let speed and player decision be the ultimate factor in a high score. I wanted the game to have a skill curve such that, after a certain amount of time/score is reached, every second, and even half-second that you survive, counts. This was the main goal for the scoring system of flutter.runner, specifically for the purpose of the Haste Arcade.
Engine Choice
I decided to make my game in PICO-8 which I had messed around with briefly in the past. This proved to be a good choice in making a game but a complicated one in terms of integration with Haste (more on that later).
My reasons for doing so were because 1) I like that Pico has a full suite of tools at its disposal - sprite editor, map editor, audio tracker, etc. and 2) I appreciated the simplicity and limitations of the Lua programming language and environment. As I would find, Lua is hardly limited at all - only by the imagination and skill of the programmer. However, by purposefully ‘limiting’ myself to the tools available within Pico, I believe I was able to actually finish the game and limit scope creep.
Step 1 (and 2, and 3)
My first goal was to get a basic endless runner going. I created a player class and a map sheet, and created a basic collision system. I created my iconic flutterboy and gave him some minor animations for looking up and down, and made him always be moving to the right, such that the only player control was the jump button. And while I was cracking my head against the wall of procedural generation, I spent a good amount of time learning about 1-bit art and how to draw good sprite art.
That initial gif you see had a couple issues. For one, I realized that having ground elevation could get you stuck if you’re forced to always be moving. Thus I had two choices to make - either make getting stuck part of the core difficulty, or eliminate it entirely. I chose the latter.
Secondly, I drew all the maps by hand.
For those not familiar, Pico gives a 8x8x8 map grid to build levels/whatever you want with. The map sheet is separate (they might be linked in the memory, but this is deeper knowledge than I presently have) from the sprite sheet. This means that, if you draw in a level moving left to right on the map sheet as I was doing, you will always reach a limit at 8 screens. And 8 screens was really not enough space to do much at all. My thinking was - I know that eventually I will have to make the level ‘endless’, i.e. new platforms will be generated procedurally, so what I will do is: draw in 8 sets of 8 screens, and then I will link them randomly, so that every 8 screens you are entering a ‘new’ set of 8 screens. Well, it sounds amateurish now to think back on it, but that was my line of thinking and this ended up giving me a lot of headache as time went on.
For one, the elements drawn into the map screen don’t actually ‘exist’ in the game worldspace. This means that the only way I could have tiles drawn into the map actually have collision, ie. you can land on them or jump through them, was to log their positions prior to beginning the game itself. This posed a lot of issues for me when I was trying to procedurally generate the platforms later, as the platforms could be generated visually but not physically.
Eventually I had to scrap the idea of drawing the levels in map form at all. Well, I didn’t go all the way entirely - I ended up drawing the floor and ceiling as part of the map sheet. I still consider this an unfinished feature, as ideally I would be procedurally generating the floor, ceiling, AND the platform tiles, but I digress.
The difficulty of all this comes from the idea of ‘traveling’ the length of the 8 screens. If I knew what I was doing when I started, I would have only used 1 screen, maybe 2 for mathematical certainty. That means that the character never travels beyond y-pixel 64, instead being moved back to 0 every time it reaches 64. Or another way to think about it is that it always stays at pixel 64, and the world just moves around it. Instead, I had to deal with making a system that takes the player to the end of the level, then magically teleports them back to the beginning without them realizing it. And with the limitations of Pico, that’s just not simple to do. I ended up with a visual representation of the ‘end’ of the 8th screen, with the hope that it isn’t too distracting for the player.
And after a lot of tweaking, I got the procedural generation of platforms working, along with some visual effects. Interestingly, the ‘falling dust particles’ I found are best drawn on the map screen, and then animated as an overlay onto the level. Again, something I never would have thought of if I didn’t make the mistakes I made starting out.
I will note that the procgen is not perfect, not even close. Though every playthrough of flutter.runner will have new platforms, the platforms remain relatively consistent per 8 screens. What this means is that every 8 screens, you will have the same platforms as before, there will just likely be more of them (and more stops, as I added later).
I found however, that this has a minor gameplay benefit, in that the addition of more platforms actually makes the game harder when you are moving a lot faster, so until I redesign the entire system, I’m keeping it this way. Besides, nobody has made it that far anyway ; )
A way to die
I now had a decent looking world for our flutterboy (though he didn’t flutter yet) to jump through. But now I needed a way for him to die. I decided that moving enemies were the best way to accomplish this, though I tried a couple other ideas before - traps, pits, ‘chasing’ death screen that kills you if you slow down, etc. I settled on enemies because I figured that if they were procedurally generated too, they would add some needed variety to the game.
My first enemies were dumb af, and couldn’t kill you even if you wanted to die.
This was another casualty of drawing the enemies to the map screen - they could exist visually, but even if you tried your hardest to touch them, you couldn’t collide with them.
So I had to treat the enemies as if they were other players, give them their own classes. I now realize that a best paradigm for this would be to create a generic player/object class that all players, enemies, etc could be instantiated from, but hindsight is 20/20. Also, considering Pico’s memory limitations.. well I can’t say I know them well enough, but I can see pitfalls everywhere.
After getting the enemies to actually do their job, I implemented a way for them to sort of move unpredictably and at different speeds. This system is currently highly imperfect. However, this is one of those things where it adds somewhat to the gameplay as the enemies are predictable enough such that a skilled player can avoid them when necessary, but they will eventually in all likelihood catch up.
From here, I added some SFX (still haven’t added in proper music yet) and cleaned up the look of the game. At some point, I made my brother play the game for a whole day and watched his reactions as somebody who does not play video games at all. It was through this that I decided to add the ‘flutter’ jump, so that the player can always be mobile. I believe this adds to the game feel tremendously and also makes it less frustrating, though still difficult.
From here on, it was all tweaking to make things gel, and I’m very proud of the (almost) finished product.
Some things I have to work on:
Better level generation
Adding music
More interesting enemies
Powerups
UI tweaking
Thank you for reading.
The next part of this will delve into the integration with Haste, and how I will turn a simple game like this into a profitable enterprise, as well as the unique and interesting player feedback I’ve received.
If you try the game please give me any feedback and send me a screenshot of your high score!