The Ultimate Race Postmortem

2010, August 10th 5:23 PM

So, I made this game. And – let's be fair – it sort of sucked.

I could go on at great length about how this is Box2D's fault, and thanks to issues integrating Box2D with Lua. There's some truth to that. There's still bugs in there causing the physics to behave a bit wonkily. I spent a lot more time working on infrastructure and bindings than I'd meant to, and even today it doesn't work properly on Linux (and probably won't, to be honest, it's not a good enough game for me to spend the time on it.)

But that's not why the game isn't fun.

The original view I had of the game was that you'd be running along towards the next checkpoint, trying desperately to get there in time, and at the last minute you'd fling yourself towards the goal, die in midair, and get to watch your corpse crunching its way down the mountain until you happened to fall into the rejuvenator and oh god time to keep running go go go get to the next checkpoint.

But there's a bunch of problems here. First off, it requires you to make a crucial decision – your exact death position and velocity – before you can even see what you're trying to aim for. Second, physics tends to be extremely chaotic, so either the game needs to be built to funnel the player inevitably down into the right location or the player's death is near-certain, regardless of skill. And, of course, if you funnel the player down to the right location, then you're making skill irrelevant anyway. A game with no skill component tends to not be a good game, and as much as I rail against them for being low-skill, even Bejeweled and Peggle involve more skill than my original vision of The Ultimate Race did.

Even that isn't the crucial problem.

The reason the game isn't fun – and this took me a while to hunt down – is that a game of this sort is only fun if you're almost losing. If you get to the goal quickly, it's easy, and you'll never bother with an early death and it's boring. If you're nowhere near the goal, you lose no matter what and it's simultaneously boring and frustrating. The player has to be right on that knife edge of losing for that fun moment I had in mind to actually occur.

And that's just ridiculously hard to orchestrate.

If I were going to do this again, I'd be trying one of two different approaches. One is to make the difficulty self-balancing in a sense. I hate auto-adjusting difficulty, so in reality what this would mean is that reaching a goal very quickly would unlock a "harder path" that the player could choose to go on, so the player could make the game as hard as they could handle and thereby keep it fun.

Another thing I would try would be to remove the time limit entirely, and even remove the voluntary-death button. I'd add some kind of a laser defense grid around the checkpoint. You die because you leap headlong into a laser and get fried, and that is when the whole "fall down the cliff praying" thing would kick in. I'd play up the "race" thing more heavily – you'd ideally be racing against a bunch of computer opponents that don't get shot by lasers ("sorry man, we're out of the reflective armor. good luck!") and so you'd be encouraged to leap headlong into danger without really spending a lot of time preparing.

And finally, the player needs at least some influence over the corpse behavior once he's dead. Even if that's limited to twitching in one direction or another. Something.

I feel like, in some sense, this game is a milestone for me. It's the first time I came up with an idea that just flat-out didn't work – the rest of my unsuccessful games have been unsuccessful because I didn't have an idea. So, I mean, I had an idea, and it sucked. That's progress.

I think that's progress.

The Ultimate Race

2010, July 31st 12:04 PM

Windows (.zip version available)
(No Linux version currently available, there's a bug I can't find right now)

This was made for the annual SomethingAwful GameDev Challenge V. The theme for this year was "You Can't ____" – something that you would normally be able to do, but something that you are now unable to do.

Make a game out of it!

As such, The Ultimate Race is subtitled "You Can't Avoid Having Constant, Instantly-Fatal Heart Attacks".

This is my first game with a physics engine involved, and some parts of it went . . . rockily. I ended up spending far more time fighting my tools and far less time actually making the game than I'd expected. Still, here's a game. Go play it!

No Such Thing As Too Many Guns

2009, June 24th 8:34 AM

Here it is. Experimental Game #1.

(Download for Windows)

I've already shared this around to some friends, so I'm just going to post my postmortem right now.

It all comes down to my mom.

My mom is not very good at games. She's better than she thinks she is, but she has a real hard time working through any kind of roadblock. Super Mario World? Can't get to a save point – can't even beat the second level. Braid? Well . . . it turns out she's actually pretty dang good at Braid.

The difference is the "game over" screen. She hates the Game Over screen. She hates being told "well, you're doing better . . . but *not good enough*!" Super Mario World has that failure screen. Braid doesn't – you just hit the rewind button and try it again.

No Such Thing is impossible to lose.

Seriously. You cannot do it. If you get hit, you lose your current gun and instantly get a better gun. Eventually, you will kill the final boss, and win the game.

The challenge is in seeing how far you can make it without being hit, and it's self-balancing in the sense that, if you can sling more firepower around, you can avoid being hit better.

