I apologize if this series seems to be glossing over some details while exploring others in exhaustive detail. This is a very hard series to write and I’m having trouble keeping it all straight in my head.
At any given moment there’s the stuff I’m working on and thinking about. Lagging behind that activity by about three weeks is the stuff I’ve written about and organized into words. And lagging behind that by another week is what has actually been posted to the blog. So when organizing my thoughts I have to figure out if feature X is something I’ve done, something I’ve documented, and something you know about.
It’s confusing, is what I’m getting at.
One one hand, these programming posts are really useful for documenting and clarifying my thoughts. On the other hand, having this muti-stage process with three weeks of lag time is not so useful. Because of this, I’m really eager to plow through this early stuff as fast as possible. Do I document all the little diversions and side-paths that didn’t work out? If so, then I’ll never get caught up. But if I leave that stuff out then this threatens to became a very dry recounting of features added.
I still don’t know how to handle this.
And now I’m about to make the problem worse. Here is a feature from day three. It took me longer to document in this post than it did to write the feature in the first place:
It was many hours into my first play-through of Plants vs. Zombies before I realized, “Wow. Every single object in this game has a face.” Every plant, every weapon in your arsenal has at least a pair of eyes to give it some personality.
I’m kind of hoping I can use this idea to give the game some character. Descent put eyes on their robots and I have to say it made a huge difference. They would have been much less interesting if they just looked like flying vehicles. I’m hoping that if I give them faces (or at least eyes) then the player will fill in the gaps and ascribe personalities to them based on their behavior and facial “expression”. Ideally, the suicide bomber foes should look kind of crazed while the ones that shoot at a distance should look kind of cunning and maybe slow-moving foes would look kind of derp. That’s the idea, anyway.
To keep the eyes from looking like simple running lights, I hope to make them move around a bit.
So I make a new type of object called “eyes”. To start with, it just renders this little circle of color:
I don’t know if I actually want to use that color banding. We’ll see. In any case, we need an iris (or a pupil, the physiology of cartoon robot eyes is a little sketchy) that moves around in this circle. It’s not enough to just draw a bright dot and call it a day.
The illusion of this being an eye is broken, and it looks like a firefly zipping around in front of a round window. The dot needs to be constrained to the area we’ve already drawn. You can accomplish this with the depth buffer.
How it normally works is that you draw some polygons to the screen and OpenGL (or whatever you’re using for drawing) writes to the depth buffer. This keeps track of how far away from the camera each pixel is. When you draw more polygons later, they only show up if they end up closer to the camera than the stuff that’s already drawn. This is how we can draw a scene and have some objects appear to be behind others.
But we can change this behavior if we want. We can tell it to only draw if the new polygons and the old polygons are at exactly the same depth. Doing it this way, the iris only shows up where it overlaps with the eye:
Up until now my bad robots have just been silhouettes with some running lights or simple fixed dots for eyes.
And now we add the eyes:
Some eyes sweep back and forth, like Cylons. Some point the way the robot is going, like Pac-Man ghosts. And some follow the player, like creepy stalkers.
The difference is not nearly as noticeable or attention-grabbing as I’d hoped it would be. In my mind it was going to be this OH WOW effect that would bring the robots to life. In practice it’s kind of subtle. But it’s good. (Enough. (For now.))
Now we need a character for the player.
I went through a lot of designs over the course of a week or so. I didn’t take screenshots of the early versions, but the figure in the lower right is what I settled on:
The player is a robot, and their character is a little bit cute and a little more “human” than the robots they’re fighting. I was aiming for gender-less. Not sure how well I succeeded. There’s nothing about it that screams out “THIS IS A MAN ROBOT” to me, at any rate.
It’s called an “avatar” internally, a habit I picked up at Activeworlds where all human-controlled models were called that. Outside of the source, there’s no official name for it. The avatar is made of several different sprites instead of a single fixed whole. This means that as they zip around the level, the torso can lean into the turns or lag behind ever so slightly when accelerating. This puts a lot of life into the little thing. The avatar has animated eyes like the other robots, although the avatar eyes look whichever way the mouse is pointing / aiming.
The left (from your perspective) arm shoots lasers, and the right arm shoots missiles. And now I realize I added missiles to the game without mentioning it. I also didn’t mention them in the next entry, which is already written but which you won’t see until Friday.
See what I mean about this being confusing?
Gah. Here we are at the end of an entry and I’ve managed to document ONE feature. Considering at this point in the project I was adding a half dozen features a day, I was working 6 days a week, and these blog entries only come out three times a week, this is clearly unsustainable. We will never catch up like this.
So you get a double-sized entry today. Let’s talk about game controllers. The only controller I have is the USB XBox controller for the PC:
I did have another off-brand controller around here, but I seem to have misplaced it in the move. For the purposes of what we’re doing here, it doesn’t really matter.
For the record: I’m talking to the joystick through SDL. SDL is nice, clean, lightweight, and cross-platform. I could also talk to the joysticks through the Direct X library, which I think is called Direct Input. Or maybe I’m remembering wrong. It’s been more than a decade. At any rate, this would marry my project to Direct X, which is massive, complex, and would limit me to platforms supported by Microsoft. It would also give my game a bad case of DirectX dependency. Many of you have no doubt installed a game and ran into the situation where it said, “Installing Direct X step 1 of N” even though Direct X was up to date, in which case you’ve seen this terrible sickness.
Basically there are an endless number of different versions of Direct X. There are versions and sub-versions and in-between versions and each of them has their own binaries. Games are usually built against one particular version, and getting the various nearly identical but ever-so-slightly distinct versions to coexist is apparently a major headache.
So screw DirectX, is what I’m getting at.
Here is how the controller looks to the software engineer:
Yes, the trigger buttons are actually a single analog stick. If neither trigger is down, then the stick is in the neutral position. If one trigger is down then this “stick” is pressed one way and if the other trigger is down it’s going the other. If you squeeze both triggers at once they cancel each other out. This is kind of interesting. I didn’t know that about the trigger buttons. That does place limits on how we can use it. I don’t know if this is how it’s “supposed” to work, or if this is a limitation of interfacing with a Microsoft controller using a non-Microsoft interface. It does seem strange to design a controller with trigger buttons that are mutually exclusive.
The green X brand button in the middle of the controller is invisible to me. If I was using Direct Input I could probably see it, but since I’m talking to the hardware through SDL I don’t know it’s there and I can’t tell if the user is pressing it. But who cares?
The d-pad is technically a “hat” control. You probably remember hat controls from those mid-90’s joysticks:
I imagine the reason for this distinction is that while the hat can technically be thought of as four direction buttons, in practice they have a limited number of mutually exclusive states. You can’t physically push the hat right and left at the same time. You can’t press both “hat up” and “hat down”.
And while all of this is kind of interesting and important to people who design these input devices, it’s all very tedious and beside the point to a would-be game developer. When I’m trying to figure out if the user is pressing a “go right” button, I really don’t care if that button is a joystick button, a hat button, an analog stick, a keyboard button, or a mouse button. All I care about is if they are doing it.
In terms of physical hardware, we have seven distinct forms of input:
- Analog stick.
- Hat control.
- Joystick buttons.
- Mouse movement.
- Mouse buttons.
- Keyboard standard keys.
- Keyboard special keys like shift, control, alt, or numlock.
Each of these input types has a unique way of querying it. Sometimes SDL tells me, but sometimes I have to ask. Sometimes the answer is obvious and easy to read, and sometimes the answer is complex and needs to undergo some sort of scaling and conversion. (Like mouse speed or joystick dead zone, for example.) But in practice, there are only three distinct forms of input:
- Analog stick
- Mouse movement
- Buttons. Any button.
It’s really inconvenient to have to poll all these different input channels, so what we need is a system of virtual buttons. I create a layer of pretend buttons and we feed all these disparate input channels into this one list of inputs. While the user is playing, I can query if they’re pressing button X without concerning myself with where X is physically located or what sort of button it is.
This also lets me create new abstract or amalgamated buttons. I can create a generic “joystick up” button. If the user nudges the left analog stick up and releases it again, it generates a keypress on this new imaginary button. If they hit up on the D-pad, it generates the same keypress. This means I can ask even more abstract questions, like “Is the user trying to move up?” and not worry about how they are expressing this decision. It’s not terribly useful for gameplay, but it’s a must-have if you’re going to navigate menus.
With all of this done, I end up with a nice clean input system. I shouldn’t need to touch it again unless this turns into an actual game, in which case I’d probably need some kind of way to remap keys, which is a complete pain in the ass to support properly. In order to let the user re-map keys, you need a robust menu system, you need to be aware of all the possible inputs even if they don’t exist on keyboards in your country, and you need to know the proper written names of those buttons so the user can see that the selection they made is the one they wanted. (The piss-easy way to do it is to just jam all those settings in a config file, which hasn’t been tolerated since the 90’s. I should know. I’m one of the people who doesn’t tolerate it.)
One thing to note is that the hardest part of adding controller support was getting it to handle hot-plugging of joysticks. When you start up SDL, it looks around, takes an inventory of the available joysticks, and sets them up for use. But if you plug one in afterward it doesn’t notice or tell you about it. Not even if you ask.
This is made worse by the fact that some genius at Microsoft decided that when the wireless controller loses connection it should yank the device away from you as if someone physically unplugged the joystick. I have no idea why. The receiver is still plugged in.
This is annoyingly common. If the batteries are starting to go, that will lead to a disconnect. If you set the controller down for more than a couple of minutes it goes to sleep, which is also a disconnect. To your game, this doesn’t just mean you stop getting input. It means the device itself vanishes.
A lot of games (indies in particular) don’t take this into account, which means that if the device isn’t awake when you launch the game, you will never, ever get the game to acknowledge the controller. You have to exit the game and re-launch it to use the controller. (Which sucks, since the most likely thing is for the user to launch the game and THEN pick up the controller.) In some cases, it doesn’t handle the re-connect properly which means that this disconnect will force you to relaunch the game if you want to use the controller again.
It turns out the secret to fix this is to shut down the SDL joystick subsystem and restart it at regular intervals. Like so:
//No joystick active. See if one showed up. SDL_QuitSubSystem(SDL_INIT_JOYSTICK); SDL_InitSubSystem(SDL_INIT_JOYSTICK); if (!SDL_NumJoysticks()) return; //Still nothin'
I mention this only because it was really, really hard to track this down. I found this buried deep in a forum somewhere. I didn’t even know you could shut down individual SDL subsystems like this. I have no idea if there’s a performance cost to doing this, so I only do the check every five seconds.
With this in place, I can handle the strange comings and goings of Xbox wireless controllers without forcing the user to exit the game.
The game feels very different with the controller. Unlike an FPS, this is more of a trade-off than a handicap. It’s a little easier to set up really precise long-distance shots with the mouse, while the analog stick just doesn’t have the same ability to make fine micro-adjustments. On the other hand, moving with an analog stick is a lot nicer than with the keyboard. WASD are obviously binary indicators, and the only way to go slow is to flutter the keys. Worse, you can only go in the 8 ordinal directions with the keyboard, while the controller allows you to fly in nice swooping curves if you want to.
I don’t think one is significantly easier than the other, but they do feel very different in your hands.
What is Vulkan?
There's a new graphics API in town. What does that mean, and why do we need it?
Even allegedly smart people can make life-changing blunders that seem very, very obvious in retrospect.
The Opportunity Crunch
No, brutal, soul-sucking, marriage-destroying crunch mode in game development isn't a privilege or an opportunity. It's idiocy.
A programming project where I set out to make a Minecraft-style world so I can experiment with Octree data.
Denuvo and the "Death" of Piracy
Denuvo videogame DRM didn't actually kill piracy, but it did stop it for several months. Here's what we learned from that.