Every now and then, in a game’s development lifecycle, you get the chance to pause and reflect. With the recent conclusion of our Jetsam Beta Test, we’ve been given such an opportunity.

While we’ve used the feedback to recalibrate our approach to level design, it’s also made a great stopping point to reflect on the bane of all developers’ existence: tech debt.

Jetsam has been in off-and-on development since 2015, when I was in grad school and working on it as an independent study. My programming prowess has improved noticeably since then – so looking through the codebase shocks me at times.

This week, I identified a number of anti-patterns in the code, and I wanted to walk through each of them to hopefully help newer developers recognize the traps they can lead to and how to avoid them. But first, I’d like to answer a question about the whole process.

Why refactor your code in the first place?

Refactoring can be really controversial among developers, especially in the game development community. It can be said that shipping something, even something that isn’t perfect, is more important than worrying about code quality – and rightly so! If no one actually gets a chance to play your game, it doesn’t matter how pretty your code is.

You’re not going to win awards for writing beautiful code in your game. You’re not going to be showered with acclaim for making a key algorithm perform slightly more efficiently. In fact – it’s pretty likely none of your players are ever going to see your game’s source code. Ever.

Then why refactor at all?

I think refactoring is important as a matter of hygiene, not as a goal in and of itself. I posit that refactoring is worth it if and only if it will make feature development faster in the future, and you have more features to develop. Refactoring greases the wheels to make development go faster, and it can do so in all kinds of situations:

Working in a team?

Refactoring can make it easier for other developers to understand what everyone else has built, meaning that people can “learn” other areas of the codebase faster and thus get work done quicker.

Working alone?

Refactoring can help you quickly re-learn areas of the code that you’ve long forgotten about, and increase your ramp-up speed the next time you need to come back to that part of the codebase.

Do you want to add new features to your game, ever?

Refactoring can make connecting new components to old code much easier.

After our beta test of Jetsam, it was clear that a few new features would really improve the game’s design. Those new features will change the way Jetsam moves – and the movement code in the game was a tangled hornet’s nest of repetition, magic numbers, and closed design. So it became a prime target for refactoring.

Don’t Repeat Yourself

This one’s a classic – the DRY (Don’t Repeat Yourself) principle. In Jetsam’s movement code, I had four separate methods bound to swipe event handlers – movePlayerLeft, movePlayerRight, movePlayerUp, and movePlayerDown. Each method was itself a rough copy-paste job of the others, with only X and Y values changed to reflect different iterator patterns for each of the cardinal directions. Most of the code in these methods was identical!

The solution: creating a movePlayer method that has an enum of the four cardinal directions as a parameter. That way, other code can call movePlayer(MoveDirection.kUp) to create an Upward motion; or movePlayer(MoveDirection.kLeft) to create a Leftward motion. The X and Y iterators are set in a switch statement at the beginning of the method, so that the looping logic works identically for all four directions.

As a result, Jetsam’s movement code went down in lines of code by a factor of four.

Jetsam’s movement code checks for things like wormholes and crates

Minimize Magic Numbers

Numbers are extremely important as configuration parameters – especially in games. That shrinking animation won’t look quite right at a scale of 0.25x or 0.5x, but 0.378x seems just right, you know?

All too often though, these “magic numbers” get into our code in raw number form – we create a label with font size 30, or instantiate a sprite with dimensions 64px by 32px directly.

When I went to tweak the default menu button size in the game, I found, to my horror, that every single menu button throughout the entire game had its size set directly. Rather than changing the value in 39 separate locations, I declared a static variable on a Utilities class called defaultMenuButtonSize, and changed all 39 separate instantiations of menu buttons to utilize Utilities.defaultMenuButtonSize for their size.

As a result, I was able to tweak and re-tweak the button size until I got it juuuust right. And I’ll be able to easily tweak it again in the future if I change something about the UI’s layout.

Jetsam Beta’s Level Select UI

Think Open-Ended

This one is a little more hand-wavey than the rest, but one of the joys of game development is that things always change. If you’re just starting to develop a game, it’s pretty likely that it will go through several revisions of the core concept – whether they be tweaks, full-on redos, etc.

