This post is about Soulcaster I, and how it evolved from the first stages. This was the first project I designed and built from scratch, and my philosophy has been to keep the design as minimal as possible. Rather than write a lengthy design document and try to detail all the enemies, their behaviors, the items, etc., I just jotted down some rough ideas and used it as a starting point. To be extra careful, I didn’t even use Word, I just did plain text in Notepad, and didn’t bother with formatting–or even capitalization. This was not intended to be a blueprint I would refer to throughout the project. Continue Reading…
Archives For Articles
Essays, thoughts and how-to articles
I don’t have a CS degree. I learned programming from books, starting with QBasic in high school, then C, then enough C++ to scare me off programming for a few years, and finally the siren’s call of C# has lured me back. My education is lacking in some areas, so I try to make up for this by reading as much as possible. A couple of my favorite books are Object-Oriented Design Heuristics and Code Complete.
As part of my ongoing research, I listened to Jonathan Blow’s talk at Berkeley last month. It was eye-opening, and packed with great advice, but more importantly, it was reassuring.
After listening, I felt less ashamed of my “shooting from the hip” approach to programming. I don’t write formal specs, I just code incrementally and refactor as needed. When the project is done, parts are pretty messy. There are always a few things I would have done differently if I had time to undertake a major refactoring, but hey, the game works, and it’s done.
Why was this talk reassuring? Jon is going out on a limb to challenge some computer science orthodoxy. Despite my not having taken classes in programming, I’ve been exposed to plenty of this in books and online discussions. There is such a value placed on the Right Way to program something that the cost to program it is often overlooked. The Right Way can be wrong. The heresy! And he’s right.
Here are the main points I took home from the talk:
1. A generalized system is usually worse than a specific system.
There are times when I’m programming a new feature, and an angel on my shoulder says, “Hey, you’ll need this for more than one thing. Let’s make it just a bit more abstract, and not so closely tied to this one class. Remember loose coupling? Eat your peas and carrots.”
I’ve heard to this practice referred to as “gold plating” … adding something you MIGHT need later, but don’t need NOW. The advantages of an abstract, general solution? I’m not sure, but it seems to look nicer. The cost? a) time to think about potential other situations to accommodate, b) more time to code these solutions, c) more cases to test for bugs, d) the method now has a name like ProcessInput() which gives no clue as to what it does.
Jon responds to one of the comments on his post on the talk with: “Why don’t you just, you know, let the two objects communicate?” I need a bronze engraving of this.
At some point during development of Escape Goat, I decided it would be good to have two projects: the game, and the “engine.” All the low-level stuff like sprites, player input, and audio could go into the engine, and the stuff specific to the game, well that belongs in the game. What a mistake. Now I have all kinds of interface definitions just so the engine can operate without knowledge of the game’s types. Which means updating everything in multiple places for every interface change. (Lately, I have been discretely smuggling classes from the engine into the game project.) The costs have been real. The benefits? Who knows, I can’t think of one writing this here and now.
Imprinted in my mind now: Start with the simple, specific solution, no matter what coupling this might introduce. And if enough parts of the system use this solution, think about refactoring it into something general.
You can always go from two similar specifics to one general. It can wait.
2. Don’t optimize early on.
37Signals likes to say “Ignore the details early on.” I think this is one of those details. Wait until you have performance problems, then find the parts of the code that are slow and work on them for a while.
I have a more positive Escape Goat anecdote for this one.
Case in point: Garbage collection. My current build generates tons of garbage. I haven’ t even profiled it, but it’s going to be nasty if I do. Each room change builds a new copy of everything in the room, and most room actors are composites of some very complex types. When a TNT keg explodes, every debris particle gets freshly minted, then discarded 30 frames later to be gobbled up by the GC. Horrible practice, right? I should be pooling and reusing these, right? Nah, I’ll pass. There is zero noticeable performance hit, because my game is a puzzle platformer with a few hundred actors on-screen, not a bullet hell shmup with thousands. The pooling system could have taken me a week, and now I get to spend the week on other stuff.
3. Use straight-line code instead of function calls for single instances.
This one hits close to home because one of my favorite programming principles used to be, “When you’re going to add a comment describing what the next bit of code does, instead make it into its own function, with the comment as the function name.” I loved this principle so much that it’s my only programming tip tweet.
And… I’m not letting it go just yet. I still think this is valuable when designing new code, because in the function call you decide what gets passed in, and it narrows the scope of the problem to a few key variables. The cost of doing business this way is that you end up with a bunch of small functions that are only used in one place, and you have to bounce around when stepping through code.
My compromise? I’ll keep doing this, but after the code is working, I’ll re-inline those functions back to the parent method. I like solving problems in the safety of a smaller scope, but once it’s working, it’s back to the Mega Function for you. Pro Tip: You can even simulate the function call with an opening curly brace to open a new scope.
Bottom line: There is a beauty in some ugly code, when you can see the time saved by not handcrafting the ideal, elegant solution.
The results are in after last night’s playtest, and Escape Goat Alpha 5 is the new face of drinkability. OK, what I meant to say was that the testers really understood the puzzles better, enjoyed experimenting, and felt rewarded when they completed them. This was a big changes from Alpha 4, where testers found several of the puzzles so confusing they resorted to trial-and-error.
So what changed?
I have a new approach to puzzle design. This post is probably a version of something the masters have already discussed at length, and I’m pretending like I’ve discovered the wheel. But bear with me, I’m a do-it-yourselfer (to a fault) and maybe there’s some original research in here. Read on.
This all started with a study project where I spent an afternoon playing various puzzle games on Kongregate and Newgrounds. (I know, this is totally like real work.) I wanted to see how games dealt with the Unsolvable Puzzle Dilemma–how they let the player know it was time to restart a level after it couldn’t be solved anymore. After playing about 20 games, I found that each of them falls neatly into one of two categories:
These are the reparable puzzles, where you can undo moves all the way back to the original state of the puzzle. There is no unsolvable state. A “restart” of the puzzle only serves as a shortcut. Examples: Rubik’s Cube, simple mazes, slider puzzles. Portal 2 is a Type 1 puzzle; even though the environment permanently changes as you progress through a room, it’s never in an unsolvable state. There’s no need for a restart button in Type 1 puzzles.
These puzzles have consequences for every move. After you’ve made your moves, you’ve either solved the puzzle or failed it. A restart is now necessary. Any game featuring destructible environments, blocks that merge together, switches that can only be used once… these are all Type 2′s. Some examples are: Sudoku, Lemmings, Adventures of Lolo.
Categorizing games into one of these two camps makes things more straightforward. Type 1′s are all about experimentation and movement within the puzzle, maybe inching toward completion 5 steps forward and 4 steps back. Type 2′s are about coming up with a plan, executing the plan, and observing the results. They are different enough that I bet if you ran a brain scan of people solving either puzzle type, different parts of the brain would light up for each type.
So wouldn’t Braid be a great example of type 1? I mean, you can rewind time as much as you want.
Not so fast. Some levels in Braid are Type 1, and some are what I’m going to call:
This is the black magic, the unholy witch’s brew combining both styles. Parts of the puzzle are reparable, and parts are not.
Remember the levels in Braid with the glowy green objects? Some of them got permanently messed up if you didn’t do things in a certain order. And yet you could still rewind time… you had a sense of being able to repair parts of the puzzle, yet not the whole thing. And that’s what made these puzzles some of the hardest: once you knew you could make a permanent mistake that your magic time rewinding couldn’t fix, you were forced to question whether you had messed up the puzzle.
Braid did a good job of conveying the unsolvable state, usually with a green door that was shut, or a key that was destroyed and unusable. You could look at the door and “get it.” It was time to exit the level and restart fresh.
Back to Escape Goat. I applied this paradigm to the new levels I made for yesterday’s playtest by making sure each puzzle room fit squarely in either Type 1 or Type 2. Rooms that had to be restarted made it clear that there was nothing left to do–no toggle switches that would shift things back and forth. (I even added the Back button as a hotkey to restart the room. The presence of a hotkey should clue players in that restarting is a way of life for some of these puzzles and is nothing to be ashamed of.)
Am I going to have any Type 3 puzzles? Probably, but not until the final stages. I recognize that this takes the most brainpower and shouldn’t be foisted on the player in the first ten minutes of the game.
When I set out to make Escape Goat, I had no idea all the things I would need to learn, least of all this categorization of puzzles. I would absolutely love to hear back from you experienced puzzle game designers. I’m probably just scratching the surface here.
Tomorrow is another Escape Goat Alpha Playtest Party! That means tonight is a blog post on playtesting, lightweight, indie-style. In other words: you’ve got friends, some of them play lots of games and others don’t, and you want both types to try out your game and give you their valuable feedback.
Throwing a party is a great way to get this free labor from your friends and family without feeling too guilty about it. Just provide them with enough food and drinks to ease your conscience. And it’s not such bad work either, playtesting games.
Now I’m not the master of testing, and a lot of these tips probably come straight from Jesse Schell’s The Art of Game Design. For Soulcaster I, II, and Escape Goat I’ve probably hosted about ten proper playtesting parties, and here’s what I’ve picked up from them.
First question: When to start playtesting? There’s a point during development where you think things have come together enough that you have what could, in some stretch of the imagination, be described as a “game.” Maybe it doesn’t have a beginning/middle/end, maybe the graphics are placeholder and there’s no audio yet, but there’s something there and you can put it in someone’s hands to be judged. This thing that has until now been called a prototype has now somehow become a game. If you’re there, let’s get started.
1. Test it yourself beforehand. If you’re making tons of last minute changes like I do on the day of the party, just be sure you’ve given it one full run-through to make sure you didn’t accidentally delete the switch that opens the door to level 3.
2. Have a cohost. A significant other or close friend will be needed to greet guests, cook food, send out for more beer, talk to the cops, etc. You’ll be way too engrossed in the testing to be a proper host, so don’t even try.
3. Fresh meat first. Anyone who hasn’t seen the game yet gets first dibs, lest they catch a glimpse of someone else’s session and have their first impression become corrupted. If someone else is testing while a newbie arrives, have your cohost distract him or her for a bit. “Have you seen the new patch changes in HON?” for example.
4. Tell your tester to speak their mind while playing. Anything that’s running through their head. Here are some direct quotes from the last playtest:
- “Wait, did that do anything?” (after skull activation)
- “That’s the same message that I read earlier.” (story tablet at region 1 entrance)
- “Did that guy just hit something?” (reaper in level 3 third room)
- “I like that he looks really, really scared” (idle edge sprite for goat)
- “Why do the blocks have color on them?”
This way, when a player is wandering back and forth in the opening area, you can get an idea of why. Can they not find the exit, or do they just like the walk animation? Personally I hope it’s the latter, because the two-frame goat run visual took me freaking forever.
5. Remain silent. No hints or explanations of how things work. You’re a fly on the wall. (The exception would be a known glitch that hides information and could cost time, for example a tutorial notification that isn’t showing up in this version for some reason, but will be in the final version.)
6. Take notes.
7. Have a roundtable discussion afterwards. If you’re making games, odds are you have friends who have strong opinions on games. This is one of my favorite parts of the testing party. I’m surprised to hear what people liked and didn’t like, what they would like to see in the next version.
8. Process your notes and thoughts the next day. I personally love using BlueMind for this, but maybe that’s because I’m a recovering organization software junkie. The point is to get a nice list of bugs to be fixed, major remaining design issues, principles on designing levels going forward, etc.
So that’s it, hope this has been somewhat helpful, and if you’ve got additional tips, please post them in the comments!
Last Saturday was the Escape Goat Alpha 4 playtest, and the next big design challenge has shown itself and thrown down the gauntlet. It’s a hard problem to define, so I’m hoping that writing this will clear it up in my head. It might also make an interesting story for anyone who wants to see the gruesome behind-the-scenes of game design.
Escape Goat is a puzzle game at heart. You’re there in a room, pitted against obstacles and mechanisms, trying to reach the goal. My playtest version of the game had several stages of training, where the player learns the game mechanics, controls, and how the various gadgets operate, before being plunged into a puzzle that takes some problem-solving skills to beat. The first few puzzles would basically solve themselves in a fun and visual exciting way while teaching you how the game works. These rooms were fun to build–basically single-button Rube Goldberg machines that make you go WHOA, COOL and that’s about it. Then come the puzzle rooms.
By their nature, puzzles can enter an unsolvable state if you mess up. There are blocks that drop and can’t be lifted again, crates that break, switches that stay permanently thrown. If you do things in the wrong order, you’re cooked and have to retry the room.
The problem is, the game doesn’t know when it’s in that state. Play testers were poking around in a room for a few minutes trying to solve the puzzle even though it was no longer possible. This just can’t happen in the release version of the game–the lack of feedback is frustrating and boring. The player has to get the memo that the room is now unsolvable, and a retry is required. Detecting and delivering this message is the big challenge I’m facing.
Let’s look at one room in particular. This room is the first one where you can actually “lose.” There aren’t many interactive elements, just the three buttons on the top level. The green one operates the push block on the left, the blue one lifts the platforms in the middle level, and the red one releases the top (red) platforms. The proper solution is to hit the buttons from left to right, which transforms the level like this:
The blue platforms lift to catch the blocks, allowing access to the yellow button on the bottom right, which opens up the passage to the left. If you hit the red button first, here’s what happens:
The blocks have fallen and are blocking access to the yellow button on the bottom right. You’re screwed. You have to restart the level from the pause menu or by leaving the room and re-entering. But… how can a new player know the puzzle is unsolvable now? There’s no message. You’re just left wondering what to do next.
I’ve thought of a few solutions:
- Design the puzzles so they can’t enter an unsolvable state. Well, this would make for a pretty easy game.
- Design the puzzles so that something kills the player when they enter an unsolvable state. This is a better solution than what we have now, but it means inserting a lot of violent gadgets (lightning machines, buzz saws) and having to account for all locations the player could be on the screen.
- The game checks to see if it is unsolvable and delivers a message if so. Checking programmatically is something that would add months to the project, if not years, so that’s out of the question. But maybe the room could have a bit of hidden data built in to detect for states that would make the room unsolvable. For example, I could have a hidden block detector checking the area in front of the switch to see if the blocks have fallen there.
- Kill the player instantly, which would be really jarring. No good.
- Bring up an on-screen text sprite, which is more subtle, but not very thematically interesting.
- Have a gadget that activates in the unsolvable state and allows quick restarting of the level, a cool thematic solution but maybe a bit too abstract compared with the on-screen text.
While I work on Escape Goat, my Soulcaster engine is collecting dust. Let’s put it to use. I’m looking for a skilled designer to make a complete, original game using the awesome technology I’ve developed for Soulcaster I & II. Here are some of the capabilities already built in:
- Behavior based AI including dynamic pathfinding
- 2D tile based maps, with animation and parallax layers
- Versatile and blazing fast level editor in Windows
- Sprite and actor handling, including collision detection, particle systems
- Creature-based controller input abstraction
- Multiplayer support (a by-product of an experiment during the production of Soulcaster II)
- Menus, animating text, tooltips and other UI goodie
Your game probably has some mechanics not currently in the engine. For example, you may want pixel-based movement instead of smooth grid-based movement. That shouldn’t be too hard to add in. In other words, I’m willing to make small modifications to fit the game design. So long as you want to make a top-view, 2D sprite based game, where all entities are no larger than the tile size of 16×16, it should be workable. If your idea is an RPG that requires a separate battle mode, it’s not going to work–a whole new system is too much for me to add.
Collaboration terms are negotiable, such as sharing of revenue and ownership of IP.
Interested? Have a design that is just too awesome to be sitting around? Use the contact form and get in touch. Link me to any game design or art you have online, and tell me about your background in game design.
I’ve worked on several new features over the last two weeks, including an upgrade system and a shopping menu. Nevertheless, as I build test maps I continue to find ways to tweak pathfinding so that the monsters behave intelligently, smoothly and aggressively.
One issue is the weight given to creature positions on the map. I give walls and trenches a weight of 9999 to ensure they are never a preferable part of the path. I also poll the creature positions to give creatures some weight–without this, the monsters don’t bother to move around one another, and it’s just not scary.
Fine-tuning of the weight added to squares occupied by creatures has paid off. Below are three examples of too little, too much, and just right. All three screens were taken at the same point after game start, and you can see how many more monsters can reach the player with the proper weight bias.
Monsters are shaded different colors to show their current movement mode:
- Red: Full pathfinding (cannot see target)
- Yellow: Uncrowding (cannot reach target and only has one direction to move)
- Violet: Has line-of-sight to target and moves toward it Gauntlet-style
- Blue: Adjacent to target (attacks repeatedly)
- Black: Crowded, no movement options (stunned briefly)
- Gauntlet style, or simple line-of-sight. If there is no obstacle between the monster and his prey, he’ll just move forward. This looks good in an open area, and doesn’t use much CPU. However, if there are walls between you and the monster, you’ll see the Gauntlet Effect: monsters crowding against the wall trying in vain to break through it.
- Full pathing with the Djikstra algorithm. I can’t use the A* algorithm because monsters are going to path to the summoned creatures, hoping to kill them, as well as the main character. A* is much faster because it can favor the direction towards the target “as the crow flies” and be right most of the time, saving lots of CPU. Djikstra maps a path to every walkable square within a radius and takes a ton of CPU.
How Pathing Works
Imagine you are stuck in a labyrinth where the walls are blocks of equal size. At some point, a wizard is going to warp you to a random place in the labyrinth and unleash an undead man-bear on you. It’s vital that you find the shortest path back to the exit from wherever you find yourself. Fortunately, you have a piece of chalk with you.
You use the chalk to draw a 1 on the first tile next to the exit. This means there is1 step left until the exit. You then turn left and mark this tile with a 2, move forward and mark this tile with a 3, and so forth. When you reach a dead end, you backtrack until you find a tile that hasn’t been marked yet, and continue your path there. All you are doing is marking tiles with the value of the adjacent tile +1.
Pretty soon you’ll have marked every floor tile in the maze with a number. When you get teleported, you can see the numbers on all adjacent tiles, and move to the tile with the lower number. No guessing, just following the path you’ve already found.
Solution: Large Radius Pathing from Heroes, Line of Sight and Uncrowding
The shortest path from point A to point B is also the shortest path from point B to point A. So rather than have the monsters calculate their own paths, I have the player and summons calculate a large path using their position as the source. The monsters then request this path data from the gamestate, and can pick the closest target and move there. Because there are only 4 possible heroes on screen at once, this cuts down tremendously on CPU usage.
Gauntlet-style movement still has its place. When the monster has a line of sight to a target, he won’t even bother to use pathing anymore. This not only takes less CPU, but it looks better. Pathing in a large open space can be awkward because the monsters usually move in large horizontal and vertical paths, rather than zigzagging to close the distance to their target.
Another optimization is uncrowding. Before a monster even attempts to use pathing or line-of-sight movement, he checks to see if he can even move in any direction. If not, he gets stunned for 30 frames under the assumption that it’ll be a while before a path opens up. If there is only one direction to take, he will sometimes move in that direction, even if it is away from his target. This helps cut down on the “conga line” phenomenon at bottleneck points.
Pathing torture test: over 100 monsters
You can see the uncrowding behavior in bottom right area of the final screen. Monsters have moved downward, away from the player, to rally and let the other monsters through.
The monsters are pretty smart at this point. Check out how easily they solve this maze and overwhelm our hero: