Log In  

Tiny Tactics Postmortem

A few months ago I discovered PICO-8 and decided to make a game for it. It was a nice experience, and I'm proud of this little turn based, tactics / puzzle game. I'll try to summarize below how I made Tiny Tactics.

I didn't have much spare time to devote to this project (maybe 5 to 10 hours a week), but I think it went pretty smooth. I spent exactly 3 months making the game (from purchasing a license for PICO-8 to exporting the final version of the cart).

You can play the game and download the cart here.

I have also created a project on GitHub. It contains:

  • source code before flattening / compression;
  • spritesheet and map exports;
  • a standalone HTML version;
  • binaries for Windows, Mac, Linux and Raspberry Pi;
  • a walkthrough: GIFs I recorded using PICO-8, showing my solutions for each level;
  • leaderboards: more GIFs, showing how to get gold trophies I'm aware are possible (first person to claim the record gets the credit).

Note: Tiny Tactics was created using PICO-8 version 0.2.5g

1. What I set out to do

One of my main goals when making the game was learning PICO-8, so I decided to only use documented features while reading the PICO-8 Manual. This means no alternate palette, for example. I only used graphics functions in the most straightforward way possible (no 3D, lighting or other complicated techniques not directly supported by the PICO-8 graphics callbacks).

I also decided right away to fit everything into a single cart: I liked the limitations imposed by PICO-8, and I feared going multicart would allow the scope to creeply get out of hand (as it tends to do), especially on my first project.

On the other hand, I didn't want the game to feel like a vertical slice demo. I wanted it to feel somewhat complete, so I decided to cram as much levels into the cart as possible (no decompression or other advance techniques though, see the previous paragraphs). One of the earliest design decisions I made in Tiny Tactics was to devote all shared memory to the map.

You can see below that i used 99.87% of the available tiles in the map (including shared memory).

This left me with only two tabs for sprites. I also decided very early on that all enemies would use the player sprites, palette swapped. I experimented with different sprites for enemies, but the levels became more difficult to read so I abandoned the idea.

Of course there are also sprites for drawing the map (including enemies starting postion), story elements etc.
In the end I had just enough sprites to make a simple (yet pleasant) title screen and add a few "easter eggs" to the levels (dining rooms and offices, just to make the levels feel more lived-in).

You can see below that I used all available sprites (first two tabs only).

The UI was also very important from the start, as I feel this is part of what makes the game feel "somewhat complete." I opted not to include transitions or other fancy UI animations, though. Instead, I focused on the quality of life settings the player can configure in the game's menus (customizable animations, controls, tips and so on).

Also, I tried to "do it only once". I didn't want to spend lots of time redesigning levels or reimplementing mechanincs. If it wasn't fun, I scrapped it ASAP. If it was fun, I finished it and moved on. This is my first project, it was not supposed to have a long development cycle.

Finally, I quickly decided to use an external editor (Notepad++) to edit the code. The code section in tiny_tactics.p8 only contains 20 #include lines and a few comments, like the cart label ones.

2. What changed along the way

2.1. Feel

The game started as a minimalist tactics game, but it ended up also feeling like a puzzle game. Which is fine by me, these are my two favorite game genres. What was set on stone from the very beginning was that I wanted the game to feel "fair". The player and the enemies should be able to perform the same actions. Everyone should die in one hit. I wanted the player to feel he is overcoming all those enemies by being smarter than their simple AI, not because of some super powered armor or weapon.

2.2. Colors

The colors I used to palette swap the enemies changed a lot during development. I needed to have good contrast with the floor. The checkered white / light gray floor tile was set on stone very early on, but enemies' colors changed a lot along the way. When designing the last enemy types, the idea of using both hair and jacket colors to independently indicate different characteristics of their AI behaviour finally came to me.