With that in mind, it is extremely important to write your code in a way that makes individual components easy to tweak, revise, and redo – without breaking the rest of your game in the process.

When I initially built Jetsam’s level editor, one of the core ideas was that levels themselves could be completely represented with ASCII strings. For example, ))/”()%%”!&”&#&$&%&&$&%&& or )) “(%((“!!!”!(!)”!”E”)$B%B&B(!(E())!)”)()). This more or less required that each bit in each character be assigned a meaning – and that any changes to the meanings of each bit would require all of the previous levels to be re-encoded.

So, when building the encoding scheme, I intentionally left some “spare bits” in these ASCII strings that I defaulted to zeroes. I didn’t have any plans to use them at the time, but I figured that in the future I might come up with a reason to use them, and I didn’t want to have to re-encode all the levels if I ever wanted to change something. And lo and behold, thanks to a beta tester’s great feedback, an entirely new way to play the puzzles was invented – and I had the spare bits to encode its activation into the level strings, without needing to change any of the existing 50+ levels.

Jetsam’s Level Editor

Conclusion

Refactoring was the right call for Jetsam at this point – we significantly reduced the complexity of the movement code, making way for some exciting new features. We made our UI much easier to tweak with some magic number removal, and we also benefited from earlier open-ended thinking in that we were able to quickly add hooks in for a new level style to be announced in the near future.

While we don’t have anything tangible to show for it (other than some gory git diffs), we’ll definitely have lots to show for it in the near future as we build exciting new things.

We came. We saw. We beta’d. Jetsam has been in the hands of real players for the first time!

The Jetsam Beta was a carefully-selected sampling of stuff that will be in the final game. We were intentionally cagey about further details – we wanted players to get into the game and explore, thereby testing how good our design was.

The first 9 levels in the Jetsam Beta

We collected feedback and all kinds of metrics from our Beta – including how people navigated the basic UI, what they enjoyed/hated, and how difficult they thought the levels were.

This post will go into detail about some of the lessons we learned – what worked, what didn’t, and how we plan to address the great feedback we received from finally putting our game out into the wild.

Jetsam Beta – By the Numbers

12 levels and 1 unlockable “secret” level

3 testable, level-altering mechanics

27 unique beta testers

146 unique play sessions

6.2/10 average fun rating for fuel cells

6.8/10 average fun rating for crates

7.5/10 average fun rating for wormholes

~1/2 of players say that wormholes were their favorite mechanic

~1/3 of players say that wormholes were their least favorite mechanic

UI Design

We asked a lot of questions about our UI to our testers – here are some lessons learned.

Main Menu UI

Expectation: Main Menu UI is easy to navigate.Reality: True! All testers reported that navigating the menus was easy.

Level Select UI

Expectation: Level Select UI clearly described how levels were unlocked and whether or not you had completed a given level.Reality: False! One third of testers reported some degree of confusion about which levels were completed.

What we’ll be changing: We’ll be making the level selection UI prettier and abandoning the “badge” approach of putting an icon on the level button if you’ve completed or perfected a level. There was clearly too much confusion.

Gameplay Mechanics

Expectation: All three tested mechanics would be roughly equally liked.Reality: False! Testers seemed to really engage with the wormhole mechanic, with half citing it as their favorite, and a third citing it as their least favorite. Testers liked all of the mechanics, but cited fuel cells as their least favorite, generally because grabbing extra fuel always seemed like the best option and didn’t present much strategic challenge.

Crate and Wormhole mechanics

Expectation: The three tested mechanics would be relatively easy to understand and learn without a tutorial.Reality: True! Most players cited that it was easy or very easy to learn how the three mechanics worked.

What we’ll be changing: We’ll be adding more wormhole levels since players liked them. We will be more purposeful about our fuel cell level design though, so several of those levels are going to be redesigned to make their fuel component more enjoyable.

Level Difficulty

Expectation: The levels we selected were a good representation of the difficulty spectrum across the whole game.Reality: False! Several players cited “extreme difficulty jumps” across our levels.

