Focus on Really Small Things

Today was one of those days.  Less than great sleep last night, oppressive heat in the home office (though nothing compared to the rest of the country I guess), general lack of energy.  It’s easy to get really down on these days–especially for those of us who work alone.

I watched Harp Dreams tonight on Netflix, and it was worth watching for one quote, which I’ll paraphrase.  One of the finalists in the harp competition, Cheryl Losey, said about day-to-day practicing at this level:

“Some days are worse than other days. On those days, when everything just seems overwhelming, you just have to block that out and focus on really small things.  You can’t think about the big picture on those days, and you can’t make any huge judgments.”

 

VGM

It’s Wednesday, and there’s not much to report on the Escape Goat front. I’m currently making sure the game can cope with multiple worlds, so the player can play the campaign or user-created worlds from the title menu.  Straightforward so far: fingers crossed.  From here on out, it’s just ticking off the boxes until it’s what I consider beta.

For a change of pace, I’d like to offer up some video game music favorites.

First off is a page of metal and classical guitar remixes of NES and SNES tunes, by Famicom Guitar.  (Hint: subscribe to @Surasshu on Twitter to have awesome links like this land in your feed.)  Be sure to check out:

  • Disc 3, track 5: Final Fantasy IV, The Dreadful Fight
  • Disc 3, track 9: Dragon Quest II, file select theme
  • Disc 3, track 10: Ninja Gaiden, (level one theme?)
  • Disc 2, track 7: Adventure of Link, town theme
  • Disc 1, track 2: Ducktales, the freakin’ MOON
  • Disc 1, track 4, Megaman 2 opening theme monster ballad
  • Disc 1, track 5, Super Mario Bros. 2 main BGM
  • Disc 1, track 8, Dragon Quest 2 overworld
Now let’s move on to some YouTube classics.
YouTube also has great modern game music on tap:
There, you have your next workday playlist.

The Specific Solution – Thoughts on Jonathan Blow’s “How to Program Independent Games”

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.

Some Thoughts on Puzzle Design

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:

Type 1:

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.

Type 2:

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:

Type 3:

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.