I'd hoped people – even people who were bad at games – would find this interesting enough to give the game a few playthroughs. I'd hoped people wouldn't be scared of it – sure, you got shot, but all you got was a better gun! No worries! Keep on truckin'!

In the end, the game took about five days to make. I considered adding one to three more levels but I couldn't think of any interesting level gimmicks that wouldn't take a solid day just to code (walls, for example, or enemies that couldn't be described easily as circles) and it just didn't seem worth it. The gimmick was the interesting part, and the game is developed enough to test the gimmick.

What Worked

The game design itself seems to be rather effective. People enjoy it, and seem to frequently try several times to get a better score. That's what I was hoping for! Even people who are traditionally very bad at action games seem to enjoy giving it a few shots – sure, they're bad at it, but they get better at it rapidly. And since they're competing against themselves, rather than against a fixed "ha ha you are terrible at games" metric, they can see their progress in a more useful way than moving from #722,857 to #618,004 on a gigantic leaderboard.

So the idea seems to be a success.

Another goal for this game was to try out the game framework I put together. The framework is also a success. All the game and UI logic is in Lua, and that worked stunningly. (Coroutines and upvalues are golden.) I was worried about performance issues, but I flat-out haven't had any – the only performance problem I had was when I had a bug keeping bullets from being culled, so every bullet you fired flew forever. I cannot recommend Lua enough. You should be using it.

The framework was a little rocky at times – there were inevitable bugs – but I probably spent only a day or so overall fixing them.

SFXR turned out to be great for sound, and I'm actually quite satisfied with the sound in this game. For the amount of time I spent on it, it's perfect.

What Didn't Work

Originally I'd planned for the weapon types to upgrade as well. It seems to be a common issue in my games that I end up changing "upgrade weapon types" into "upgrade firerate" – this is the second time I've done it. I tend to do it because it's easy to tell which weapons are better if you do it this way, and it's easier to make a level that behaves interestingly, but the character suffers as a result. I need to stop doing this. Varied weapon behavior is more fun than just turning a slow-fire weapon into a rapid-fire weapon.

My art, while better than I feared, is pretty damn awful. I need to work on that more. Practice will help.

I'm still, fundamentally, afraid of OpenGL. I think my next project may have to involve a lot more flashy OpenGL effects that cannot be expressed with "draw some sprites", because I'm basically limiting myself to "draw some sprites". As with my art, that's making the graphics suffer, and graphics are an important part of the experience.

I still need to figure out some better patterns for making Lua do what I want. Things went great until I set it up so you could start over, or start another level, and then they degenerated into murk. I've got some ideas to fix this also, but I'm going to have to research Lua semantics more than I'd hoped.

Speaking of Lua semantics, I had a constant war with getting useful stack traces. I'm going to have to sit down and make a good stack trace library so I can get Lua stacktraces easily. It all works great until you involve coroutines, and then it melts down. It's solvable – it'll just take a day or two.

Taking a week to do some other stuff, then working on my dev framework a little . . . then moving on to another game.

Let me know what you thought of this one.

Games, movies, and magic have one major thing in common – misdirection. Show people one thing, then indicate to them that they saw another, and usually they'll believe you. In magic, it's harder because they're trying to figure out what you're doing while you're doing it. In movies, it's easier because the person is really just going along for the ride. In games, it's really easy, because the player is being assaulted by zombies and doesn't have any attention to spare.

At least, they don't the first time they play the game.

The second time, they're probably paying a lot more attention to what's going on around them. The zombies attacking, yeah, sure, they're a problem – but we've dealt with them before. Let's look at the other things around us!

This is when they discover how careful the game is at showing you exactly what they want you to see, and keeping you from doing anything besides what you're supposed to.

Not supposed to go through a door yet? It's locked. Got a cutscene to watch? I can guarantee every door leaving that room is locked – even if you just came through it ten seconds earlier. You can walk through a door, have it lock behind you, and then have the very same door unlock the instant you're done with a cutscene or a movie. Happens all the time.

Sometimes they even force you to look in certain directions. Sometimes, this is to make you look at something you're supposed to see. Sometimes, this is to make you look away from something you're not supposed to see. In the first level, there's an exploding shuttle. I bet you remember seeing it explode, right? It was really cool? No! You didn't. Because you can't have. The camera is jerked away from it at the last second, and when you turn back to it, it's already exploded. You're carefully prevented from seeing the exact moment it explodes.

The reason for that, of course, is that animating something large exploding in a realistic manner is expensive and hard. It's easier to just not show it. And it works great . . . up until the person realizes what's going on and decides to try exploring the boundaries.

This is a common issue in games. There are a good number of games out there that pretend you're given choices, but actually prevent all choice. The Half-Life 2 series is a perfect example – the first time you play it feels like an exploration, but every time after that you realize, hey, wait, I'm not allowed to go anywhere else! That exploration feeling was a ripoff!

I should mention that this is not necessarily a bad thing. The fact is that most people will never start a second playthrough – in fact, many people won't even finish the first. It's arguably kind of silly to triple your budget by making content that 95% of your users will never even see. (It's also arguably not. I'll post an entry about this someday.) But it does mean that going through the game a second time is kind of like being invited backstage at a live performance, or having the magician explain his tricks – all those cute things you noticed the first time turn out to be your own fevered imagination running a bit too fast.

Solution? There isn't one, besides solving the hard AI problem and writing programs that can generate content for us. Unfortunately, this is a ways off, and if we ever do solve it, we've put ourselves out of a job.

All I can say is: be aware of it, and try hard to keep the player from feeling constrained. At least, on the first playthrough.

It's far more subtle than you think.

2007, February 2nd 1:27 PM

User interface design is hard.

Not because it's hard to invent interfaces. Oh no, it's easy to invent interfaces. You can throw the suckers together in minutes once you've gotten a little practice. And not because it's hard to invent usable interfaces. That part is hard, actually, but with a little introspection and a lot of blind testing and a whole hell of a lot of agonizing and rewriting, you can make them work. No, the problem is to invent intuitive interfaces. Because intuitive interfaces are usually far, far more complicated than anyone would expect – since, after all, they're intuitive, and they look so easy.

Right now I'm designing a level editor. It's a 2d editor, used to create curves and paths. It doesn't have very many general classes of "thing" it has to deal with yet. There are paths which have centers. Paths are made up of nodes. Nodes may have curve controls on them. Lines connect the nodes in a loop, either curved or straight depending on the curve controls.

Fundamentally it's quite simple. Here's a screenshot, and I imagine most people feel they could jump right in and start moving points around easily.

Well, you're right. You could. You could grab a point, and drag it, and it would move (and, in fact, the point on the other side of the figure would move too – this particular path is set up with vertical symmetry, which the editor will happily preserve for you. Classy, no?) Note that one point is selected right now – the one that's red, in the upper left – but really that doesn't matter much, you can drag any node around at will, and if there are multiple nodes on top of each other you can just click multiple times until you have the one you want selected. You could also right-click a line to toggle it from "curved" to "straight", and the curve handles will automatically go away. And come back if needed. Pretty neat, huh?

You have no idea how complicated all this stuff is.

Imagine the actual code behind it. The user clicks on a node. The program must select this node. Now the user drags the mouse, and the node moves with the mouse. That's easy.

But what happens if the user clicks on the node again? I've mentioned that you can cycle through nodes by clicking, so we could just have it select the "next" node at that position, whatever that means. Sounds reasonable. Except then the user selects a node, and drags it coincidentally on top of a second node, and changes his mind, and drags it further . . . but whoops, he got the wrong node. The program changed to the next node when he clicked. That wasn't supposed to happen.

Instead, we could make it so the node change happens when the user lets go of the button. Only now we have a different problem – the user selects a node, moves it coincidentally over another node, and lets go . . . and it selects the other node. That's not the right behavior either! We shouldn't change nodes in that case. And, in fact, there's a further problem – if I select a new node that happens to be in a stack of three or four nodes, then let go of the mouse button, it will change to the second node in the stack when I let go of the button.

After some thought, you can come up with the following process to solve these problems:

  • The user clicks. If the currently selected item is not underneath the mouse button, it selects whatever is underneath the mouse.
  • The user may or may not drag the mouse. If they do, move the currently selected item.
  • The user releases the mouse button. If an item was not selected, and the user did not drag the mouse, and there is a second item under the mouse that can be selected, select that one instead.

Note that I still haven't defined "second node" or "next node" or anything like that. Oh, also, there's a bug in that process, which I'm not going to point out. (Find it! It's a challenge!)

But we're not even done. Remember, there's four things in this universe that can be selected – and lines aren't draggable! But you can right-click them to change if they're curved. Or you can right-click nodes to lock their curve controls to "straight", so there are no obvious angles to that node. But curve controls, or the path center itself, can't be right-clicked (even though they can be dragged.) And if the "add node" button at the top is selected, then clicking a curve creates a new node at that position. What happens if you click "add node" and then click a curve control? What if it's a curve control on top of a line? What if you right-click it?

And now I have to go, rip out a hundred lines of code, and rewrite it, because my current approach just doesn't work.

User interface design is hard.