Expectation: We selected an easy (A), medium (B), and hard (C) level from each mechanic’s pool of existing levels, so we believed that each letter’s corresponding levels should receive similar difficulty scores.Reality: False! The 1X series of levels were too easy. The 4X series of levels were way too hard. The 2X and 3X series of levels hit our difficulty targets right about on the money.

Level Difficulty/10

A

B

C

1

1.2

2.4

3.4

2

2.6

3.0

6.5

3

2.0

4.8

6.7

4

4.5

6.0

9.0

?

9.3

Expectation: Roughly half of players would unlock the secret bonus level.Reality: False! Only three of our twenty-seven testers unlocked it.

What we’ll be changing: Our levels were too damn hard! We need to refocus our level design on creating more “medium” difficulty levels.

Conclusion

That about wraps it up – we learned a ton from the Jetsam Beta, and look forward to improving things across the board as we approach our holiday season launch window! There are lots of new tickets on the backlog and new ideas swimming through our heads.

We view Jetsam, progression-wise, almost like two different games. Some players will enjoy one section more than the other, but we want to make sure our appeal is as broad as possible by giving both our best attention.

The “campaign” in Jetsam (consisting of the main puzzle missions) can rightly be seen as a game in and of itself. There are progression aspects, the player learns new mechanics, secrets await discovery, and more. Plus – all of that is totally free to play, no strings attached. No ads, no annoyances – just a great offline puzzle game.

We could’ve stopped there, but decided not to. A lot of games these days attempt to bring players to an “endgame” state – the point where most players perceive that they have completed the content available in the game, but discover new, more advanced forms of gameplay they can repeat. This is a valuable goal for us, because we want to give something really rewarding to our most dedicated players, while also giving our more casual players something that keeps them coming back.

To do endgame “right” requires that we design a whole new set of mechanics that veteran players can keep doing forever.

It’s hard to articulate how intimidating “forever” is in game design – it requires thinking about a game in sandbox terms instead of completion terms, which is a pretty radical development mindset shift.

After looking at endgame experiences across a lot of titles and genres, we found that the successful ones usually emphasize some form of player-to-player socialization aspect. We thus concluded that a level editor with sharing tools would be a great place to start. We have so far structured our endgame around building and sharing levels, enabling that socialization in our specific genre’s context. The idea is that an otherwise solo experience becomes dramatically enriched by others once you reach “endgame.”

Work-in-progress: Jetsam’s level editor

Making levels buildable was exciting – we designed a new interface for our game that lets players “stamp” the tiles they want to create onto the play space. Given that we’re making a mobile game, the touch interface is perfect for something like that. Obviously lots more polish is needed, but the basic idea was effective enough that we have been able to develop the main game’s levels using the editor!

But making levels shareable was trickier – essentially we had to compress level data down to the most succinct format possible with some bit-level hacking, which resulted in what we call “level codes” – shareable strings of ASCII text that fully encode levels created by players in the editor. They’re so compressed, in fact, that you could conceivably tweet them…

The way we see it, the main game trains players to understand the sandbox, and the endgame frees them to go wild with it.That’s the philosophy underpinning our endgame experience. We want to empower players to build their own worlds in our game, and we hope that gives players of all stripes reasons to stick around long after they start playing Jetsam.

We’re serving up a delicious taster of Jetsam soon, so if you have an iOS device, sign up for Jetsam’s Beta here.

It will be free to download, and will have a main “campaign” of no less than 50 levels, ranging in difficulty from easy learner levels to downright sadistic brain-benders.

It will also ship with the ability to purchase a full-fledged in-game level editor that allows the player to craft their own levels, using all of the game mechanics we designed and more. Level sharing mechanisms will allow people to show off their levels far and wide, and the best of the best will be collected into level packs that will give players plenty of awesome new content to keep coming back to after launch.

So – how on Earth do you beta test all that?

We concluded that we want to start small. Therefore, our beta test will start off with a small handful of levels – enough to give a taste of what our main game has to offer, but not so much that it spoils the main course when the time comes. We’ll talk plenty about the level editor in the coming months, but mostly through dev blogs and not hands-on testing.