Also, the idea of palette swapping wall colors to make each secret base feel different from the last one only came when designing the final bases. For most of the development process, the map was all in shades of gray. In the end of the development process, I realized this made progress very monotonous. I tried to choose wall colors that don't sacrifice level readability and also don't distract you from the enemies you should be focusing on.

2.3. Levels

When I was designing the first levels, the plan was to make all levels 15x13 tiles (which is the maximum level size, as some screen space is reserved for status bars on top and bottom and for the EXIT text when the door is unlocked).

Each level would consist of multiple rooms and, when a player moved from one room to another, enemies would enter the room from color coded doors. I quickly realized this made the code a lot more complex than it should... And also severely limited level design, which was the worst drawback.

I changed the approach, making each level be a single room with x, y, width and height coordinates stored in a table. Player and enemies are positioned when the level starts. This is done through color coded floor sprites (invisible by palette swapping these colors to white when drawing the map).

This brought much more freedom for the level design - and was much simpler to code.

2.4. Menu

When I designed the first few levels I quickly realized the game felt a lot like a puzzle game. I remembered how much fun I had optimizing my solutions in Sokoban and Hitman Go, so I decided to add a turn counter and records very early on. Player records are persisted on cart data.

On the other hand, I didn't want players to feel stuck so I included an option to unlock all levels (I also persisted that option on cart data). This helped a lot with development and testing.

For a long part of development, the menu only had three options.

  • play - I wanted level names front and center, as I quickly found out they were important to make levels feel unique and keep them engaging. When you press play you only see level numbers, names and wether you've cleared them or not.
  • options - The only option at the time was to unlock all levels.
  • records - Where the player can compare their best score for each level with the dev's record. As the record chase is meant to be the endgame, I decided this would be the last option on the menu.

2.5. Controls

Using arrows to move and a button to shoot was obvious from the start, but what about the other button? Should it also be used? For what? In the beginning I thought about using it to let the player spin in place (without moving), but I had the intuition this would lead to less interesting, more bureaucratic puzzles - and more complex AI.

I decided the second button would simply skip the turn for the player. This made the levels more claustrophobic - in a good way! Now the player has to consider if he has enough space to position himself so he can aim at enemies. This made for much more interesting puzzles.

I know the community knows keyboard input mappings like the back of their hands, but I wanted to show the game to friends who never played a PICO-8 game, so I decided to ask the player if he is using a keyboard and change button / key labels accordingly. The game does this when it can't load cart data (first time the game is started) or at any time the player selects controls on the title screen.

Based on the feedback I received, this was the right choice.

2.6. Tips

Tips started as random hints that appeared when levels were small enough, so i had some screen space to show them. In the end, though, it was best to leave them in the main menu, so they are easier to access.

I like that you have the basic info you need to play available in the game's menus, even if you don't read anything in a blog post. I wrote a lot more tips, but had to trim them down to the absolutely necessary so I could fit everything in a single cart.

2.7. Customizable animations

When I added animations to the game and tested levels with more enemies (like level 1-9), the testing process quickly became tedious. I decided to do what a lot of RPGs do: give players an option to skip animations. Then I decided to break that into more detailed options, like letting players set the animation speed and skip move and idle animations.

I decided to let animations in mid speed by default and trust the player to find these options by himself. Many didn't. I noticed many people didn't read the instructions or even checked to see what was available in the options. I suspected this could happen, but didn't want to interrupt the player with yet another text screen. I'm not sure it was the best decision though.

While testing with move / idle animations turned off, I noticed the game was letting only one enemy act in each frame. This made the game feel too slow when there were many enemies on a level. I changed the code to make sure the game lets more than one enemy act in a single frame when their animations are skipped.

The game still isn't as responsive as I would like when idle and specially move animations are turned off. But I don't have the tokens to completely overhaul the animations, turns and controls. Besides, as I said before, this is my first project. "Do it once and move on" was my motto! In any case, the game still is pretty playable and enjoyable as it is, IMHO.

