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.