We really want to nail down the level design and make sure we’re properly calibrating the difficulty as the game progresses. We also want to introduce players to a few of the mechanics that we’ve thus far kept under wraps, but do so in an open-ended way that leaves us with the ability to surprise players when they download the finished game.

Each row represents a different mechanic – the top row consists of standard levels, the second row consists of levels where managing your fuel count is important, the third row consists of levels with breakable crates, and the fourth row consists of… a mysterious kind of level you’ll just have to find out about yourself! Difficulty escalates across the columns – so 1A is easier than 1B, which is easier than 1C. That same progression should be true for the other rows as well.

Because the final game will have a lot of secrets and unlockables, we figured we ought to reflect some of that in the beta. As such, the last row’s levels can only be unlocked by beating the levels in the first three rows.

We expect that a typical player will get somewhere between 30 minutes and 1 hour worth of gameplay from our beta test – enough for them to formulate opinions about our game and give us some useful feedback that we can incorporate into the game before we launch it later this year.

Trapped! This player will never beat the level; they destroyed their one way out.

A malicious sequence of wormholes could get players caught in an infinite loop.

Looped! This player will never beat the level; they would loop until they decided to reset the level.

Given that we wanted the game to have a full-fledged level editor, this caused problems. We couldn’t just hand-wave away these scenarios and exclusively design levels where these situations weren’t possible – the community was eventually going to get ahold of the level editor, and they surely wouldn’t be so merciful with it.

So we came up with a game mechanic that solved all of these issues: fuel.

How does fuel work? Well, it’s pretty simple really. Every move costs the player one fuel point. Run out of fuel and you explode. Explode, and the level restarts.

Fuel is what we call a “Hidden Rail” mechanic. Even though it appears benign, its intent is to prevent the player from getting too frustrated with him or herself. Believe it or not, this happens a lot – especially in puzzle games. Our game is pretty openly inspired by the Pokémon Ice Cave puzzles of yore – so posts like this one on Reddit really resonated with us and underscored the need to keep players from getting frustrated with Jetsam.

Running out of fuel lets us loudly tell the player “TRY AGAIN,” which prevents them from reaching a moment of frustration on their own after many failed attempts at a situation that might actually be impossible. We don’t want to waste our players’ limited time by letting them struggle indefinitely against impossible odds. Fuel also constrains the total number of moves players can make on a given level, in effect making levels slightly easier and giving us another difficulty lever to play with. The same level can be radically more or less difficult if we tweak the starting amount of fuel.

Boom! Exploding early and restarting is better than the slow realization that you’re in an impossible place.

Hidden Rails (like Jetsam’s fuel mechanic) can help you as the game’s designer shepherd your player into enjoying your game in the way you intended. By identifying undesirable behavior patterns, you can use the invisible hand of design to break players of those behaviors – whether it’s through the sweet embrace of explody death as in Jetsam, or otherwise.

Constraints, counterintuitively, can often bring more creativity to bear. Adding fuel in as a Hidden Rail actually gave us more ideas, which led to further developments in the game, including collectible fuel packs, fuel maximization challenges, and a whole host of other things beyond the scope of this dev blog post.

We can’t wait to tell you about those soon!

If you liked this blog post, follow us on Twitter and check back regularly here for updates. If you have an iOS device, sign up for our iOS TestFlight Beta here. We would love for you to be part of our development journey.

One of the trickiest parts of building Jetsam thus far has been conveying the game’s core concepts through our level design.

For those of you not in the know, Jetsam is a 2D puzzle game with a simple user interaction forming the game’s core gameplay loop – swipe in a direction, and Jetsam will move indefinitely in that direction until he collides with something that stops him.

But things get more complicated, quickly. See that gear in the gif above? Jetsam has to collect it. And on every level, he has to then proceed forward to our exit – the purple circle you can also see. That exit is not “active” until the gear is collected.

How on earth do you convey ALL OF THAT to the player without being obnoxious in your tutorial?

We opted for something a little devious – breaking our teaching out into two levels, and focusing on different things in each. Let’s break down the first learner level’s structure a bit to explain.