2.8. Pause menu

Restarting or quitting the level is possible through additional commands in the pause menu.

I didn't want to assume player would be familiar with the pause menu, though. That's why the button / key used to access the pause menu are displayed in the controls screen.

That's also why I added the ability to turn off sound from the title screen, even though you can do the same in the pause menu. Redundant? Yes. But if I didn't know I could do that for the first few weeks of development, why assume players will know about it? Many players are friends of mine who never played a PICO-8 game before...

2.9. Story

While designing levels and testing them, I felt the game was feeling like a simple pack of levels. Level names did make each level more unique, but I still missed some kind of overarching narrative. I believed the game needed a story, even if it was as simple as the story in the first Super Mario Bros. game. So I made a simple story referencing some sentences from that game, but using a Mission: Impossible / 007 setting - which better fitted the gameplay.

Did you catch those references while playing the game? I even hightlighted the word substitutions in different colors to make them as identifiable as possible.

I liked the end result, even if it actually consists of just a few screens with text and very simple graphics with a different perspective (side view instead of top-down). It's simple, but I find it charming.

2.10. Trophies and alternative skin

I wanted to give players an additional challenge, and I wanted to reward them for it. So I added my personal records to the level coordinates table as the dev's record. I also added a screen to track records... And trophies! If the player matches my record for a level, he gets a silver trophy. If he breaks my record, he gets a gold one.

Note: I won't update the dev's records, even if I break my own records after the game's release (it has already happened, in fact). That's what the leaderboards are for.

