This post gives programming tips for making games fast, usually in a fixed time frame: how to plan and focus, strategies for dealing with tricky code, ways to get unstuck, and what you can do between prototyping projects to get better at it.
In November 2013, two colleagues* and I made 30 games. Although I have done some game prototyping before, working on so many games in such a short period gave me some insights I did not have before. As I said to a friend, it’s like watching a television series in a few days, instead of watching each episode week by week – you just see different things.
In this article, I collect some of these observations in the form of a set of tips. I kind-of assume you are already familiar with the classical How to prototype a game in 7 days, which describes prototyping from a more general point of view. In some ways, this is a programming-specific extension to the ideas presented there.
Making something very quickly is a lot of fun. Day-to-day (“proper”) development can be slow progress, with the final products months (or years) away. Tinkering with an idea in your spare time gives you insight, but its outcome is vague.
Building a rapid-prototype gives you immediate satisfaction. It gives you focus; it allows you to channel energy, and gear up.
But you need to get out of development mode, and out of tinkering mode. You have to know you will throw work away, and waste some effort. You have to know that sometimes you don’t need the best decision – you just need to make it straight away. You have to let go of perfection and details.
You have to get to the core.
Know and remember the goal
The reasons for building a prototype are varied. Here are a few examples:
To have a finished game at the end of a fixed time (such as in a jam).
To have the core of a game that is fun (such as in a pre-preproduction phase of a project).
To select the best idea from a set of alternatives.
To test the technical feasibility of an idea (where the idea can be anything from a full game to a graphics technique or AI algorithm).
The best way to work and make decisions depends on this goal. Be clear about what the goal is, and remind yourself frequently.
If a task is not moving you closer to your goal, don’t do it.
Find the essence of your idea, and schedule enough time for it
An essence can be a mechanic, a style, a theme, a setting, an emotional experience. Find it, and, separate it from the supporting elements.
When you schedule, assign time accordingly. If your essence is a new mechanic, you want to have enough time to explore it; you don’t want to spend all your time building levels or coding atmospheric shaders.
Take creative risks, but avoid technical risks
If a game prototype fails, it should be because it’s boring, not because of a technical problem. (Except of course when the goal is technical prototyping). You want your prototypes to be finished, and allow you and your team to explore the essence, and pass this on to players.
Avoid tricky, sophisticated data structures and algorithms.
Avoid sophisticated systems and architectures.
Avoid trying to make things too fast.
Avoid technical feats that do not improve gameplay.
B. Planning and Process
Think about content
Ask yourself a few questions about the content your game will need to get a feel for its scope.
How much do you need to build?
How will you inject variety?
At what stage during development should it go in?
What fallbacks can be used? If you run out of time, have a plan to get essential content in. Will it work to make the boss simply a pink version of the level-enemies? Could boxes work instead of characters?
Work out a rough plan, and put down some time estimates.
Check your plan against the goal, and remove things that do not take you closer to your goal.
Check your plan against the essence of your idea, and verify that you allocated enough time to explore the essence.
Identify the sections where your estimates are likely to be out. Keep fallbacks in mind; consider reducing their impact / role / complexity. Think of placeholder strategies (see below).
Follow an implementation strategy, but adapt it to follow the ebbs and flow of the creative process
Here is the order I would suggest you follow. It puts the most important things first, keeping in mind (typical) dependencies.
Get something on screen
Implement top level game
Get user controls in
Implement a minimalist level
Implement toy logic
Implement game goal
Prototyping is not coding a specification, though. Remember that there will be false starts, and that you will have to adapt. When necessary, change the plan and update the schedule.
Know and avoid time drains
Certain things just suck up a disproportionate amount of time. It’s useful to recognise these, and make sure you schedule enough time, have fallbacks, and keep it even simpler than you would other parts of the game. (Typical time drains depend of course on the type of game; the examples below are just to give you a sense of what I mean).
Controls. In actions games, controls can take a long time to get playable (never mind to getnice).
Balance. In simulation games, balancing takes a long time. Keep the number of systems low; keep the number of variables low. And leave enough time to balance them out.
GUI. In information-heavy games, the GUI can suck up all your time. Finding audio can take a long time – don’t add it unless it’s essential, or you already know where the sounds you need are.
Animation. Animation can slurp up a lot of time: consider using snap poses, or gliding when appropriate.
Procedural content generation. Tweaking algorithms for content generation can be time consuming. Unlike other algorithms, the specifications for content generation algorithms are hard to make objective.
Consider using serial development (programming pipelines)
For teams with more than one programmer, it’s common to divide a project up in components, and dish each one out to a separate programmer, so that everyone works in parallel.
There is another method, and it is worth trying it out once or twice and see if it works for your team.
With this approach, most of the code passes through all the programmers, in a staggered fashion. The first programmer will implement the broad strokes of the project; the next one will start filling in details, and so on. Instead of owning a piece of the code, each programmer owns the entire project for a time slice (these slices may overlap somewhat, of course). It works especially well if you use a lot of placeholder code.
The nice thing about the approach is that it reduces discussions about interfaces. In your time slot, if you need an interface change, you make it. It won’t break other programmers’ code because you are the only programmer. (It still works with overlapping slices, but you need more care, and a plan.)
The down side is of course the design you end up with may be quite bad. It does not matter, though, because the design is not important. It seems a bit wasteful, since there are times where some programmers don’t program.
(Of course, this is meant to split off programming tasks; it would be a shame if you do this with other creative activities. In many prototyping projects programmers are also game designers; this does not apply when you have your design hats on!)
Don’t get stuck
Several things can make you feel stuck: bugs, tricky-to-implement code, unusable and fiddly controls, sheer amounts of content, level design.
When you are in the mud, get out of it, and move on. Here are a few things you could do when stuck:
Ignore tricky bugs that do not ruin gameplay.
Cut culprit features; reduce content.
Use a simpler placeholder algorithm or approximation.
Use brute force with lookup – a handy technique to design correct, fast algorithms quickly at the expense of memory and some extra time spent outside the main loop (maybe even outside the game altogether).
Fake it. Monsters that respond to players look more intelligent than monsters that don’t, regardless of how intelligent they actually are.
Hard-code it. A long array of monster sequences is much easier to code than a sophisticated wave generation algorithm.
C. Code Design and Implementation
Use placeholder code
Use placeholder code wherever you can so that you can move onto core gameplay earlier. Once you have that nailed, you can systematically replace placeholder code with code that does a better job. What is nice about this approach is that once you have all the systems in and working with your core gameplay, it is much easier to prioritize tasks according to their gameplay impact.
Here are some typical candidates for placeholder code.
Controls In some games, controls are extremely important. But if second-rate controls won’t cripple the player’s experience, and it’s faster to implement, implement that first. For example, in many sliding puzzle games, the best controls allow you to use the mouse (or finger) to slide around objects. This can be a difficult scheme to implement. On the other hand, mouse selection with arrow keys or buttons can be quick to implement, allowing you to get onto other gameplay elements faster.
Feedback Feedback is important, it provides information and improves the experience. But first do something simple: dialogs and messages, colour changes, flashes or beeps.
AI AI is difficult: it’s hard to predict whether any given strategy will work. The following can be use for AI placeholders before you start implementing the final algorithm:
AI using simple heuristics can stand in for look-ahead AI, or vice-vera, whatever is easier.
AI that cannot play all possible moves.
Procedural Content If your game relies on procedural content; you can use the following types of stand-ins:
Purely random content.
Approximation Crude approximations can often be very useful. Here are some examples:
Using collision spheres instead of collision meshes.
Using linear approximations for curves.
Use design Shortcuts
[USE WITH CARE] You should avoid god-like classes in production code, but in prototypes, they can save a large amount of time. Instead of having a monster and player that extends from actor, dump all the code in actor, and use the familiar switch-atrocity instead of polymorphism.
Mistrust any data structure that is more sophisticated than a dynamic list, especially if you need to implement it yourself. Focus on simple, safe, and easy to understand data structures and algorithms.
Avoid false design shortcuts
Rules for naming and organisation in prototypes don’t change.
Unnecessary exposure of data (using public fields) can bite you even in the short span of a day or two. It’s simply not that easy to remember whether setting health to zero will kill a monster, or whether you also need to destroy it yourself.
States machines (or anything that keeps track of states) almost always need more states than you think. If you try to use a shortcut instead of an appropriate abstraction, there is a big chance that you will get entangled in a buggy if-else mess.
Manage your tweakables
Make the game easier to fine-tune by keeping tweakables in one place.
Not every value needs to be tweaked; don’t be afraid to make them constants. But put them in a central place. Do not use magic numbers.
Carefully name your tweakables.
When you design a tweakable system, make sure that the exposed variables have clear and linear effects. Subtle tweakables are best hidden away (or left until the end). It’s better to make a system that is clear to understand, but cannot be controlled completely.
Write readable code
Throwaway code should not be engineered to the highest standards possible.
However, it still needs to serve the purpose of communicating your intention, especially if you work in a team. A muddle of code can waste a lot of time. The most useful things to do when writing code for rapid development:
Name things properly.
Avoid deep hierarchies and complicated dependencies.
Keep methods damn short.
Limit the number of “smart” classes. A smart class is anything that handles game logic. In smaller, volatile projects, it’s better to centralise logic and reserve helper classes for presentation. It makes it easier to make drastic changes. As long as you keep methodsshort.
Use implementation patterns
An implementation pattern is a standard way of doing things. As an example, here is the implementation pattern of a game’s main file:
By using standards, you will reduce the time you need to spend on thinking, and then quibbling over alternatives. Implementation patterns are useful for production code too, but prototyping patterns are different: they emphasise simplicity and clarity, rather than OOP re-use and efficiency.
You can often set up patterns as templates that you can use during prototyping. If you can, do it!
(You collect or design these standards between prototyping stints – see below).
As another example, here is an implementation pattern for changing a variable’s value smoothly over time.
If you build prototypes regularly, you can put yourself at a great advantage by doing some stuff in-between prototyping stints.
After you finish with a prototype, do a post mortem, and identify the good and the bad. Review your notes shortly before your next prototyping project.
Build a library of code that enables rapid prototyping
This is different from your library that’s shared between production projects. You can use techniques that are a bit slow, or make some platform assumptions, or uses other technology that you cannot normally use for production code.
As an example, we rendered the grid in many of our games by drawing on a texture pixel-by-pixel, instead of using sprites. This method is fine for prototyping, but too slow for the final product.
Identify implementation patterns, bug patterns and time sinks
After a few prototypes, you may begin to see some patterns emerge. Make a list, and use this to inform what to put in your library.
Can you codify implementation patterns as templates in your library? Could you design better data structures to avoid some bug pattern? Can you give yourself a head start on a time sink by adding to your library?