Despite Learner Level 1 taking on the standard 9×9 grid size, Jetsam is physically restricted to the positions marked by squares in the screenshot above. He cannot move elsewhere, even though he can technically “travel across” a lot of the tiles. By starting him in the middle of the lower 3×9 rectangle, we open up some other movement options for him – that way the player doesn’t feel as restricted as they actually are.

The level design reaches a critical point in the right column. Jetsam can move relatively freely through the bottom portion of the level, but if he wants to move anywhere else, he must move upward in the right column. This chokepoint is intentional – no matter what inputs the player makes, they will ALWAYS collect the gear when they move to the upper part of the level, learning that it is collectible and safe to pass through in the process.

From there, we’ve made Jetsam’s possible positions entirely linear – we don’t want to try to teach too much in one sitting! After collecting the gear, he lands in the top right corner. His only options from there are to either go back the way he came, or go left to the top left corner. From there, the linearity is pretty self-explanatory – he eventually ends up on the purple tile, successfully completing the level, which gives the player some positive feedback for figuring out what’s going on.

But does the player really understand the cause-and-effect relationship between collecting the gear and exiting the level on the purple tile? Probably not – hence, Learner Level 2.

Jetsam moves through Learner Level 2

The philosophy here builds upon the technique used in Learner Level 1 – restricted movement, a chokepoint, and an intended teaching.

In this case, we want the player to learn that simply passing through the exit prior to collecting the gear will not complete the level – the act of collecting the gear actually “activates” the exit. The easiest way to teach that? Make the player pass through the unactivated exit!

The player’s immediate “wtf” reaction when the level isn’t complete on the first up swipe is designed to set up for an Aha! moment (those critical moments in puzzle games where the player learns something), in this case the realization that grabbing the gear activates the exit. By segmenting the upper part of the level, we call attention to the separateness of the gear and the exit, hopefully prompting that lightbulb to come on when they grab the easily-positioned gear. Then, it’s only a matter of time until they revisit the exit’s location and successfully complete the level (ideally they will also notice that the exit looks different the moment they collect the gear, but that isn’t necessary for their training to be complete).

Structure-wise, this level foregoes an opening “free movement” section and opens straight into the chokepoint – the reason being that it builds on the past level, and we don’t want our player to get distracted and forget about what they learned in Learner Level 1 upon starting this one (they are meant to be played in sequence, after all). However, they are free to conquer the top section of the level in multiple ways – restoring their feeling of freedom for a short spell. By carefully stitching together linear and free-moving sections, the tutorial feels less like a tutorial and more like a playground.

And so, two easy levels teach three rules:

The gear is collectible

The purple blob is the level exit

The exit doesn’t turn on until you’ve collected the gear

Success! We’ve taught our player how to navigate our somewhat-complicated game, and never issued a single text prompt to tell them anything directly. They got to figure it out themself – and that’s what you want in a puzzle game. Hopefully what we’ve learned from tuning our game can help you tune yours – and you can design some kick-ass learner levels that help convert players who download your game into players who finish it.

If you liked this blog post, follow us on Twitter and check back regularly here for updates. If you have an iOS device, sign up for our iOS TestFlight Beta here. We can’t wait to test our design and puzzlecraft with a wider audience, and would love for you to be part of it.

It’s been a while since we last updated the blog – life has a certain way of sneaking up on you when game dev isn’t your full-time job. So that’s what we want to talk about today.

Making a game is an incredibly powerful experience – there’s something special about building something for people to play with, which is entirely distinct from building something people use for work or for other purposes. The person playing your game wants to play it. They’re motivated. They show up. They cooperate with other players to build community. They compete with each other too. They find bugs, and sometimes exploit them to do absolutely insane things that you can never, ever even hope to anticipate as the game’s developer.

When you make a game, you’re essentially giving a bunch of passionate, talented, and slightly crazy people a toybox, and saying “go.”