I also added an alternative skin in the options if the player manages to get trophies for all levels (doesn't matter if they are silver or gold). It's been a week since I published the game on the BBS. As far as I know, nobody has unlocked the alternative skin yet... If you manage to do so, post a screenshot on the game's thread!

2.11. Sound

I'll admit sound was an afterthough in this project. The game didn't have any sound for most of the development process. Adding sound effects was pretty straightforward, but composing music takes time. I decided to do a very simple theme for the menus, use looping sound effects for story screens and not have any music during levels (just sound effects). I used less than 25% of the available sound effects and music patterns, but that's OK.

Maybe my next game will have more sophisticated sound, but the sound in Tiny Tactics, though simple, is serviceable and pleasent - at least to my ears. Also, it doesn't distract you from solving the puzzles, which is something I consider essential.

2.12. Tokens

I have used 99.86% of the available tokens and 99.55% of the compressed bytes limit.

I broke the token limit a few times during the last few weeks. I had to use some tricks to optimize my token count. Here are the two main ones...

  • The coordinates for each stage were combined into a single integer. As there are 81 levels, this saved me more than 1,000 tokens. For example, for stage 5-8 we have 0x1216.892e. Let me break it down (all numbers are in hex):
    • 12 and 16 are the x and y map coordinates of the topmost, leftmost tile of the level.
    • 8 is the level width in tiles.
    • 9 is the level height.
    • Finally, 2e is the dev's record for the level.
  • I used split() to create most of my tables, as each string only consume a single token. For example, the main menu is created with split("play,controls,options,tips,records"). You can do the same with numbers, split() will convert them for you.

Something I wish I could do is to define constants without wasting tokens. Constants in lua are just variables, and you spent tokens to define them. I still created a lot of them to make the code more readable, but in the final weeks I just didn't have the tokens to spare.

Sound effects constants, for example, are commented, and sfx numbers are hardcoded in the lua files. Side view character sprites for the story are also hardcoded for the same reason. I'm glad I managed to avoid this in most other situations, though.

I also simplified or removed a few redundant, verbose comments from the code to keep everything under the compressed bytes limit. The code is still very readable though, I believe.

2.13. Release

Before posting the cart on the Lexaloffle BBS, I uploaded the whole project to GitHub. I wanted anyone to have easy access to the bynary apps (Windows / Mac / Linux / Raspberry Pi), the standalone HTML version and the uncompressed source code, even if the person doesn't have PICO-8 to export everything.

I also created a walkthrough by recording GIFs, showing my solution for every level. After some time, it may be difficult to recall how exactly you finished a level in X turns. I know this from experience...

2.14. Updates

I updated the game a couple of days after releasing it. This first change was very minor: I addeed the level number to the clear screen, because my friends were sending me screenshots of broken records - and I had to ask which level they were playing.

Then, a couple of days later, I found a bug (suprised no one complained!) where you could walk through the locked exit door before you eliminated all the enemies... So I updated the game again to fix this.

I also added a leaderboards folder to the GitHub project. It contains GIFs showing how to get the gold trophies I'm aware are achieavable. First person to claim the record gets the credit (once I verify the new record is actually possible, of course).

This game has 9 enemy types and 9 secret bases with 9 levels each. Right now there are 9 gold trophies on the leaderboards. Funny coincidence, huh?

3. How did it go?

It's been a week since I published the game on the BBS.

I'm very happy to see my friends and family playing the game, finishing bases and even breaking my records! Two of them have already finished the whole game, all 81 levels!

It's also very nice that several forum members tried the game and liked it enough to give it a star or leave a comment.

I remember when I was a kid, drawing super heroes on my notebook. I didn't want to make money out of it, put my drawings in an art gallery or anything. I just liked drawing... Whenever I was proud of a drawing and my friends thought it was cool, that was my definition of success.

That's how I feel now, decades later, about Tiny Tactics. I had fun making it! I'm thrilled people are also having fun playing it! It's not perfect, but I'm proud of it.

Anyway, I hope you liked this writeup! And, if you still haven't played Tiny Tactics... Go try it!

P#138396 2023-12-08 15:54 ( Edited 2023-12-08 15:55)

An enjoyable read, thanks for sharing!

P#138548 2023-12-11 09:12

I'm glad you liked it, @iamlukesky!

P#138596 2023-12-12 11:43

It has been a week since I published the Tiny Tactics Postmortem.

I have updated the game today. Let me share a few details about the new version...

Solutions

A few days ago, after reading this comment on the game's thread, I decided to add a new feature to Tiny Tactics. Thanks for the idea, @Rascal!

Now, when you get a gold trophy, the level clear screen will display your solution! The message at the top of the screen will even suggest you take a screenshot.

The image above shows the longest possible gold trophy solution. This solution is 124 turns, which is just a turn shorter than the dev's record for this level - and this is the longest dev's record in Tiny Tactics.

I hope players will post their screenshots on the game's trhread. I'll appreciate it, and I'll add their records to the leaderboards, giving them credit for being the first to discover the solutions (to the best of my knowledge, naturally).

Before this feature, the only ways to share a solution were:

  • recording a GIF using PICO-8 or a bynary version of the game;
  • recording your screen using specialized software (if you are playing on the BBS).

Also, you had to remeber your solution so you could replay the level while recording.

While my friends Elton and Filipe D. went to the trouble of recording their screen to send me their solutions, this feature simplifies the sharing process to a simple screenshot. I hope this way people feel more inclined to share their gold trophy solutions.

Tokens

While implementing this feature, I crossed the token limit (again). So I did some light refactoring using a few techniques I had read on this thread. I also removed some of the least necessary comments. That's why most lua files were changed in the last commit, not just the ones affected by the new feature.

In hindsight, keeping this close to the token and compressed limits is not the best approach. I'm glad I still could fit everything in a single cart... But, if I had this insight when I started, I would avoid coding some less essential features (like tips) to give myself a bigger token margin.

Notes:

  • The standalone HTML version and the binary apps (for Windows / Mac / Linux / Raspberry Pi) were updated as well. If you use them, please download the new version.
  • Don't worry, the way the game saves your progress did not change. You won't lose any progress!
P#138693 2023-12-14 20:36

[Please log in to post a comment]