The joy of building something for an audience like that can consume you. You find your idle time punctuated by a constant stream of creative thoughts – should I add that new mechanic? Tweak that level? Expand the plotline? And every now and then, your wandering mind strikes gold and the intoxicating force of inspiration keeps you up late into the night, distracts you at work, or makes you unwittingly skip a meal.

But at a certain point in any long-lived project, that fountain of creativity shuts off.

And then, to make it worse, we tell ourselves that it’s okay.

We say we don’t have the time to keep working on our game – work is too busy, life events need our focus instead. We criticize ourselves for lacking the discipline to finish, but secretly we know that working on our games with anything resembling “discipline” would probably suck the fun out of working on them. Where does the inspiration for writing out tickets in a task manager come from? Where’s the joy of creation in writing unit tests? Where’s the excitement in fixing that ONE. STUPID. BUG?

We’ve reached what I’ve started calling the “Chasm of Death.”

This is the place where most games die.

The emotions, skills, and motivations that brought us to this chasm will not get us past it. Our excitement about what our game could be has been dampened by an all-too-real understanding of what our game currently is, and the realization that there’s a hell of a lot of distance between those two states.

All too often, this is because we now have to do something unpleasant. But not just any unpleasant thing – after all, we’ve slain a number of bugs and recovered from many prior setbacks to get to this point. Now, we’re facing something so unpleasant that we’d rather just go back to where we were before this game, and forget the whole experiment. Rewrite the whole physics engine? Redo the entire story arc of the protagonist? Devise a brand new algorithm? Refactor the netcode? Throw out every level we’ve already built and build new ones?

These are Herculean tasks for anyone, but become especially daunting and scary if building this game isn’t your full-time job. If you have other responsibilities, hobbies, and desires, you’re instantly in a pit – it’s easy to justify spending free time making your game when it’s fun, but this? This?!

The Chasm of Death is where we found ourselves two years ago, shortly after we posted our first, super naïve and enthusiastic dev blog. For Jetsam’s level editor to work out – what we hoped to be its defining feature as a puzzle game – we had to build a level solver. To build that solver, we’d have to abstract away a lot of the game logic into something memory-light that would be usable in a giant, gory BFS algorithm meant to run on mobile phones. A scary task indeed. We just wanted to make puzzles.

So, we let the dream fizzle. An early version of the app sat on our phones, untouched, unloved, and basically forgotten.

Fast-forward two years, and I found myself getting drinks to catch up with a former coworker of mine (who I had shown some early builds of Jetsam). This coworker mentioned the game – she asked me if it was in the App Store yet, or how it was going.

Embarrassment and regret about the game’s then-current state stung me hard.

In that moment, Jetsam came back to life. I launched the app, and to my surprise it still worked (iOS updates had me worried). I handed my phone across the table and watched my former coworker play some of the demo levels. She really enjoyed them.

Instantly, a fire was re-lit. I called up my friend who had been helping with the game, and instructed him to clear his Thursday afternoons for the foreseeable future. Fortunately, he agreed.

I have certainly improved as a developer in the intervening years – as has my friend. So we approached our old problems with a totally fresh perspective, and an understanding that we wanted to get to a point where lots of people could play our game. We weren’t going to let a pesky level solver stand in the way.

Several weeks of work later, our lovable hero has gotten quite the facelift. We went crazy with particle effects, animations, and iconography. We’re also nearing final graphics for our levels, and currently assembling a playable beta of 11 levels to test our design and puzzlecraft with a wider audience (iOS TestFlight Beta signup form here). The first part of our level solver monster has been slain – and we’ll definitely be blogging about that soon.

Meanwhile, check out one of the newest levels we’ve added to the game’s first world! Swiping moves you indefinitely in the swipe’s direction until you hit something to stop. The goal of each level is to collect the gear and get to the exit before consuming all of your swipe fuel.

When all is said and done, we were really lucky that one person who cared gave us the spark that reignited Jetsam’s boosters. So we’d like to be “that person” for you.

If you have a “dead” game you want someone to look at – and we know you do – post about it in the comments! We’ll make sure to reply to everyone.

If you liked this blog post, follow us on Twitter and check back regularly here for updates. You won’t have to wait two years for the next one.