Jump to content
  • Advertisement
  • entries
  • comments
  • views

Entries in this blog


The Poor Man's Netcode

The more you know about a given topic, the more you realize that no one knows anything. For some reason (why God, why?) my topic of choice is game development. Everyone in that field agrees: don't add networked multiplayer to an existing game, you drunken clown. Well, I did it anyway because I hate myself. Somehow it turned out great. None of us know anything. Problem #1: assets My first question was: how do I tell a client to use such-and-such mesh to render an object? Serialize the whole mesh? Nah, they already have it on disk. Send its filename? Nah, that's inefficient and insecure. Okay, just a string identifier then? Fortunately, before I had time to implement any of my own terrible ideas, I watched a talk from Mike Acton where he mentions the danger of "lazy decision-making". One of his points was: strings let you lazily ignore decisions until runtime, when it's too late to fix. If I rename a texture, I don't want to get a bug report from a player with a screenshot like this: I had never thought about how powerful and complex strings are. Half the field of computer science deals with strings and what they can do. They usually require a heap allocation, or something even more complex like ropes and interning. I usually don't bother to limit their length, so a single string expands the possibility space to infinity, destroying whatever limited ability I had to predict runtime behavior. And here I am using these complex beasts to identify objects. Heck, I've even used strings to access object properties. What madness! Long story short, I cultivated a firm conviction to avoid strings where possible. I wrote a pre-processor that outputs header files like this at build time: namespace Asset { namespace Mesh { const int count = 3; const AssetID player = 0; const AssetID enemy = 1; const AssetID projectile = 2; } } So I can reference meshes like this: renderer->mesh = Asset::Mesh::player; If I rename a mesh, the compiler makes it my problem instead of some poor player's problem. That's good! The bad news is, I still have to interact with the file system, which requires the use of strings. The good news is the pre-processor can save the day. const char* Asset::Mesh::filenames[] = { "assets/player.msh", "assets/enemy.msh", "assets/projectile.msh", 0, }; With all this in place, I can easily send assets across the network. They're just numbers! I can even verify them. if (mesh < 0 || mesh >= Asset::Mesh::count) net_error(); // just what are you trying to pull, buddy? Problem #2: object references My next question was: how do I tell a client to please move/delete/frobnicate "that one object from before, you know the one". Once again, I was lucky enough to hear from smart people before I could shoot myself in the foot. From the start, I knew I needed a bunch of lists of different kinds of objects, like this: Array<Turret> Turret::list; Array<Projectile> Projectile::list; Array<Avatar> Avatar::list; Let's say I want to reference the first object in the Avatar list, even without networking, just on our local machine. My first idea is to just use a pointer:
  Avatar* avatar; avatar = &Avatar::list[0]; This introduces a ton of non-obvious problems. First, I'm compiling for a 64 bit architecture, which means that pointer takes up 8 whole bytes of memory, even though most of it is probably zeroes. And memory is the number one performance bottleneck in games. Second, if I add enough objects to the array, it will get reallocated to a different place in memory, and the pointer will point to garbage. Okay, fine. I'll use an ID instead. template<typename Type> struct Ref { short id; inline Type* ref() { return &Type::list[id]; } // overloaded "=" operator omitted }; Ref<Avatar> avatar = &Avatar::list[0]; avatar.ref()->frobnicate(); Second problem: if I remove that Avatar from the list, some other Avatar will get moved into its place without me knowing. The program will continue, blissfully and silently screwing things up, until some player sends a bug report that the game is "acting weird". I much prefer the program to explode instantly so I at least get a crash dump with a line number. Okay, fine. Instead of actually removing the avatar, I'll put a revision number on it: struct Avatar { short revision; }; template<typename Type> struct Ref { short id; short revision; inline Type* ref() { Type* t = &Type::list[id]; return t->revision == revision ? t : nullptr; } }; Instead of actually deleting the avatar, I'll mark it dead and increment the revision number. Now anything trying to access it will give a null pointer exception. And serializing a reference across the network is just a matter of sending two easily verifiable numbers. Problem #3: delta compression If I had to cut this article down to one line, it would just be a link to Glenn Fiedler's blog. Which by the way is here: gafferongames.com As I set out to implement my own version of Glenn's netcode, I read this article, which details one of the biggest challenges of multiplayer games. Namely, if you just blast the entire world state across the network 60 times a second, you could gobble up 17 mbps of bandwidth. Per client. Delta compression is one of the best ways to cut down bandwidth usage. If a client already knows where an object is, and it hasn't moved, then I don't need to send its position again. This can be tricky to get right. The first part is the trickiest: does the client really know where the object is? Just because I sent the position doesn't mean the client actually received it. The client might send an acknowledgement back that says "hey I received packet #218, but that was 0.5 seconds ago and I haven't gotten anything since." So to send a new packet to that client, I have to remember what the world looked like when I sent out packet #218, and delta compress the new packet against that. Another client might have received everything up to packet #224, so I can delta compress the new packet differently for them. Point is, we need to store a whole bunch of separate copies of the entire world. Someone on Reddit asked "isn't that a huge memory hog"? No, it is not. Actually I store 255 world copies in memory. All in a single giant array. Not only that, but each copy has enough room for the maximum number of objects (2048) even if only 2 objects are active. If you store an object's state as a position and orientation, that's 7 floats. 3 for XYZ coordinates and 4 for a quaternion. Each float takes 4 bytes. My game supports up to 2048 objects. 7 floats * 4 bytes * 2048 objects * 255 copies = ... 14 MB. That's like, half of one texture these days. I can see myself writing this system five years ago in C#. I would start off immediately worried about memory usage, just like that Redditor, without stopping to think about the actual data involved. I would write some unnecessary, crazy fancy, bug-ridden compression system. Taking a second to stop and think about actual data like this is called Data-Oriented Design. When I talk to people about DOD, many immediately say, "Woah, that's really low-level. I guess you want to wring out every last bit of performance. I don't have time for that. Anyway, my code runs fine." Let's break down the assumptions in this statement. Assumption 1: "That's really low-level". Look, I multiplied four numbers together. It's not rocket science. Assumption 2: "You sacrifice readability and simplicity for performance." Let's picture two different solutions to this netcode problem. For clarity, let's pretend we only need 3 world copies, each containing up to 2 objects. Here's the solution I just described. Everything is statically allocated in the .bss segment. It never moves around. Everything is the same size. No pointers at all. Here's the idiomatic C# solution. Everything is scattered randomly throughout the heap. Things can get reallocated or moved right in the middle of a frame. The array is jagged. 64-bit pointers all over the place. Which is simpler? The second diagram is actually far from exhaustive. C#-land is a lot more complex in reality. Check the comments and you'll probably find someone correcting me about how C# actually works. But that's my point. With my solution, I can easily construct a "good enough" mental model to understand what's actually happening on the machine. I've barely scratched the surface with the C# solution. I have no idea how it will behave at runtime. Assumption 3: "Performance is the only reason you would code like this." To me, performance is a nice side benefit of data-oriented design. The main benefit is clarity of thought. Five years ago, when I sat down to solve a problem, my first thought was not about the problem itself, but how to shoehorn it into classes and interfaces. I witnessed this analysis paralysis first-hand at a game jam recently. My friend got stuck designing a grid for a 2048-like game. He couldn't figure out if each number was an object, or if each grid cell was an object, or both. I said, "the grid is an array of numbers. Each operation is a function that mutates the grid." Suddenly everything became crystal clear to him. Assumption 4: "My code runs fine". Again, performance is not the main concern, but it's important. The whole world switched from Firefox to Chrome because of it. Try this experiment: open up calc.exe. Now copy a 100 MB file from one folder to another. I don't know what calc.exe is doing during that 300ms eternity, but you can draw your own conclusions from my two minutes of research: calc.exe actually launches a process called Calculator.exe, and one of the command line arguments is called "-ServerName". Does calc.exe "run fine"? Did throwing a server in simplify things at all, or is it just slower and more complex? I don't want to get side-tracked. The point is, I want to think about the actual problem and the data involved, not about classes and interfaces. Most of the arguments against this mindset amount to "it's different than what I know". Problem #4: lag I now hand-wave us through to the part of the story where the netcode is somewhat operational. Right off the bat I ran into problems dealing with network lag. Games need to respond to players immediately, even if it takes 150ms to get a packet from the server. Projectiles were particularly useless under laggy network conditions. They were impossible to aim. I decided to re-use those 14 MB of world copies. When the server receives a command to fire a projectile, it steps the world back 150ms to the way the world appeared to the player when they hit the fire button. Then it simulates the projectile and steps the world forward until it's up to date with the present. That's where it creates the projectile. I ended up having the client create a fake projectile immediately, then as soon as it hears back from the server that the projectile was created, it deletes the fake and replaces it with the real thing. If all goes well, they should be in the same place due to the server's timey-wimey magic. Here it is in action. The fake projectile appears immediately but goes right through the wall. The server receives the message and fast-forwards the projectile straight to the part where it hits the wall. 150ms later the client gets the packet and sees the impact particle effect. The problem with netcode is, each mechanic requires a different approach to lag compensation. For example, my game has an "active armor" ability. If players react quick enough, they can reflect damage back at enemies. This breaks down in high lag scenarios. By the time the player sees the projectile hitting their character, the server has already registered the hit 100ms ago. The packet just hasn't made it to the client yet. This means you have to anticipate incoming damage and react long before it hits. Notice in the gif above how early I had to hit the button. To correct this, the server implements something I call "damage buffering". Instead of applying damage instantly, the server puts the damage into a buffer for 100ms, or whatever the round-trip time is to the client. At the end of that time, it either applies the damage, or if the player reacted, reflects it back. Here it is in action. You can see the 200ms delay between the projectile hitting me and the damage actually being applied. Here's another example. In my game, players can launch themselves at enemies. Enemies die instantly to perfect shots, but they deflect glancing blows and send you flying like this: Which direction should the player bounce? The client has to simulate the bounce before the server knows about it. The server and client need to agree which direction to bounce or they'll get out of sync, and they have no time to communicate beforehand. At first I tried quantizing the collision vector so that there were only six possible directions. This made it more likely that the client and server would choose the same direction, but it didn't guarantee anything. Finally I implemented another buffer system. Both client and server, when they detect a hit, enter a "buffer" state where the player sits and waits for the remote host to confirm the hit. To minimize jankiness, the server always defers to the client as to which direction to bounce. If the client never acknowledges the hit, the server acts like nothing happened and continues the player on their original course, fast-forwarding them to make up for the time they sat still waiting for confirmation. Problem #5: jitter My server sends out packets 60 times per second. What about players whose computers run faster than that? They'll see jittery animation. Interpolation is the industry-standard solution. Instead of immediately applying position data received from the server, you buffer it a little bit, then you blend smoothly between whatever data that you have. In my previous attempt at networked multiplayer, I tried to have each object keep track of its position data and smooth itself out. I ended up getting confused and it never worked well. This time, since I could already easily store the entire world state in a struct, I was able to write just two functions to make it work. One function takes two world states and blends them together. Another function takes a world state and applies it to the game. How big should the buffer delay be? I originally used a constant until I watched a video from the Overwatch devs where they mention adaptive interpolation delay. The buffer delay should smooth out not only the framerate from the server, but also any variance in packet delivery time. This was an easy win. Clients start out with a short interpolation delay, and any time they're missing a packet to interpolate toward, they increase their "lag score". Once it crosses a certain threshold, they tell the server to switch them to a higher interpolation delay. Of course, automated systems like this often act against the user's wishes, so it's important to add switches and knobs to the algorithm! Problem #6: joining servers mid-match Wait, I already have a way to serialize the entire game state. What's the hold up? Turns out, it takes more than one packet to serialize a fresh game state from scratch. And each packet may take multiple attempts to make it to the client. It may take a few hundred milliseconds to get the full state, and as we've seen already, that's an eternity. If the game is already in progress, that's enough time to send 20 packets' worth of new messages, which the client is not ready to process because it hasn't loaded yet. The solution is—you guessed it—another buffer. I changed the messaging system to support two separate streams of messages in the same packet. The first stream contains the map data, which is processed as soon as it comes in. The second stream is just the usual fire-hose of game messages that come in while the client is loading. The client buffers these messages until it's done loading, then processes them all until it's caught up. Problem #7: cross-cutting concerns This next part may be the most controversial. Remember that bit of gamedev wisdom from the beginning? "don't add networked multiplayer to an existing game"? Well, most of the netcode in this game is literally tacked on. It lives in its own 5000-line source file. It reaches into the game, pokes stuff into memory, and the game renders it. Just listen a second before stoning me. Is it better to group all network code in one place, or spread it out inside each game object? I think both approaches have advantages and disadvantages. In fact, I use both approaches in different parts of the game, for various reasons human and technical. But some design paradigms (*cough* OOP) leave no room for you to make this decision. Of course you put the netcode inside the object! Its data is private, so you'll have to write an interface to access it anyway. Might as well put all the smarts in there too. Conclusion I'm not saying you should write netcode like I do; only that this approach has worked for me so far. Read the code and judge for yourself. There is an objectively optimal approach for each use case, although people may disagree on which one it is. You should be free to choose based on actual constraints rather than arbitrary ones set forth by some paradigm. Thanks for reading. DECEIVER is launching on Kickstarter soon. Sign up to play the demo here!




DECEIVER - announce trailer!

After two and a half years, it's finally, officially announced. And it got some attention! Here's PC Gamer: http://www.pcgamer.com/deceiver-is-a-philosophical-shooter-that-lets-you-shoot-drones-through-enemies/ And Rock Paper Shotgun: https://www.rockpapershotgun.com/2018/01/29/deceiver-is-a-neat-looking-parkour-game-with-added-spiderbots/ And two articles on Kotaku! https://kotaku.com/1822533385 https://www.kotaku.com.au/2018/01/854413/ The bad news is, I'm completely broke. Will I pull off this crazy thing? Stay tuned to find out.




The Poor Man's 3D Camera

Each of us have our own giants to face. This is a story about one of my giants. Something I never imagined could make a grown man cry, until it did. A 3D camera. No one can face your giants for you. This is a story, not a walkthrough. Expect no useful information. For that I recommend 50 Game Camera Mistakes by thatgamecompany's John Nesky. His job title is literally "camera designer". The story starts in 2014 with a seemingly harmless seven-day game jam. 7DFPS 2014 Exhausted from crunching on my first game, I decide it would be good to take a break and make a first-person shooter for 7DFPS. This is a terrible decision. The idea is simple: an FPS where you are the bullet. Click to jump from wall to wall. If someone's head happens to be between you and the next wall, sucks for them. The main problem is that, when you shoot to a new surface, the game necessarily buries your face directly into that surface, filling the whole screen with a solid blue color. You can see my first solution above. After landing, the game "reflects" your direction off the surface you landed on, and tweens the camera to face the new direction. So when you shoot into a wall head-on, the camera does a complete 180. Of course, I can't place the camera directly on the surface, or there would be clipping artifacts. So I space the camera a bit out from the wall, which leads to another problem: Here the extra space between the camera and the surface allows you to aim at any location on the surface and launch there. In theory, your "character" is actually flush with the surface, which should make this physically impossible. I throw a band-aid on this problem by clamping the camera so that it can't aim at or behind the surface it's attached to. I end up working 96 hours in a week to finish this masterpiece that will surely take the world by storm. Two YouTubers play it for a grand total of 10,000 views. Like every good vacation, the experience leaves me exhausted and depressed on Monday morning. Over the next 8 months, I finish my other game, take an actual vacation, then, instead of throwing away the Unity prototype like any sane person would, I start rebuilding it in a brand new custom C++ engine. The new game is an expansion of the prototype, which means more features, more gameplay, more pain and suffering! One of the first new features is the ability to crawl along walls, floors, and ceilings: A couple months later, camera problems start to manifest. As players crawl around different surfaces, I have to forcefully nudge the camera to keep it from staring straight into a wall. I use spherical linear interpolation (slerp) to smooth out the transition between angled surfaces, but it's still not great. In the gif below, the player on the right does not manually move their camera at all; the wall forces them to look up automatically. At this point, everything is completely unreadable and confusing. The first-person view prevents players from seeing their character, and they often take several minutes to realize they're playing as a creepy crawly spider bot. Once they finally get it, they can't figure out why the camera keeps getting forced around, and why they can't aim at the surface they're attached to. I slap on another band-aid in the form of a shader that blacks out everything "behind" you, everything that's physically impossible to reach. I also have it darken out of range areas. Now the game is not only unreadable and confusing, but also ugly! By sheer chance, I happen to turn on a debug flag that moves the camera back from the player two units. Literally the poor man's third-person camera. And behold, it was not the worst thing in the world. I finally realize I'm making a third-person game, and so set about implementing it for real. The Culling Saga One of the deadliest scourges of humanity, right after Facebook and taxes, is a third-person camera that constantly zooms and bumps around to avoid in-game geometry. Rather than move the camera, I decide to just cull anything that blocks the camera's view. Half of the job is done right out the gate because my game engine, like most engines, automatically culls triangles that face away from the camera (also known as "back-faces"). I write a shader to handle the rest. It culls everything within a certain distance of the camera. What's that you say? My screenshots look weird with spider bots floating in empty space because the wall got culled? It'll be fine. It's fine. Don't worry about it. What, what now? You can catch glimpses of stuff you're not supposed to see, including the skybox? Look, I'm sure it's just a minor thing. Okay. That's pretty bad. What if instead of carving spheres around the player and camera, I use a cylinder instead? And what if I actually render a solid black cylinder whenever the camera is inside a wall? So close, yet so far. The cylinder blocks out most of the ugly geometry it needs to, but sometimes you can still see through things. I could extend the cylinder to block more, but I would have to clip it so it stays inside the level geometry. Even now, the cylinder sometimes blocks too much, because I only clip it against the player's current wall. Here I only want the cylinder to block the ugly insides of the ledge. Instead it blocks everything behind the player. I would have to query all the geometry surrounding the player and clip the cylinder against all those planes. And that's assuming I can figure out what's "inside" and "outside" the level geometry, which is not air-tight. There are overlapping faces, shapes that clip into each other, and plenty of other lazy hacks— I mean, clever tricks. What if I just turn off back-face culling? Use the same culling shader, and instead of the cylinder, rely on the back-faces to block anything you're not supposed to see. This works perfectly in some cases: Other times it does a great job of showing off my lazy hacks, like this ramp that simply extends into the floor rather than meeting it cleanly: It doesn't help that the back-faces still receive accurate lighting. Feels like the level is a hollow shell. Fine. I'll spend a couple weeks welding level geometry together, and I'll render the back-faces in solid black. How about now? Getting less terrible all the time. With a little depth buffer bias, a special tag in the G-buffer for back-faces, and a few extra texture taps in the composite shader, I can filter out those pesky lines too: The gifs above also show off the new culling shape. Instead of a cylinder, which indiscriminately carves out an uncouth shape much larger than necessary: I switch to a cone, to ensure I only cull things what need culling. But the cone has problems too. It's possible for the player to back up so close to a wall that the cone intersects the wall in an incredibly narrow circle, leaving a tiny hole for the player to peer through. From somewhere deep inside my repressed memories of Calc II springs an exotic mathematical creature known as a paraboloid: A quick trip to the grimoire to remind myself of the formula, a few incantations in a darkened room with a mirror, and the creature is summoned into GLSL form. The culled circle still tapers a little, but it's enough to see fairly well. Good enough for government work. Ship it! UX UX is, like, super important. Take it from me, a full-stack growth-hacking happiness engineer co-founder. Let me tell you about Our Incredible Journey. At first, the game had a terrible retention rate. I did some A/B testing, streamlined the user onboarding, pivoted to clickbait, bought some fraudulent traffic, and now I'm at $10k MRR. I'm also taking cold showers every morning and learning Farsi on Duolingo. If you can keep your eyes from rolling back completely and irreparably inside your head, "User Experience" is a decent descriptor for the interactive aspects of game development, the other contender being "Game Feel". It's the thing you can only get by interacting with the software yourself. It's the thing that makes you smile with surprise when you pick up the controller for the first time, even though you've spent the last hour watching someone else play. It's also basically impossible for me to get right on the first ten tries. Here's an example of my terrible camera UX. When you hit an enemy player without killing them, you bounce off. The camera instantly snaps around to follow your new trajectory. Of course I slerp the rotation a little, but still, you can barely see it happen. I have no reason to lock the camera like this, it's just a carry-over from when the game was first-person, when it made sense to always point the camera where the player was headed next. It takes someone at a convention telling me what an idiot I am to make me finally unlock the camera. A one-line change that takes a whole year. Later, someone on Twitch yells out a self-evidently brilliant suggestion: let the camera lag behind the player a little when they launch. This also takes only a handful of lines to test out, although the numbers don't feel good until a week or two of tweaking. So many of my design decisions are simply carried over from old and outdated assumptions, even though they stopped making sense several versions ago. Here's an example. Remember how I clamp the camera rotation to keep the player from aiming at the surface they're currently attached to? Turns out this gets old after a while, especially in third-person. People want freedom, they don't want you shoving their camera around. I grudgingly loosen the shackles a bit by clamping the rotation against a cone instead of a plane. I use spherical interpolation again to smoothly swing the cone around and nudge the camera away from the wall. Unfortunately, slerp doesn't always rotate the way you want it to. Sometimes the cone pushes the camera in a completely different direction as it rotates to match the surface. Instead of rotating the cone, I decide to instantly snap it to the surface, but scale it up slowly. So it starts as a thin sliver and slowly expands into a fat cone, pushing the camera out as it grows. Everything's going just swimmingly until Momin Khan asks me... is it even necessary to clamp the camera any more? It made sense when the game was first-person, but that was two years ago. I immediately get defensive. How else can I keep the camera from staring straight at the wall for 90% of the game? But I slowly realize he's right. I compromise in the end. I nudge the camera for a split second after landing, then shrink the cone back down to zero to allow glorious unfettered camera freedom. Full range of motion, baybee Now that the player can aim at their currently attached surface, what happens when they try to launch there? I eventually solve this problem by allowing spider bots to "dash" along the surface. I have no physical explanation for how they do it. It's a video game. There's another problem, however. Normally, the game does a raycast starting at the third-person camera and continuing straight through the reticle. When the spider bot launches, it compensates and launches where the player wants it to go, like this: However, sometimes the camera can see and aim at a point the spider bot can't reach. I solve this by ruling everything behind the player's current surface "out of bounds". I even have a shader to darken the forbidden areas. Look, Simba. Everything the light touches is our kingdom. This is really frustrating and confusing to people. Finally Nathan Fouts from Mommy's Best Games tells me to just cheat and let the player go there even if it doesn't make physical sense. I don't like the idea of breaking my game physics so I compromise by having the spider bot dash to the edge and then fly the rest of the way. It all happens so fast, you don't even notice it. Here it is in slow motion: Conclusion Some tasks cannot be sped up. If I had put "make 3D camera" on a Gantt chart, I would have stopped 10% of the way through this article and called it good. The point is, most of these ideas are simple and obvious in hindsight, but I had to make a lot of mistakes to find them. It's nearly impossible to coalesce good design decisions straight from the ether. The only reliable method is iteration and trial and error. Thanks for reading. What embarrassingly obvious design improvements have you been slapped in the face with? Deceiver is set to launch on Kickstarter in early February 2018. Join the mail list to be notified of its release, or check out the source code on GitHub.




Thirteen Years of Bad Game Code

Alone on a Friday night, in need of some inspiration, you decide to relive some of your past programming conquests. The old archive hard drive slowly spins up, and the source code of the glory days scrolls by... Oh no. This is not at all what you expected. Were things really this bad? Why did no one tell you? Why were you like this? Is it even possible to have that many gotos in a single function? You quickly close the project. For a brief second, you consider deleting it and scrubbing the hard drive. What follows is a compilation of lessons, snippets, and words of warning salvaged from my own excursion into the past. Names have not been changed, to expose the guilty. 2004 I was thirteen. The project was called Red Moon -- a wildly ambitious third-person jet combat game. The few bits of code that were not copied verbatim out of Developing Games in Java were patently atrocious. Let's look at an example. I wanted to give the player multiple weapons to switch between. The plan was to rotate the weapon model down inside the player model, swap it out for the next weapon, then rotate it back. Here's the animation code. Don't think about it too hard.public void updateAnimation(long eTime) { if(group.getGroup("gun") == null) { group.addGroup((PolygonGroup)gun.clone()); } changeTime -= eTime; if(changing && changeTime I want to point out two fun facts. First, observe how many state variables are involved:

Even with all that, it feels like something's missing... ah yes, we need a variable to track which weapon is currently equipped. Of course, that's in another file entirely. The other fun fact is that I never actually created more than one weapon model. Every weapon used the same model. All that weapon model code was just a liability. How to Improve Remove redundant variables. In this case, the state could be captured by two variables: [font='courier new']weaponSwitchTimer [/font]and [font='courier new']weaponCurrent[/font]. Everything else can be derived from those two variables. Explicitly initialize everything. This function checks if the weapon is [font=consolas][size=1]null[/font] and initializes it if necessary. Thirty seconds of contemplation would reveal that the player always has a weapon in this game, and if they don't, the game is unplayable and might as well crash anyway. Clearly, at some point, I encountered a [font='courier new']NullPointerException [/font]in this function, and instead of thinking about why it happened, I threw in a quick [font='courier new']null [/font]check and moved on. In fact, most of the functions dealing with weapons have a check like this! Be proactive and make decisions upfront! Don't leave them for the computer to figure out. Namingboolean noenemies = true; // why oh why Name your booleans positively. If you find yourself writing code like this, re-evaluate your life decisions:if (!noenemies) { // are there enemies or not??} Error Handling Snippets like this are sprinkled liberally throughout the codebase:static { try { gun = Resources.parseModel("images/gun.txt"); } catch (FileNotFoundException e) {} // *shrug* catch (IOException e) {}} You might be thinking "it should handle that error more gracefully! Show a message to the user or something." But I actually think the opposite. You can never have too much error checking, but you can definitely have too much error handling. In this case, the game is unplayable without the weapon model, so I might as well let it crash. Don't try to gracefully recover from unrecoverable errors. Once again, this requires you to make an upfront decision as to which errors are recoverable. Unfortunately, Sun decided that almost all Java errors must be recoverable, which results in lazy error handling like the above. 2005-2006 At this point I learned C++ and DirectX. I decided to write a reusable engine so that mankind could benefit from the vast wealth of knowledge and experience I had acquired in my fourteen years on the earth. If you thought the last trailer was cringey, just wait. By now I learned that Object-Oriented Programming is Good(TM), which resulted in monstrosities like this:class Mesh {public: static std::list meshes; // Static list of meshes; used for caching and rendering Mesh(LPCSTR file); // Loads the x file specified Mesh(); Mesh(const Mesh& vMesh); ~Mesh(); void LoadMesh(LPCSTR xfile); // Loads the x file specified void DrawSubset(DWORD index); // Draws the specified subset of the mesh DWORD GetNumFaces(); // Returns the number of faces (triangles) in the mesh DWORD GetNumVertices(); // Returns the number of vertices (points) in the mesh DWORD GetFVF(); // Returns the Flexible Vertex Format of the mesh int GetNumSubsets(); // Returns the number of subsets (materials) in the mesh Transform transform; // World transform std::vector* GetMaterials(); // Gets the list of materials in this mesh std::vector* GetCells(); // Gets the list of cells this mesh is inside D3DXVECTOR3 GetCenter(); // Gets the center of the mesh float GetRadius(); // Gets the distance from the center to the outermost vertex of the mesh bool IsAlpha(); // Returns true if this mesh has alpha information bool IsTranslucent(); // Returns true if this mesh needs access to the back buffer void AddCell(Cell* cell); // Adds a cell to the list of cells this mesh is inside void ClearCells(); // Clears the list of cells this mesh is insideprotected: ID3DXMesh* d3dmesh; // Actual mesh data LPCSTR filename; // Mesh file name; used for caching DWORD numSubsets; // Number of subsets (materials) in the mesh std::vector materials; // List of materials; loaded from X file std::vector cells; // List of cells this mesh is inside D3DXVECTOR3 center; // The center of the mesh float radius; // The distance from the center to the outermost vertex of the mesh bool alpha; // True if this mesh has alpha information bool translucent; // True if this mesh needs access to the back buffer void SetTo(Mesh* mesh);} I also learned that comments are Good(TM), which led me to write gems like this:D3DXVECTOR3 GetCenter(); // Gets the center of the mesh This class presents more serious problems though. The idea of a Mesh is a confusing abstraction that has no real-world equivalent. I was confused about it even as I wrote it. Is it a container that holds vertices, indices, and other data? Is it a resource manager that loads and unloads that data from disk? Is it a renderer that sends the data to the GPU? It's all of these things. How to Improve The Mesh class should be a "plain old data structure". It should have no "smarts", which means we can safely trash all the useless getters and setters and make all the fields public. Then we can separate the resource management and rendering into separate systems which operate on the inert data. Yes, systems, not objects. Don't shoehorn every problem into an object-oriented abstraction when another abstraction might be a better fit. The comments can be improved, mostly, by deleting them. Comments easily fall out of date and become misleading liabilities, since they're not checked by the compiler. I posit that comments should be eliminated unless they fall into one of these categories:
Comments explaining why, rather than what. These are the most useful.
Comments with a few words explaining what the following giant chunk of code does. These are useful for navigation and reading.
Comments in the declaration of a data structure, explaining what each field means. These are often unnecessary, but sometimes it's not possible to map a concept intuitively to memory, and comments are necessary to describe the mapping.

2007-2008 I call these years "The PHP Dark Ages". 2009-2010 By now, I'm in college. I'm making a Python-based third-person multiplayer shooter called Acquire, Attack, Asplode, Pwn. I have no excuse at this point. The cringe just keeps getting worse, and now it comes with a healthy dose of copyright infringing background music. When I wrote this game, the most recent piece of wisdom I had picked up was that global variables are Bad(TM). They lead to spaghetti code. They allow function "A" to break a completely unrelated function "B" by modifying global state. They don't work with threads. However, almost all gameplay code needs access to the entire world state. I "solved" this problem by storing everything in a "world" object and passed the world into every single function. No more globals! I thought this was great because I could theoretically run multiple, separate worlds simultaneously. In practice, the "world" functioned as a de facto global state container. The idea of multiple worlds was of course never needed, never tested, and I'm convinced, would never work without significant refactoring. Once you join the strange cult of global tea-totallers, you discover a whole world of creative methods to delude yourself. The worst is the singleton:class Thing{ static Thing i = null; public static Thing Instance() { if (i == null) i = new Thing(); return i; }}Thing thing = Thing.Instance(); Poof, magic! Not a global variable in sight! And yet, a singleton is much worse than a global, for the following reasons:
All the potential pitfalls of global variables still apply. If you think a singleton is not a global, you're just lying to yourself.
At best, accessing a singleton adds an expensive branch instruction to your program. At worst, it's a full function call.
You don't know when a singleton will be initialized until you actually run the program. This is another case of a programmer lazily offloading a decision that should be made at design time.

How to Improve If something needs to be global, just make it global. Consider the whole of your project when making this decision. Experience helps. The real problem is code interdependence. Global variables make it easy to create invisible dependencies between disparate bits of code. Group interdependent code together into cohesive systems to minimize these invisible dependencies. A good way to enforce this is to throw everything related to a system onto its own thread, and force the rest of the code to communicate with it via messaging. Boolean Parameters Maybe you've written code like this:class ObjectEntity: def delete(self, killed, local): # ... if killed: # ... if local: # ... Here we have four different "delete" operations that are highly similar, with a few minor differences depending on two boolean parameters. Seems perfectly reasonable. Now let's look at the client code that calls this function:obj.delete(True, False) Not so readable, huh? How to Improve This is a case-by-case thing. However, one piece of advice from Casey Muratori certainly applies here: write the client code first. I'm sure that no sane person would write the above client code. You might write something like this instead:obj.killLocal() And then go write out the implementation of the [font='courier new']killLocal()[/font] function. Naming It may seem strange to focus so heavily on naming, but as the old joke goes, it's one of the two remaining unsolved problems in computer science. The other being cache invalidation and off-by-one errors. Take a look at these functions:class TeamEntityController(Controller): def buildSpawnPacket(self): # ... def readSpawnPacket(self): # ... def serverUpdate(self): # ... def clientUpdate(self): # ... Clearly the first two functions are related to each other, and the last two functions are related. But they are not named to reflect that reality. If I start typing [font='courier new']self.[/font] in an IDE, these functions will not show up next to each other in the autocomplete menu. Better to make each name start with the general and end with the specific, like this:class TeamEntityController(Controller): def packetSpawnBuild(self): # ... def packetSpawnRead(self): # ... def updateServer(self): # ... def updateClient(self): # ... The autocomplete menu will make much more sense with this code. 2010-2015 After only 12 years of work, I actually finished a game. Despite all I had learned up to this point, this game featured some of my biggest blunders. Data Binding At this time, people were just starting to get excited about "reactive" UI frameworks like Microsoft's MVVM and Google's Angular. Today, this style of programming lives on mainly in React. All of these frameworks start with the same basic promise. They show you an HTML text field, an empty [font='courier new'][/font] element, and a single line of code that inextricably binds the two together. Type in the text field, and pow! The [font='courier new'] [/font]magically updates. In the context of a game, it looks something like this:public class Player{ public Property Name = new Property { Value = "Ryu" };}public class TextElement : UIComponent{ public Property Text = new Property { Value = "" };}label.add(new Binding(label.Text, player.Name)); Wow, now the UI automatically updates based on the player's name! I can keep the UI and game code totally separate. This is appealing because we're eliminating the state of the UI and instead deriving it from the state of the game. There were some red flags, however. I had to turn every single field in the game into a Property object, which included a list of bindings that depended on it:public class Property : IProperty{ protected Type _value; protected List bindings; public Type Value { get { return this._value; } set { this._value = value; for (int i = this.bindings.Count - 1; i >= 0; i = Math.Min(this.bindings.Count - 1, i - 1)) this.bindings.OnChanged(this); } }} Every single field in the game, down to the last boolean, had an unwieldy dynamically allocated array attached to it. Take a look at the loop that notifies the bindings of a property change to get an idea of the issues I ran into with this paradigm. It has to iterate through the binding list backward, since a binding could actually add or delete UI elements, causing the binding list to change. Still, I loved data binding so much that I built the entire game on top of it. I broke down objects into components and bound their properties together. Things quickly got out of hand.jump.Add(new Binding(jump.Crouched, player.Character.Crouched));jump.Add(new TwoWayBinding(player.Character.IsSupported, jump.IsSupported));jump.Add(new TwoWayBinding(player.Character.HasTraction, jump.HasTraction));jump.Add(new TwoWayBinding(player.Character.LinearVelocity, jump.LinearVelocity));jump.Add(new TwoWayBinding(jump.SupportEntity, player.Character.SupportEntity));jump.Add(new TwoWayBinding(jump.SupportVelocity, player.Character.SupportVelocity));jump.Add(new Binding(jump.AbsoluteMovementDirection, player.Character.MovementDirection));jump.Add(new Binding(jump.WallRunState, wallRun.CurrentState));jump.Add(new Binding(jump.Rotation, rotation.Rotation));jump.Add(new Binding(jump.Position, transform.Position));jump.Add(new Binding(jump.FloorPosition, floor));jump.Add(new Binding(jump.MaxSpeed, player.Character.MaxSpeed));jump.Add(new Binding(jump.JumpSpeed, player.Character.JumpSpeed));jump.Add(new Binding(jump.Mass, player.Character.Mass));jump.Add(new Binding(jump.LastRollKickEnded, rollKickSlide.LastRollKickEnded));jump.Add(new Binding(jump.WallRunMap, wallRun.WallRunVoxel));jump.Add(new Binding(jump.WallDirection, wallRun.WallDirection));jump.Add(new CommandBinding(jump.WalkedOn, footsteps.WalkedOn));jump.Add(new CommandBinding(jump.DeactivateWallRun, (Action)wallRun.Deactivate));jump.FallDamage = fallDamage;jump.Predictor = predictor;jump.Bind(model);jump.Add(new TwoWayBinding(wallRun.LastWallRunMap, jump.LastWallRunMap));jump.Add(new TwoWayBinding(wallRun.LastWallDirection, jump.LastWallDirection));jump.Add(new TwoWayBinding(rollKickSlide.CanKick, jump.CanKick));jump.Add(new TwoWayBinding(player.Character.LastSupportedSpeed, jump.LastSupportedSpeed));wallRun.Add(new Binding(wallRun.IsSwimming, player.Character.IsSwimming));wallRun.Add(new TwoWayBinding(player.Character.LinearVelocity, wallRun.LinearVelocity));wallRun.Add(new TwoWayBinding(transform.Position, wallRun.Position));wallRun.Add(new TwoWayBinding(player.Character.IsSupported, wallRun.IsSupported));wallRun.Add(new CommandBinding(wallRun.LockRotation, (Action)rotation.Lock));wallRun.Add(new CommandBinding(wallRun.UpdateLockedRotation, rotation.UpdateLockedRotation));vault.Add(new CommandBinding(wallRun.Vault, delegate() { vault.Go(true); }));wallRun.Predictor = predictor;wallRun.Add(new Binding(wallRun.Height, player.Character.Height));wallRun.Add(new Binding(wallRun.JumpSpeed, player.Character.JumpSpeed));wallRun.Add(new Binding(wallRun.MaxSpeed, player.Character.MaxSpeed));wallRun.Add(new TwoWayBinding(rotation.Rotation, wallRun.Rotation));wallRun.Add(new TwoWayBinding(player.Character.AllowUncrouch, wallRun.AllowUncrouch));wallRun.Add(new TwoWayBinding(player.Character.HasTraction, wallRun.HasTraction));wallRun.Add(new Binding(wallRun.LastWallJump, jump.LastWallJump));wallRun.Add(new Binding(player.Character.LastSupportedSpeed, wallRun.LastSupportedSpeed));player.Add(new Binding(player.Character.WallRunState, wallRun.CurrentState));input.Bind(rollKickSlide.RollKickButton, settings.RollKick);rollKickSlide.Add(new Binding(rollKickSlide.EnableCrouch, player.EnableCrouch));rollKickSlide.Add(new Binding(rollKickSlide.Rotation, rotation.Rotation));rollKickSlide.Add(new Binding(rollKickSlide.IsSwimming, player.Character.IsSwimming));rollKickSlide.Add(new Binding(rollKickSlide.IsSupported, player.Character.IsSupported));rollKickSlide.Add(new Binding(rollKickSlide.FloorPosition, floor));rollKickSlide.Add(new Binding(rollKickSlide.Height, player.Character.Height));rollKickSlide.Add(new Binding(rollKickSlide.MaxSpeed, player.Character.MaxSpeed));rollKickSlide.Add(new Binding(rollKickSlide.JumpSpeed, player.Character.JumpSpeed));rollKickSlide.Add(new Binding(rollKickSlide.SupportVelocity, player.Character.SupportVelocity));rollKickSlide.Add(new TwoWayBinding(wallRun.EnableEnhancedWallRun, rollKickSlide.EnableEnhancedRollSlide));rollKickSlide.Add(new TwoWayBinding(player.Character.AllowUncrouch, rollKickSlide.AllowUncrouch));rollKickSlide.Add(new TwoWayBinding(player.Character.Crouched, rollKickSlide.Crouched));rollKickSlide.Add(new TwoWayBinding(player.Character.EnableWalking, rollKickSlide.EnableWalking));rollKickSlide.Add(new TwoWayBinding(player.Character.LinearVelocity, rollKickSlide.LinearVelocity));rollKickSlide.Add(new TwoWayBinding(transform.Position, rollKickSlide.Position));rollKickSlide.Predictor = predictor;rollKickSlide.Bind(model);rollKickSlide.VoxelTools = voxelTools;rollKickSlide.Add(new CommandBinding(rollKickSlide.DeactivateWallRun, (Action)wallRun.Deactivate));rollKickSlide.Add(new CommandBinding(rollKickSlide.Footstep, footsteps.Footstep)); I ran into tons of problems. I created binding cycles that caused infinite loops. I found out that initialization order is often important, and initialization is a nightmare with data binding, with some properties getting initialized multiple times as bindings are added. When it came time to add animation, I found that data binding made it difficult and non-intuitive to animate between two states. And this isn't just me. Watch this Netflix talk which gushes about how great React is before explaining how they have to turn it off any time they run an animation. I too realized the power of turning a binding on or off, so I added a new field:class Binding{ public bool Enabled;} Unfortunately, this defeated the purpose of data binding. I wanted to get rid of UI state, and this code actually added some. How can I eliminate this state? I know! Data binding!class Binding{ public Property Enabled = new Property { Value = true };} Yes, I really did try this briefly. It was bindings all the way down. I soon realized how crazy it was. How can we improve on data binding? Try making your UI actually functional and stateless. dear imgui is a great example of this. Separate behavior and state as much as possible. Avoid techniques that make it easy to create state. It should be a pain for you to create state. Conclusion There are many, many more embarrassing mistakes to discuss. I discovered another "creative" method to avoid globals. For some time I was obsessed with closures. I designed an "entity" "component" "system" that was anything but. I tried to multithread a voxel engine by sprinkling locks everywhere. Here's the takeaway:
Make decisions upfront instead of lazily leaving them to the computer.
Separate behavior and state.
Write pure functions.
Write the client code first.
Write boring code.

That's my story. What horrors from your past are you willing to share? If you enjoyed this article, try these:
The Poor Man's Voxel Engine
The Poor Man's Character Controller
One Weird Trick to Write Better Code




Enlisting IBM Watson as a voice actor

Allow me to regale you with an exciting tale: the birth of a janky dialogue and voice system. I have a JSON file with all the localized strings in my game, like this:{ "danger": "Danger", "level": "Level %d", ...}
A preprocessor takes this and generates a header file with integer constants for each string, like this:namespace strings{ const int danger = 0; const int level = 1; // ...}
At runtime, it loads the JSON file and hooks up the integer IDs to localized strings. A function called "_" takes an integer ID and returns the corresponding localized string. I use it like this:draw_string(_(strings::danger), position);
This all worked (and still works) pretty well for UI strings. Not so much for dialogue. To write dialogue, I had to come up with a unique ID for each line, then add it to the strings file, like this:{ "hello_penelope": "Hello! I am Penelope.", "nice_meet_you": "Nice to meet you.", ...}
Yes, the preprocessor generated a new integer ID in the header file every time I added a line of dialogue. Gross. I construct dialogue trees in Dialogger. With this setup, I had to use IDs like "hello_penelope" rather than actual English strings. Also gross.
A better way
I keep the string system, but extend it to support "dynamic" strings loaded at runtime that do not have integer IDs in the header file. Now I can write plain English in the dialogue trees. The preprocessor goes through all of them and extracts the strings into a separate JSON file, using the SHA-1 hash of each string for its ID. Once everything is loaded, I discard all string IDs in favor of integer IDs. I couldn't find a simple straightforward SHA-1 implementation that worked on plain C strings, so here's one for you. The point of all this is: I now have a single JSON file containing all the dialogue in the game. Ripe for automation... Speak and spell Penelope is an AI character. I'm using text-to-speech for her voice, at least for now. I don't want to integrate a text-to-speech engine in the game; that's way too much work. And I don't want to manually export WAVs from a text-to-speech program. Also too much work. I create a free IBM Bluemix account. They have a dead simple text-to-speech API: make an HTTP request with basic HTTP authentication, get a WAV file back. I write an 82-line Python script that goes through all the dialogue strings and makes an HTTP request for each one. It keeps track of which strings have previously been voiced, to facilitate incremental updates. Now I have a folder of WAV files, each one named after a SHA-1 hash. I'm using Wwise for audio, so the next step requires a bit of manual involvement. I drag all the WAVs into the project and batch create events for them. Now when I display a dialogue string, I just have to look up the SHA-1 hash and play the audio event. Easy. Disaster strikes
I don't hear anything. All signs indicate the audio is playing correctly, but nothing comes out of my speakers. I look at one of the audio files in Wwise. Looks like the file is corrupted. I play the WAV in a number of different programs. Some play it fine, others don't play it at all. I edit my text-to-speech script to use Python's wave library to load the WAV file after downloading it from IBM. Sure enough, the library doesn't know what to make of it. Too lazy to care, I edit the wave library in-place in my Python distribution. YOLO. After a bit of printf debugging, I pinpoint the issue. The WAV format is based on RIFF, a binary format which breaks the file into "chunks". According to Wikipedia, the format of each chunk is as follows:
4 bytes: an ASCII identifier for this chunk (examples are "fmt " and "data"; note the space in "fmt ").
4 bytes: an unsigned, little-endian 32-bit integer with the length of this chunk (except this field itself and the chunk identifier).
variable-sized field: the chunk data itself, of the size given in the previous field.
a pad byte, if the chunk's length is not even.

Turns out, IBM's text-to-speech API generates streaming WAV files, which means it sets the "length" field to 0. Some WAV players can handle it, while others choke. Wwise falls in the latter category. Fortunately, I can easily figure out the chunk length based on the file size, modify it using the wave library, and write it back out to the WAV file. Like so. Problem solved. Wwise is happy. Next I set up some Wwise callbacks to detect the current volume of Penelope's voice, and when she's done speaking. Here's the result, along with some rope physics in the background being destroyed by the wonky framerate caused by my GIF recorder:

If you want to hear it, check out the IBM text-to-speech demo here. Thanks for reading! Mirrored on my blog




The Poor Man's Threading Architecture

The game industry hit Peak Advice Blog a while ago. Every day I [s]read[/s] skim ten articles telling me how to live. Fear not! I would never give you useful advice. This series is about me writing bad code and you laughing at my pain. First Contact
Say you have some voxels which occasionally get modified. You regenerate their geometry like so:voxel.Regenerate();
Because you are a masochist, you want to do this on a separate thread.
Rather than redesign your engine, you simply spawn a worker thread and give it a list of voxels to process. Easy in C#:Queue workQueue;static void worker(){ while (true) { Voxel v = workQueue.Dequeue(); lock (v) { v.Regenerate(); } }}new Thread(new ThreadStart(worker)).Start();// And awaaaaaaay we go!workQueue.Enqueue(voxel);
The [font=consolas]lock[/font] signals to other threads "hey, I'm using this". If the other threads also acquire locks before using the object, then only one thread will access it at a time.
Kaboom! It crashes when thread A dequeues an item at the exact moment when thread B is enqueueing one. You need a lock on the queue as well. This is Hard and Boring and Slow
Turns out, without a lock you can't trust even a single boolean variable to act sane between threads. (Not entirely true. For wizards, there are atomic operations and something about fences. Out of scope here!) Sprinkling locks everywhere is tedious, error-prone, and terrible for performance. Sadly, you need to rethink your engine from the ground up with threads in mind. The Right Way
Why am I even including this? Ugh. You read a few articles on modern AAA engines, where you find a diagram like this: This is the job graph from Destiny. AAA engines split their workload into "jobs" or "fibers". Some jobs depend on others. The graph has bottlenecks that split the work into phases. Within phase 1, tons of jobs execute in parallel, but all of them must finish before phase 2 starts. With jobs, you wouldn't have to lock individual pieces of data. The dependency graph ensures that jobs run in the right order, and that nothing runs in parallel unless you're okay with it. You also don't have to think about individual threads -- a scheduler delegates jobs to a pool of threads. Here's another diagram you stumble across: This shows the workload of the GPU and each CPU over time as the game renders a single frame. The goal is to fill all those holes so you use every bit of available compute power at maximum efficiency. The Quick and Dirty Way
In a brief flash of clarity, you realize that you are not Bungie. You check your bank account, which sadly reports a number slightly lower than $500 million. You recall the Pareto Principle, also known as the "80/20 rule". You decide to write 80% of a decent architecture for only 20% of the work. You start with a typical game loop:while (true){ // Process window and input events SDL_PumpEvents(); SDL_Event sdl_event; while (SDL_PollEvent(&sdl_event)) { // ... } physics_step(); game_logic(); render(); // Present! SDL_GL_SwapWindow(window);}
[size=2]Side note: a while back you also switched to C++. Masochism level up.
What can you move off the main thread? If you touch OpenGL or anything within a mile of the windowing system from another thread, the universe explodes. For now, you keep graphics and input ( [font=consolas]SDL_PollEvent[/font] ) on the main thread. That leaves physics and game logic. You give each its own thread. Since you need to spawn/modify/query physics entities in game logic, no other physics can happen while you're in a game logic update. The rest of the time, the physics thread can work in the background. Sounds like a perfect case for a lock:std::mutex physics_mutex;void physics_loop(){ while (true) { std::lock_guard lock(physics_mutex); physics_step(); }}void game_logic_loop(){ while (true) { { std::lock_guard lock(physics_mutex); game_logic(); } render(); }}
In this setup, your [font=consolas]render()[/font] function can't read or write any physics data for fear of explosions. No problem! In fact, that limitation might be considered a feature. However, the [font=consolas]render()[/font] function also can't make any OpenGL calls since it's not on the main thread.
You re-watch the Destiny GDC presentation and notice a lot of talk about "data extraction". In a nutshell, Destiny executes game logic, then extracts data from the game state and lines it up for a huge fan-out array of render threads to process efficiently. That's essentially what your [font=consolas]render()[/font] function will do: go through the game state, generate graphics commands, and queue them up. In your case, you only have one render thread to execute those commands. It might look like this:enum class RenderOp{ LoadMesh, FreeMesh, LoadTexture, FreeTexture, DrawMesh, Clear, // etc.};BlockingQueue render_queue;void main_loop(){ while (true) { RenderOp op = render_queue.read(); switch (op) { case LoadMesh: int count = render_queue.read(); Vec3* vertices = render_queue.read(count); // etc... break; case FreeMesh: // etc... } }}
It's a graphics virtual machine. You could give it a pretentious name like GLVM.
Now in your [font=consolas]render()[/font] function, you just write commands to the queue:void render(){ render_queue.write(RenderOp::Clear); for (MeshRenderer& mesh : mesh_renderers) { render_queue.write(RenderOp::DrawMesh); render_queue.write(mesh.id); // etc. } render_queue.write(RenderOp::Swap);}
This will work, but it's not the best. You have to lock the queue every time you read from or write to it. That's slow. Also, you need to somehow get input data from the main thread to the game logic thread. It doesn't make sense to have queues going both directions.
Instead, you allocate two copies of everything. Now, the game logic thread can work on one copy, while the main thread works on the other. When both threads are done, they swap. Now you only have to use a lock once per frame, during the swap. Once the threads are synced, the swap operation is just two pointer reassignments. Furthermore, you can keep the render command lists allocated between frames, which is great for performance. To clear one, just reset the pointer to the start of the list. Now you don't have to worry about implementing a queue with a ring buffer. It's just an array. Our Bright and Glorious Future
For wizards, this is all wrong, because graphics drivers do command queueing anyway, and jobs and fibers are the right way. This is like Baby's First Threaded Renderer. But it's simple, it gets you thinking in terms of data flow between threads, and if you eventually end up needing a job system, you're already halfway there. This setup might also make the switch to Vulkan easier. If you keep all your data extraction in one place and make it read-only, it should be trivial to split into multiple threads, each with their own render queue. You can see a poorly-commented cross-platform implementation of this idea here. Potentially useful parts include the SDL loop, GLVM, and the swapper. If you enjoyed this article, try these:
The Poor Man's Character Controller
The Poor Man's Voxel Engine
The Poor Man's Dialogue Tree

But here's a better idea: watch the Destiny GDC talk.
Thanks for reading! Mirrored on my blog




Ludum Dare 34 Postmortem

Friday 21:15
Fifteen minutes after the theme announcement, my friend Ben Homan walks through my front door. Not really my front door, I'm just a subletter. But this is a first. Normally he ignores our instructions to walk in without knocking. The first time, he texted me from the driveway. 21:30
Jesse Kooner walks in, also unannounced, bearing frozen pizza. Before he can even kick his shoes off, I loudly explain the theme: a never-before-seen tie between "growing" and "two-button controls". 21:45
Jesse has no laptop. I dig out an old one from my closet. I plug it in and start working on a few Windows updates. 72 to be exact.
Meanwhile, we decide which technology to use. Jesse's less code-focused skillset leads him to prefer Unity, while Ben wants to use the weekend as an opportunity to become more familiar with Node.js. We decide on Node.js. Jesse will provide creative input and artwork. 22:30
My roommates, a brother and sister, arrive home from an apparently underwhelming Christmas light show. The concept of a game jam is foreign to them, but they're good sports. We spend a half hour playing QWOP with them. 23:00
Our design parameters:
Multiplayer. Otherwise, what's the point of Node.js?
Probably 2D due to the limited timeframe. Although Jesse is more comfortable working in 3D.

Jesse originally suggested doing this competition a few weeks ago, when he wanted to create a mash-up of "Cookie Clicker" and a tower defense game. He resurrects the idea now, only halfway joking.
Ben likes the idea of a multiplayer vine growing game. I'm partial to a text-based social game about growing a social media brand. No one commits to anything yet.
Ben is not a big gamer, so I pull up a few famous HTML5 games on his laptop. Cursors.io. Agar.io. 2048. This last one interests me in particular, as it involves growing numbers. 23:15
I'm pitching Ben and Jesse on a multiplayer version of 2048. I envision a free-roaming world filled with numbered tiles to collect. Instead of collapsing numbers together against the edges of the board, players would find walls and structures within the world to collapse their numbers against. 23:30
How can two or more games of 2048 occur simultaneously on the same board? In normal 2048, the player controls all tiles on the board. We decide to give each player a unique color, and allow them to assimilate unclaimed, grayed-out tiles.
What happens when tiles from two players meet? We decide that whoever moves first gets to own the resulting collapsed tile from a move.
How do players traverse through the world? We toss around the idea of procedural generation, but eventually decide on hand-crafted, linear levels linked together via portals.
This raises the question: how do the portals work? And what happens to a player's tiles once they exit a level?

Top right corner: our confidence that we'll actually finish the thing.
Saturday 00:30
We've eaten two pizzas. We have a Git repository and a Slack instance which is ultimately only used to test out amusing Slackbot responses.
Although the game is 2D, we decide on a 3D art style with an orthographic projection, to allow Jesse to use his skills in Maya LT, which he promptly installs on my old laptop.
Ben works on the server with Node.js, while I start on the client with Three.js. 02:30
Ben heads home first, then Jesse. I pass out on my bedroom floor.

You can see we're already struggling with the name.
I wake to find Ben working in the kitchen. He spends the morning building boiler plate for the server, while I work out some Three.js details. 13:30
I return from a run just in time to catch Jesse pulling up with donuts. After lunch, he and I spend the next few hours working out a pipeline between Maya and Three.js. 16:00
I tweet our first screenshot, featuring colorized instances of Jesse's tree model displayed in a horribly distorted orthographic projection.
Without the server API to code against, I run out of things to do on the client. Ben finishes the data model, but he has trouble conceptualizing the rules for player movement. I haul my laptop over to indulge in some good old-fashioned pair programming. 19:00
Break for dinner. Burritos. Jokes. 21:00
Jesse continues modelling. With the basic API done, I start working to make the client consume it. Ben works on an image loader. We want to design the levels in GIMP. 23:00
Ben takes off. I've got player movement, animations, and tile numbers done. Sunday 00:30
Multiplayer works. Jesse and I play a few games against each other. It's fun! It's a game! We work out some issues with the level loading code and try to get an interesting level loaded. 02:00
Problem: it's pretty easy for one player to gain the upper hand and quickly assimilate all the tiles on the board, making it impossible for other players to move and grow. 02:30
Solution! Players should only control tiles within a certain radius of their "center". Outlier tiles are grayed out, free to be picked up by other players. 03:15
Fix implemented. Jesse heads home and I turn in. 09:15
Tweet another screenshot before heading to church.
At this point, I'm having an existential crisis about the name. I fire off a few panicked texts about it. 12:30
Ben and I are back to the grindstone. Jesse arrives with sandwiches and more donuts! Ben adds a cool username feature, but we eventually axe it to keep things simple. 15:00
Tweaks and bug fixes all day. Ben works on the level format, while Jesse lays out some levels in GIMP. 17:00
More polish. I put in Jesse's cloud models and a "connecting" spinner. 19:00
We finally brainstorm a name: Tile Risers. Jesse whips up a logo in Maya. We go through seven iterations before everything lines up in our janky export pipeline. 20:00
I spin up a Digital Ocean droplet, allocate an S3 bucket, commit the production URLs, and start filling out the submission form. 21:00
Time's up! Fortunately, we have another hour to submit. I later found out I misread the rules, and we actually had a whole extra 24 hours. At any rate, we were done. I commit two small bug fixes after submission, which is within guidelines. Conclusion

Three.js is easy and fun.
ES5 causes a lot of pain by continuing silently when we ask it to do ridiculous things like "compare an object to an integer".
Ludum Dare is a blast and you should do it.
Play Tile Risers, view the source, and vote for it!

Mirrored on my blog




One Weird Trick to Write Better Code

Developers hate him!

We'll cover some standard tips and tricks here, but we're not really interested in those. We're looking for the One Weird Trick to rule them all. Hopefully each trick we encounter brings us closer to coding Mecca.

In the beginning
The first video game I ever wrote was called Ninja Wars.

Yes, that is an HTML table of images. I changed the src attribute to move stuff around.

The top of the Javascript file looked like this:var x = 314;var y = 8;var prevy= 1;var prevx= 1;var prevsw= 0;var row= 304;var endrow= 142;var sword= 296;var yrow = 0;var yendrow = 186;var I = 0;var e = 0;var counter = 0;var found = 0;var esword = 26;var eprevsw = 8;var bluehealth = 40;var redhealth = 40;var n = 0;var you = 'ninja';var bullet = 'sword';var enemy = 'enemy';var ebullet = 'enemysword';var besieged = 0;var siegecount = 0;var esiegecount = 0;var ebesieged = 0;var healthcount = 0;var player = 0;var starcount = 0;var infortress = false;var prevyou = you;var einfortress = false;var prevenemy = enemy;var previmg = "";var prevbullet= "";var eprevbullet= "";var randnum = 0;var randnum2 = 0;var randnum3 = 0;var randnum4 = 0;var buildcount = 0;var characters = new Array(4);characters = ['ninja','tank','heli','builder'];var bullets = new Array(3);bullets = ['sword','bullet','5cal','sword'];var echaracters = new Array(3);echaracters = ['enemy','tank2','eheli','ebuilder'];var ebullets = new Array(3);ebullets = ['enemysword','bullet2','e5cal','enemysword'];var health = new Array(4);health = [40,30,20,10];var prevorb = 0;var prevnum = 0;
Hopefully this looks somewhat familiar, and I'm not the only one to start out writing code like this. Regardless, this debacle demonstrates our first trick:

Trick #1: globals are evil

We don't even know why they're evil, we just intuitively know.

edit: I should clarify that I'm no longer against globals. But that's getting ahead of ourselves.

Pretty soon we learn about objects. We can group our variables:class Ninja{ int x, y; int previousX, previousY; int health = 100;}class Sword{ int x, y; int previousX, previousY; int sharpness = 9000;}
We can even use inheritance to avoid copy-pasting:class Movable{ int x, y; int previousX, previousY;}class Ninja : public Movable{ int health = 100;}class Sword : public Movable{ int sharpness = 9000;}
Inheritance is nice! Nice enough to serve as our next trick:

Trick #2: object-oriented programming

Object-oriented is so great that it forms the core of many classic games, including Doom 3. Which, coincidentally, is open source.
Doom 3 loves inheritance. Don't believe me? Here's a small subset of its class hierarchy:idClass idEntity idAnimatedEntity idWeapon idAFEntity_Base idAFEntity_ClawFourFingers idAFEntity_Vehicle idAFEntity_VehicleFourWheels idAFEntity_VehicleSixWheels idAFEntity_Gibbable idAFEntity_WithAttachedHead idActor idPlayer idAI
Imagine you're an employee at id Software. This inheritance hierarchy works great for a few months. Then, one fateful Monday, disaster strikes. The boss comes in and says "Hey, change of plans. The player is now a car."

Look at idPlayer and idAFEntity_VehicleFourWheels in the hierarchy. Big Problem #1: we need to move a lot of code.

Big Problem #2: the boss comes to his senses and calls off the "player is a car" idea. Instead, we're adding turrets to everything. The car is becoming a Halo Warthog, and the player is getting a giant turret mounted on his back.

As lazy programmers, we decide to use inheritance again to avoid copy-pasting. But look at the hierarchy. Where can we put the turret code? The only ancestor shared by idPlayer and idAFEntity_VehicleFourWheels is idAFEntity_Base.

We'll probably put the code in idAFEntity_Base and add a boolean flag calledturret_is_active. We'll only set it true for the car and the player. This works, but the terrible, logical conclusion is that our base classes end up loaded with tons of cruft. Here's the source code for idEntity.

Go ahead, scroll through it. You don't have to read it all.


The point is, that's a lot of code. Notice how every single entity -- down to the last piece of physics debris -- has a concept of a team, and of getting killed. Clearly not ideal.

If you're a Unity developer, you already know the solution: components! Here's what they look like:

Rather than inheriting functionality, Unity entities are just bags of components. This solves our earlier turret problem easily: just add a turret component to the player and car entities.

Here's what Doom 3 might look like if it used components:idPlayer idTransform idHealth idAnimatedModel idAnimator idRigidBody idBipedalCharacterController idPlayerControlleridAFEntity_VehicleFourWheels idTransform idAnimatedModel idRigidBody idFourWheelController...
What have we learned?

Trick #3: in general, favor composition over inheritance

Take a moment to review the tricks we've covered so far: global variables bad, objects good, components better.

You won't believe what happens next!

Let's take a small detour into the world of low-level performance with a very simple question: which function is faster?double a(double x){ return Math.sqrt(x);}static double[] data;double b(int x){ return data[x];}
We'll hand-wave a lot of complexity away and just assume that these two functions eventually compile down to one x86 instruction each. Function a will probably compile to sqrtps, and function b might compile to something like lea("load effective address").
sqrtps takes about 14 CPU cycles on a modern Intel processor, according to Intel's manual. What about lea?

The answer is "it's complicated". It depends on where we load data from.Registers ~40 per core, sort of 0 cyclesL1 32KB per core 64B line 4 cyclesL2 256KB per core 64B line 11 cyclesL3 6MB 64B line 40-75 cyclesMain memory 8GB 4KB page 100-300 cycles
That last number is important. 100-300 cycles to hit main memory! This means in any given situation, our biggest bottleneck is probably memory access. And from the looks of it, we can improve this by using L1, L2, and L3 cache more often. How do we do that?

Let's return to Doom 3 for a real-life example. Here's the Doom 3 update loop:for ( idEntity* ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ){ if ( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) { ent->GetPhysics()->UpdateTime( time ); continue; } timer_singlethink.Clear(); timer_singlethink.Start(); RunEntityThink( *ent, cmdMgr ); timer_singlethink.Stop(); ms = timer_singlethink.Milliseconds(); if ( ms >= g_timeentities.GetFloat() ) Printf( "%d: entity '%s': %.1f ms\n", time, ent->name.c_str(), ms ); num++;}
From an object-oriented perspective, this code is pretty clean and generic. I'm assuming RunEntityThink calls some virtual Think() method where we could do just about anything. Very extensible.

Uh oh, here comes the boss again. He has some questions.
What's executing? Er... sorry boss, it depends on what entities are active at the time. We don't really know.
In what order is it executing? No idea. We add and remove entities to the list at random times during gameplay.
How can we parallelize this? Gee boss, that's a tough one. Since the entities execute randomly, they may be accessing each other's state. If we split them up between threads, who knows what might happen.

In short:

But wait, there's more! If we look closely, we see the entities are stored in a linked list. Here's how that might look in memory:

This makes our L1, L2, and L3 cache very sad. When we access the first item in the list, the cache thinks, "hey, I bet the next thing they'll want is nearby", and it pulls in the next 64 bytes after our initial memory access. But then we immediately jump to a completely different location in memory, and the cache has to wipe out those 64 bytes and pull in new data from RAM.

Trick #4: line up data in memory for huge performance gains
Like this:for (int i = 0; i .update();for (int i = 0; i .update();for (int i = 0; i .update();// ...
An object-oriented programmer might be infuriated at this. It's not generic enough! But look, now we're iterating over a set of contiguous arrays (not arrays of pointers, mind you). Here's what it looks like in memory:

Everything is all lined up in order. The cache is happy. As a side bonus, this version allows us to answer all those pesky questions the boss had. We know what's executing and in what order, and it looks much easier to parallelize.

edit: Don't get too hung up on cache optimization. There's a lot more to it than just throwing everything in an array. I only bring it up to prove a point, and I'm only qualified to give the basic introduction. Check out the links at the bottom for more info.

Time to wrap this up and get to the point. What's the One Weird Trick? What do tricks 1-4 (and many more) all have in common?

The One Weird Trick: data first, not code first
Why were globals so evil (trick #1)? Because they allowed us to get away with lazy data design.

Why did objects help us (trick #2)? Because they helped us organize our data better.

Why did components help us even more (trick #3)? Because they modeled our data better by matching the structure of reality better.

Even the CPU likes it when we organize our data correctly (trick #4). No really, what is the trick actually
Let's break it down in practical terms. Here's a representation of a typical Unity-like component-based game design.

Each component is an object. It has some state variables listed at the top, and then some methods to do stuff with those variables. This is a well-designed object-oriented system, so the variables are private. The only code that can access them is the object's own methods. This is called "encapsulation".

Each object has a certain amount of complexity. But fear not! OOP promises that as long as we keep the state private, that complexity will stay encapsulated within the object, and won't spread to the other objects.

Unfortunately, this is a lie.

Sometimes, we need a function to access two or three objects. We end up either splitting the function between those objects, or writing a bunch of getters and setters so our function can access what it needs. Neither solution is very satisfying.

Here is the truth. Some things cannot be represented as objects very well. I propose an alternate paradigm, one which represents every program perfectly:

Once we separate process from data, things start to make more sense.

Object-oriented helps us write good code because it encourages us to encapsulate complexity (i.e. state). But it forces us to do so in a certain way. Why not instead encapsulate like this, if it makes sense within our problem space?

In summary, design data structures to match your specific problem. Don't shoehorn a single concept into a bunch of separate, encapsulated objects.

Next, write functions that leave the smallest possible footprint on that data. If possible, write pure stateless functions.
That's the trick.

If this struck a chord with you, it's because I stole most of it. Get it straight from the source:
Mike Acton - Data-Oriented Design
John Carmack - Inlined Code
Casey Muratori - Semantic Compression

Thanks for reading!

Mirrored on my blog




The Poor Man's Postmortem - Lemma

The big secret of our industry is, we don't actually enjoy making games. We slave away in obscurity for years in anticipation of one glorious day.

Not release day, no. The day we can finally write a postmortem full of pretentious anecdotes, bad jokes, and unsolicited advice.

Well I just finished a game, and doggone it, I am going to exercise my inalienable rights as a developer.

The formatting on this article came out a little wonky. Read here for a better formatted version.

Things to do when making a game

Ancient gamedev postmortem traditions mandate that this section be titled "what went right". Unfortunately, the game was so shockingly good and so many things went right that a full overview would stretch on endlessly.

Instead you'll have to settle for this. These are some things I did that I recommend you do as well.

Come up with a good concept

I didn't do this one, actually. The original concept was a cartoony third-person game called Parkour Ninja. It changed every other week or so for the remainder of development. For a while the player had a pistol:

And for a while, you could rip voxels apart and re-attach them:

Almost everything got cut.

The final concept is not particularly unique. First-person parkour with a female protagonist has definitely been done before, and every third game on Steam uses voxels.

It worked in the end though, by combining familiar elements in a unique way, and by throwing in a weird, trippy, puzzle-y aesthetic. Every new idea steals from existing ideas.

In 2012 I released a short, strange, ugly, buggy alpha demo which nevertheless communicated the core ideas of parkour and mysterious voxels. Incredibly, Rock Paper Shotgun covered it. I doubt I would have stuck with the project without that affirmation.

Make it through Greenlight

I honestly have no idea how to replicate this feat. I don't know how it happened. Fortunately, Greenlight is much less daunting today than it was in April 2014.

I did almost nothing to promote the Greenlight page. I ran the campaign in tandem with a Kickstarter (more on that later), but almost all traffic came from Steam itself. Here's the embarassing trailer I used for both Greenlight and Kickstarter:

The game was greenlit in 16 days as part of a bundle of 75 other games, even though it hadn't reached the top 100 yet.

As you'll see later, if you want to make a living developing PC games, you have to get through Greenlight.

Iterate the controls

My character controller article goes into much more detail on this, but basically, take however much time you plan to spend on the controls, and then double it.

Use MIDI knobs to control game variables. Explore the game space. Use offline processing to bullet-proof your character controller.

This point applies to all games, even those that don't have a character. The player's action and the game's reaction are arguably the most important aspects of a game, because they are unique to the medium.

Design your graphics carefully

Lemma has been pretty ugly for most of its life. I got lucky with a few textures in the 2012 alpha, particularly the stone texture which features heavily in the final game. But mostly I just slapped textures on haphazardly. Here are some perfectly good textures applied in the worst possible way:

At the time, I knew something was wrong with this scene, but I couldn't put my finger on it. Allow me now, with the benefit of hindsight, to put my finger all over it.
Nowhere to climb. This is a claustrophobic indoor scene set inside some sort of derelict vessel. It belongs in Bioshock, not a parkour game.
Too busy. The textures are incredibly loud and detailed while the voxels are huge, flat, and boring.
No composition. Nothing draws my attention or invites me to explore. The shapes are all uninspired boxes.
Abysmal lighting and colors. If I recall correctly, I randomly placed the red point light on the left on a whim.

Compare to this shot from the final game:

Still some rough edges, but not overly painful.

Here's what I learned to get from point A to point B:
If you're like me, make up for your lacking art skills with code. God rays, SSAO, and particle effects worked wonders for me. And turn on mip-mapping for gosh sake.
Form and composition are more important than detailed textures. You can make a beautiful scene with just a few carefully placed shapes.
If you're trying to convey a massive sense of scale, your forms should have interesting features at every scale. A single, giant, featureless cube won't inspire awe. Neither will a giant cube with a detail pattern.
Colors and lighting make or break scenes. I probably spent as much time picking (and re-picking) colors as I did building voxels.

Support all the things

At the very least, add proper gamepad support. For me, Oculus Rift support was a huge selling point and a ton of fun for YouTubers.

Things like sparse options menus, missing gamepad support, and shoddy VR implementations enrage gamers, especially PC gamers. There's a reason TotalBiscuit starts every video with a look at the options menu.

I threw in every option I could think of, and almost every option requested by players. Y axis inversion, gamma, FOV, gamepad bindings, a framerate limiter, you name it.

Get involved with the community

I almost lost it In January 2014. Shut in my apartment for days on end, stuck in a difficult rut in production, I was going insane.

Thankfully, Columbus has a budding game development scene. I rented a desk from a local gaming incubator. The mere act of driving to work and existing around other humans got me through the winter. As an added bonus, I gained a ton of playtesters!

Every month I attend a local game development meetup. As a solo developer, it's the only time I get to talk openly about the topic that consumes 90% of my life. Seeing the same people every month and catching up on their progress is incredibly rewarding.
For the rest of the month, there's Twitter!

Do your own marketing

If you're like me, your marketing budget is $0.

On launch day, I spammed announcements to all of Lemma's accumulated fan base via Twitter, Facebook, IndieDB, GameJolt, Kickstarter, Steam Greenlight, YouTube, and an email list.

I spent several days collecting contact info by hand for various press and YouTubers. Whenever possible, I automated the process with Python and Javascript. Some resources I used:
Video Game Caster
Video Game Journaliser
Top 100 YouTubers
Bolo - useful for digging up journalist email addresses

I pulled everything into a Google Docs spreadsheet and ran a mail merge on it two weeks before the launch. Somewhere around 400 Steam keys ended up being activated.

YouTube ended up bringing the most traffic. Over 400 videos have been uploaded to date, totalling over 5 million views, mostly thanks to three huge videos posted by jacksepticeye.

Ship your localization strings in plain text

Lemma uses Excel files for localization. I use a third-party library to read them, which makes the code pretty simple.
This ended up being a great decision, because foreign players step up with their own volunteer translations. They can edit the Excel files in place and see the results immediately in-game.

Things to never do, ever

Run a Kickstarter

As Greenlight becomes easier to conquer, Kickstarter becomes exponentially more difficult. Backers have been burned too many times by now, and everyone sets their goals much lower than the amount they need to deliver on their promises.

I ran a failed Kickstarter for Lemma in March 2014. Originally, I planned to abandon the game if the Kickstarter failed. Then the Greenlight went through and I decided to cut back the budget, take some contract work, and do some budgeted art items (namely the character model) myself.

Running a Kickstarter takes too much time away from development. My advice is to find another way to fund your game if at all possible.

Write a pretentious story

If you're making Deus Ex, feel free to go wild with gritty lore and philosophical questions about trans-humanism. But if you're making Flappy Bird, you can get away with maybe a 10 second cut scene, tops. Know how much story your game can "afford".

The story of Lemma features quantum mechanics, the Philadelphia Experiment, life and death choices, infidelity, betrayal, and jealousy. All this crammed into 50 optionally collectible notes in a game about parkour.

The story tries to do too much. When all these conflicting ideas combine, they blur together into a jumbled mess that neutralizes the impact of each individual idea.

When it comes to story, do one thing, and do it well.

Suddenly switch from linear to non-linear design half-way through
I planned this from the beginning, actually. The first half of the game is linear so I can introduce mechanics one at a time. The player knows everything by the second half, so the game opens up into a non-linear cornucopia of levels that review the things you've learned so far.

This is a pretty good pattern as far as pacing, but the linear to non-linear transition confuses players. The whole first half teaches you that there's one way to "win", then suddenly, you're dropped out in the cold and left to your own devices with a completely incomprehensible world map.

[size=2]Wait, what?

I did this because I thought, "this game is about exploration, it needs to be more non-linear". But all of the alpha releases were completely linear and not a single player complained about it. In fact, many of them commented that they enjoyed how each individual level could be cleared in many different ways.

The takeaway is, there are tons of ways to make your game feel more non-linear than it is, without building a confusing tangle of interconnected levels.

Design bad puzzles

My worst puzzles break the game rules. If you have to write a custom script that manually pokes the game state when the player solves the puzzle, stop and re-think your life decisions.

I'm always worried that my puzzles are too easy and that players will breeze through them too quickly, but in reality it doesn't take much to slow players down.

Often, the simple act of exploring a 3D space is enough of a puzzle. Games like this are a continuous conversation between level designer and player. It's enough of a challenge for the player to parse what the level designer is saying.

Throw in unnecessary enemies

Enemies have been a part of Lemma since day one. I love watching players encounter them for the first time, because they're truly terrifying. That small taste of horror shakes things up and fits perfectly into the pacing.

But after the novelty wears off, enemies become annoying and redundant. There's no combat; your interaction with them is always the same: run away.

My goal was always to integrate enemies seamlessly with the environment. In parkour, the environment is already your biggest enemy and your most powerful tool, so it makes sense. Unfortunately, I only came up with one enemy that came anywhere close to achieving this goal: a sort of tower that detaches from the environment and falls on you.

In hindsight, I should have been more confident in the core gameplay and remove the enemies to focus on better level design.

Spend time on an unnecessary level editor

Some games benefit hugely from a level editor. Heck, Garry's Mod is a level editor. If you're making that type of game, more power to you.

If you're making a mostly linear, story-driven singleplayer game, a level editor doesn't make much sense. Everyone says "oh cool, there's a level editor", creates a few cubes, and then completely forgets about it.

Again: do one thing, and do it well.

Start a hobby project and transition it to professional

Hobby game development is like building a tower of bricks. You don't know how tall it's going to be, you just keep stacking bricks. Each brick represents something that happens to interest you at the time. Branching dialogue? Sure, stack it on there. A pistol? Why not. Pretentious story? Check-a-mundo.

Professional game development is like sculpting. You start with a certain amount of raw materials: time, money, motivation, player attention, etc. You plan out a rough idea of your sculpture, then you start chiselling. The size of the sculpture is irrelevant if you put your chisel in exactly the right spot.

The two paradigms are incompatible. If you're a hobbyist looking to make the switch, consider starting fresh with a new project. Results Steam
The second spike in these graphs is mostly due to jacksepticeye's Let's Play videos.



Sales: 3,171

Gross revenue before 30% cut: $43,554

Max simultaneous players: 63

Demo downloads: 10,126

Demo conversions: 277 (2.7% conversion rate)


Max simultaneous demo players: 53

Key activations: 483

Positive reviews: 77 (91%)

Negative reviews: 7

Refunds: 68


Demo downloads: 1,896

Sales: 46

Gross revenue before 5% cut: $701

Humble widget (direct website sales)

Sales: 37

Gross revenue before 5% cut: $557


Sales: 4

Gross revenue before 30% cut: $57


Demo downloads: 1,388
Lemma offers the option to anonymously upload analytics. 5,732 out of 13,410 demo downloaders (43%) actually opened the game and opted in to the analytics program.

A total of 7,310 pirated copies of the game have submitted analytics data to my server. Assuming 43% of pirates opt in to the analytics, I estimate about 17,000 people have pirated the game, for a piracy rate of 82%.

The worst part about piracy is that torrents cannot be updated, which means YouTube is full of footage of old, outdated builds.


Schedule: 3 years part-time, 1.5 years full-time
Core team members: 1
Contractors: 6
Budget: $30,000
Sunk opportunity cost: $80,000
Lines of code: 55,000
Audio assets: 200
Git revisions: 1,200

Lemma released May 12. The entire game engine is on GitHub. If you enjoyed this article, try these:
The Poor Man's Character Controller
The Poor Man's Voxel Engine
The Poor Man's Dialogue Tree
The Poor Man's Gameplay Analytics

Thanks for reading!




It's done!

It took nearly 5 years, $30,000, and most of my sanity, but the game is finally done! Check it out: lemmagame.com

Look for a more in-depth postmortem... soon. It's 6:30am and I haven't slept yet.





Lemma is finally coming to Steam on May 12. Check out the new trailer:

For the first time ever, I shelled out for Adobe Premiere rather than hacking something together in Movie Maker and OpenShot. I didn't use any of the fancy features, but it was worth it just to avoid dealing with crashes all the time. Although it still did crash (it's Adobe after all).

Ashton Morris did a number on the trailer audio. I had something entirely different all ready to go before he came in with something way better.

I've been collecting contact info for some time. I mostly just went through the following lists, pulling out info for people who might be interested in my game:
Video Game Caster
Video Game Journaliser
Top 100 YouTubers
Indie YouTubers I follow or who have already covered the game

Video Game Caster lists emails right there on the page. I pulled them out using a jQuery one-liner in the Chrome web console. I noticed a lot of them seemed to be inactive, so I used BeautifulSoup to query each YouTube page and determine when their last two videos were uploaded.

I spent today blasting out emails. I forked over $25 to increase the daily mail quota for a Google Sheets mail merge add-on, only to find out that a) the quota did not actually increase, and b) you can write your own mail merge in about 20 lines of Javascript. What a scam.

In Sheets, just hit Tools -> Script editor. Here's my script, I was blown away by this:[code=js:0]var EMAIL_SENT = "Yes";function onOpen(){ var spreadsheet = SpreadsheetApp.getActive(); var menuItems = [ {name: 'Go', functionName: 'mail_merge'} ]; spreadsheet.addMenu('Mail merge', menuItems);}function mail_merge(){ var draft = GmailApp.getDraftMessages()[0]; var subject = draft.getSubject(); var template_html = draft.getBody(); var template = draft.getPlainBody(); var sheet = SpreadsheetApp.getActiveSheet(); var data = sheet.getDataRange().getValues(); var column_map = {}; for (var i = 0; i ] = i; } for (var i = 1; i ; var email = row[column_map.Email]; if (email) { var sent = row[column_map.Sent]; if (sent != EMAIL_SENT) { var message = template; var message_html = template_html; for (var column in column_map) { message = message.replace('{{' + column + '}}', row[column_map[column]]); message_html = message_html.replace('{{' + column + '}}', row[column_map[column]]); } GmailApp.sendEmail(email, subject, message, { htmlBody: message_html }); Logger.log(email); sheet.getRange(i + 1, column_map.Sent + 1).setValue(EMAIL_SENT); // Make sure the cell is updated right away in case the script is interrupted SpreadsheetApp.flush(); } } }}
It marks rows that have been sent to by putting "Yes" in the "Sent" column. It pulls the email address from the "Email" column. Those are the only two hard-coded values. I wrote the email template as a draft in Gmail. The outgoing messages all appear in your Gmail "sent" folder.

The only downside is that Gmail also has a 100 email per day quota. In my scramble to get these emails out, I had to pay for and set up Google Apps for Work on my domain to increase the quota to 1500 per day. Turns out, the quota still didn't increase, but between my personal Gmail and the Google Apps account, I was able to hit most of my contact list. I'll get the rest tomorrow.

I had no idea PR would be so much fun! It's just like programming!

Just kidding, it's still horrible.

Thanks for reading!




The Poor Man's Character Controller

Let's say that, like so many of us, you want to make a surreal voxel-based first-person parkour game. You're trying to figure out a production schedule. What will take the longest? Graphics? Sound? Level design? I bet it will be the character controller. And I bet it will take 4 1/2 years. Why?
In running/jumping games, player movement is paramount. It takes forever to nail the right feeling.
Each game is a unique snowflake. You will not find an article explaining how to design the controls for your specific game. You're flying blind.

That said, each game offers a few transferrable bits of wisdom. Here's my story.

Make a character

You're a programmer, but one time you were able to suppress the gag reflex while using GIMP, so you're pretty much an artist too. You can draw a player character.

That's certainly... a drawing. So the player is an anthropomorphized cylinder? Well, we've seen worse.
If this character has any flaw, it's that he's too exciting and interesting. Can you make him a little more boring and generic? What if you use MakeHuman? It literally generates human characters from a template.

Much better. But there's just one problem: this is a first-person game, so when players look down, they can see their own nose:

Also, the "pectoral musculature" slider is a tad high, and players are getting confused about their gender.
You end up switching to a female character. Because why not?
Now for the nose problem. You can't remove the entire head, because a headless shadow might be somewhat disconcerting. What if you just remove the face?


(Eventually you revamp the model, hire an animator, and use separate models, one sans head, for the first-person view and shadow renderer. But none of that is entertaining.)

Make it move

You're using a great physics engine (seriously, it's quite good) that comes with a simple character controller. It looks like this:

The character is a cylinder floating above the ground, supported by a single raycast. This way, the cylinder can clear a small obstacle, and once the raycast hits it, the whole apparatus jumps on top.
Since the game world is made of voxels, you quickly run into this problem:

Tons of players get stuck this way in your first alpha release. Rather than spend time on an elegant solution, you brute-force it:

Despite this, people still get stuck. You resort to a collision handler that actually pushes the character away from anything that could cause problems. You also interpolate the vertical position to smooth out the camera when traversing uneven voxels:

Make it unrealistic

In an attempt to model reality accurately, the game has no air control at this point. When you originally made this decision, you somehow forgot that the game is about an imaginary cube world.
Thankfully, after listening to player feedback, you have a change of heart. In the real world, traceurs have many control dimensions (namely, their muscles) that enable precise jumps. Video games have exactly one button. Air control is only fair.

Make it fun

Since parkour is about momentum, you want the character to take several seconds to reach max speed. Which is fine, except that low acceleration makes small adjustments difficult. The first step takes forever, and the character feels like a semi truck.
Your solution uses different accelerations depending on the current speed. The final speed curve looks like this:

This solves half the problem, but players can still use the mouse to quickly whip the camera around 90+ degrees, which resets their speed back to zero.
You experiment with a few hacks, but eventually settle on a solution using the dot product. It's basically a measure of the angle between two vectors multiplied by their magnitude. (Here's a quick interactive demo.)
You use a dot product to find out how much side-to-side momentum the character has. If they're facing perpendicular to the direction of their momentum, the dot product will be large. You use that to increase the acceleration. Long story short, turning no longer burns momentum.

Make it slippery

There are other ways to lose momentum, like running into a brick wall. You try to mitigate this with low friction physics materials, but angling yourself into a wall will always slow you down:

You are inspired by a blog post by Mike Bithell on this topic. You use three raycasts and some cross product magic to figure out a velocity that will slide along the wall.

Later on, you discover another annoyance. Your wonderful voxel engine sometimes helpfully constructs voxels like this:

There's a seam between the two adjacent blocks due to floating point error. When the character moves flush with the wall and tries to jump upward, it hits the seam and immediately stops.
The solution is brain-dead simple: change the cylinder to a capsule. Yes, it really does take you 4 years to figure this out.

Make it forgiving

At first, players just don't understand the movement mechanics. They think they can't get from point A to point B, until you tap them on the shoulder and explain they have to do XYZ. You suspect this is because your tutorial is actually a placebo at this point.
Eventually, the tutorial gets pretty good. Everyone understands the movement capabilities, and they can figure out which moves to use. But now they have a new problem: they fail in the twitchy execution and timing details of their plans.
The worst culprit is a single infamous jump in the tutorial. It tries to teach players how to grab ledges because it's too long to cross with a normal jump.

Players fail two or three times before you tell them to "button-mash", which helps them nail the timing through sheer brute-force. Interestingly, as soon as they make this one jump, they have no trouble completing future jumps without button-mashing. For a while, you arrogantly conclude that people are just stupid.
Part of the problem is still the tutorial: you ask players to make a leap of faith and perform a move they've never seen before. They have no idea what the character will do or how long it will take. So you add another, earlier tutorial that lets players try out the ledge grab in a safe space.
But the frustration of perfect timing remains. The solution is two-fold:
Let players jump for a split second after they walk off an edge.
Let them hold buttons instead of tapping at the right moment.

To the surprise of no one but you, this makes the game a lot less frustrating and a lot more fun.

Make it look good

Over the course of development, you stumble on a few animation tricks. With enough nifty procedural animation, maybe people won't notice your shoddy weight painting and texture work!
Attach the camera position to the character's head bone, but use a separate root bone to control camera rotation. This eliminates weird rotations when blending between animations.
Speaking of which, use a quadratic curve to blend between animations rather than straight linear.
Also, don't use linear matrix interpolation. Instead use quaternion interpolation.
Remember the dot product from earlier, for calculating side-to-side momentum? Use that to make the character and camera lean when turning at speed.
Run the character bone transforms through filters for nice effects like tilting the character's head when looking up and down.
Plant the character's feet and play a little foot-shuffling animation when turning in place.

(For a much more eloquent and in-depth look at procedural animation, check out David Rosen's GDC talk.)


Budget an extraordinary amount of time for your character controller. Make it special and unique. And if you're me, prepare to be wrong most of the time.
Lemma is set to release in May. The entire game engine is on GitHub. If you enjoyed this article, try these:
The Poor Man's Postmortem (coming soon!)
The Poor Man's Voxel Engine
The Poor Man's Dialogue Tree
The Poor Man's Gameplay Analytics

Thanks for reading!

Mirrored on my blog




Screenshot Saturday 213

It's the end of February and this game is supposed to be content-complete. In a sense, it actually is. All the levels are done. Twenty in all. I thought this month would never end!

Just so you know, there are sixty of those lights and I had to hook up each one individually. It fell just barely beneath the "worth it to automate" threshold.

Don't look too closely at this next one, it's a bit spoilery.

All that remains is to fill out a few story elements and wrap this thing up with some semblance of a satisfying ending.
I actually made some very interesting improvements to the parkour code in the past few weeks, but I'll save it for the upcoming character controller article.

For now, that's all I got! Thanks for reading.

Mirrored on my blog




The Poor Man's Voxel Engine

This is not a tutorial. It's a story. A Voxel Odyssey.
The story starts with 19 year old me in a dorm room next to the Ohio State stadium. I don't have the repo from this stage of development (SVN at the time), but I remember the process clearly.

[size=2]Photo by Kristen Sutton
XNA 4 comes out in September 2010. I immediately dive in. This turns out to be a poor life decision.
For some reason, one of the very first things I implement is motion blur. I think this is Lemma's first screenshot, although at this point, it's a cartoony third-person game called "Parkour Ninja":

[size=2]Such motion blur
I skip past the initial naive implementation of spawning a cube for each voxel cell. My first move is to iterate over these individual cells and combine them into larger boxes using run-length encoding.
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]
Performance is already a problem even at small scales. I'm re-optimizing the entire scene every time I make an edit. Obviously, my next move is to only optimize the parts I'm editing.
This turns out to be difficult. Take this example:

I add a cube at the top of this stack. To optimize this into a single box, I have to search all the way to the bottom of the stack to find the beginning of the large box, then add 1 to its height and delete my little cube addition.
To speed this process up, I allocate a 3D array representing the entire scene. Each cell stores a pointer to the box it's a part of. Now I can query the cells immediately adjacent to my new addition and quickly get a pointer to the large box next to it.
Removals are the next challenge. My first idea is to split the box back into individual cells, then run the optimizer on them again.
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]
This turns out to be horribly slow. I soon realize that rather than splitting the box into individual cells, I can skip a few steps and split it into "sub-boxes". I still run the optimization algorithm afterward, but I can make its life easier.
I am thrilled to get this demo running on my roommate's Xbox 360. It fails to impress the girls in the neighboring suite.

Goodbye Xbox
I quickly run into more issues. The CLR's floating point performance is absolutely abysmal on Xbox 360. The physics engine breaks down and cries after spawning 10 boxes or so. I decide to target PCs instead.


I render scenes by copying, stretching, and scaling a single cube model. Slapping a texture on this cube turns out to be a horrible idea that looks something like this:
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]
To avoid texture stretchiness, I eventually write a shader to generate UVs based on the position and normal of each vertex. Here's the final version for reference:float2x2 UVScaleRotation;float2 UVOffset;float2 CalculateUVs(float3 pos, float3 normal){ float diff = length(pos * normal) * 2; float2 uv = float2(diff + pos.x + (pos.z * normal.x), diff - pos.y + (pos.z * normal.y)); return mul(uv, UVScaleRotation) + UVOffset;}
Next, another performance crisis. Somehow I realize that doing a whole draw call for each and every box in a scene is a Bad Idea(TM). So I take the obvious step and... use hardware instancing. Yes.
Incredibly, I'm actually proud of my work so far. Somewhere around this time I also discover my lifelong love of terrible music.

[size=2]Local multiplayer? What is this game?

Improved level format
At this point, I'm saving and loading levels via .NET's XML serialization. Apparently XML is still a good idea in 2010. The voxel format is simply a 3D array represented as an XML string of ASCII 1s and 0s. Every time I load a level, I have to re-optimize the entire scene. I solve this by storing the boxes themselves in the level data as a base64 encoded int array. Much better.

Per-surface rendering
I start building larger levels and run into another graphics performance problem. The engine is simply pushing too many triangles. In a complex scene, a significant chunk of boxes are surrounded on all sides by other boxes, completely hidden. But I'm still rendering them.
I solve this problem by breaking each box into its individual faces. I actually iterate across the whole surface to determine what parts (if any) are visible. Shockingly, this turns out to be terrifically slow. I eventually mitigate the issue by caching surface data in the level file.
I render all these surfaces via... drum roll... instancing. Yes, really. I open Blender, create a 1x1 quad, export it, and instance the heck out of that thing. These are dark times. But I'm finally able to render some larger landscapes:

Time to see some cool physics. I now have two kinds of voxel entities: the static voxel, represented in the physics engine as a series of static boxes, and the dynamic voxel, represented as a single physics entity with a compound collision volume constructed of multiple boxes (I should plug the incredible BEPUPhysics for making this possible). It works surprisingly well:

Around this time I also switch to a deferred renderer, which is why I spawn an unreasonable number of lights at the end of that video.

Chopping down trees
Now it's time to take physics to the next level. My goal is simple: I want the player to be able to cut down a tree and actually see it fall over, unlike Minecraft.
This lofty dream is basically a graph problem, where each box is a node connected to its adjacent neighbors. When I empty out a voxel cell, I need a fast way to determine whether I just partitioned the graph or not.
So I add an adjacency list to the box class. Again, shockingly, calculating adjacency turns out to be a huge bottleneck. I later cache the adjacency data in the level file, which eventually balloons to several megabytes.
Now every time the player empties out a voxel cell, I do a breadth-first search through the entire scene, marking boxes as I go. This allows me to identify "islands" created by the removal of the voxel cell. I can then spawn a new physics entity for that island and break it off from the main scene.
I eventually come up with the idea of "permanent" boxes, which allows me to stop the search whenever I encounter a box that cannot be deleted.
I design a new enemy to showcase the new physics capabilities. I also test the limits of awkwardness and social norms by narrating gameplay footage in a dorm room full of people studying.

Around this time I learn about broadphase collision detection. My engine is scattering thousands of static boxes around the world, which makes it difficult for BEPUPhysics' broadphase to eliminate collision candidates. At the same time, it's becoming obvious that rendering the entire world in a single draw call is a bad idea.
I fix both of these issues by splitting the world into chunks. Each chunk has a static triangle mesh for physics, and a vertex buffer with basically the same data for rendering. Since I have to regenerate both of these meshes every time a chunk is modified, the chunk size can't be too large. Also, smaller chunks allow for more accurate frustum culling.
At the same time, the chunks can't be too small or the draw call count will explode. After some careful tuning I eventually settle on 80x80x80 chunks.
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]

Partial vertex buffer updates
This is probably the low point.
By now, I am incredibly proud of my "loosely coupled" architecture. I have a Voxel class and a DynamicModel class which know nothing about each other, and a ListBinding between the two which magically transforms a list of Boxes into a vertex buffer.
Somehow, probably through questionable use of the .NET Timer class, I locate a bottleneck: re-sending an entire vertex buffer to the GPU for every voxel mutation is a bad idea. Fortunately, XNA lets me update parts of the vertex buffer individually.
Unfortunately, with all the surface culling I do, I can't tell where to write in the vertex buffer when updating a random box. Also, how to shoe-horn this solution into my gorgeous cathedral architecture.
This conundrum occurs during the "dictionary-happy" phase of my career. Yes. The ListBinding now maintains a mapping that indicates the vertex indices allocated for each box. Now I can reach into the vertex buffer and change things without re-sending the whole buffer! And the voxel engine proper still knows nothing about it.
This turned out to never really work reliably.
I lied earlier, this is probably the low point.
Voxel mutations cause noticeable stutters by now. With no performance data to speak of, I decide that multithreading is the answer. Specifically, the worst kind of multithreading.
I spawn a worker thread, sprinkle some locks all over the place, et voila! It's multithreaded. It gains perhaps a few milliseconds before the main thread hits an unforeseen mystical code path and the menu somehow manages to acquire a lock on the physics data.
I am ashamed to admit that I never got around to correcting this colossal architectural faux pas.
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]

Large Object Heap
I'm now building large enough levels to run into memory issues. Turns out, the .NET runtime allocates monstrous 80x80x80 arrays differently than your average object. I write more about this here.
Long story short, the garbage collector doesn't like to clean up large objects. I end up writing a custom "allocator" that hands out 3D arrays from a common pool. Later, I realize most of the arrays are 90% empty, so I break each chunk into 10x10x10 "sub-chunks" to further reduce memory pressure.
This episode is one of many which explain my present-day distaste for memory-managed languages in game development.

I graduate and work at a mobile game studio for the next year. The engine doesn't improve much during this time, but I start to learn that almost everything I know about programming is wrong and incomplete.

[size=2]One of the many "offices" I've worked in over the years
I leave my job in February 2014 and continue hacking the engine. By now it's over 30k LOC and I am morally and spiritually unable to start over on it.

Goodbye allocations
With my newfound awareness of the .NET heap, I realize that my vertex arrays for physics and rendering are also probably landing in the Large Object Heap. Worse, I am reallocating arrays every time they change size, even if only to add a single vertex.
I genericize my Large Object Heap allocator and shove the vertex data in there. Then, rather than allocating arrays at exactly the size I need, I round up to the next power of 2. This cuts the number of allocations and makes it possible for my allocator to reuse arrays more often.

Goodbye cathedral
I finally throw out the "loosely coupled" ListBinding system and pull the vertex generation code into the voxel engine itself. The resulting speed boost is enough for me to go back to re-sending entire vertex buffers rather than faffing about with partial updates.

Goodbye index buffer
Up to this point, I've been maintaining an index buffer alongside the vertex buffer. In a much overdue stroke of "genius", I realize that since none of the vertices are welded, the index buffer is just a constantly repeating pattern, which is in fact the same for every voxel.
I replace the individual index buffers with a single, global buffer which gets allocated to the nearest power of 2 whenever more indices are needed.

Bit packing and compression
Many numbers in the level data format are guaranteed to fall in the 0-255 range. My friend decides to pack these numbers more efficiently into the integer array. He writes about it here.
I also pull in third party library #27 (SharpZipLib) and start zipping the level files. These changes cut the file size to under 30% of the original.

Goodbye UV optimization
I've been storing a huge amount of surface data like this:class Box{ public struct Surface { public int MinU, MaxU; public int MinV, MaxV; } public Surface[] Surfaces = new Surface[] { new Surface(), // PositiveX new Surface(), // NegativeX new Surface(), // PositiveY new Surface(), // NegativeY new Surface(), // PositiveZ new Surface(), // NegativeZ };}
I do this so that I can resize surfaces that are partially hidden, like this:
[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]
At some point in the vertex buffer overhaul, I realize that performance-wise, the physics engine doesn't care what size the surface is.
I use this fact to speed up mesh generation. I generate 8 vertices for the corners of each box, then copy them where they need to go in the vertex buffers.
Really, the graphics engine doesn't care much about the size of the surface either, aside from fill rate. What matters is whether the surface is there or not.
With this in mind, I kill the UV optimization code and store the surfaces in memory and in the level file like this:class Box{ public int Surfaces;}
The bits of the int are boolean flags for each surface. Yes, I could do it in a byte. Actually, maybe I should do that. Anyway, this simplifies my level loading and saving code, cuts my file sizes down to about 512kb on average, and drastically reduces memory usage. Axing the UV optimization routine also speeds up mutations.

[color=rgb(85,85,85)][font='PT Serif'] [/font][/color]

Clearly, this article is mostly useless if you're interested in writing your own voxel engine. The final result is far from perfect. I just want to share the petty drama of my past four and a half years. I for one thoroughly enjoy reading about other people's struggles. Maybe that's weird.
Lemma is set to release May 2015. The entire game engine is on GitHub. If you enjoyed this article, try these:
The Poor Man's Dialogue Tree
The Poor Man's Gameplay Analytics
The Poor Man's Character Controller (coming soon)

Thanks for reading!

Mirrored on my blog




Screenshot Saturday 211

Last Saturday we had the Short North gallery hop. Hundreds of people came through our gallery to see art. The guys helped me set up the Oculus and a projector on the wall.

Sometimes I had to go out and pull people in, but most of the time, there was a line. My favorite customer by far was this kid:

He jumped right in and played like a pro. The mother (recording video) was super supportive and excited.

At past expos, I dropped players into a bespoke demo level that skipped a lot of story and jumped straight into the tutorial. In December, I revamped the "real" first level (called "Rain") to be more like the demo level. If your game can't entertain people in two minutes at an expo, what's going to keep them playing if they buy the game?

After the gallery hop, I checked my Git logs. "Rain" was present in the game without a major overhaul for the past two years. I rebuilt every other level. Time to throw Rain in the trash.

For something that has received constant attention and iteration for two years, I was surprised how quickly I replaced it. Here's Rain 2.0:

It accomplishes all the same goals, teaches the same mechanics, and introduces the same (optional) story elements, but it's much more simple, streamlined, and just fun.

Rain 1.0 was the first level I ever made. At that stage I didn't entirely know what the game was. I had no level design vocabulary. I didn't even know how big the level should be or where it fit into the overall experience. I patched those details in later.

For Rain 2.0, I knew every goal from the start. I made decisions easily by asking which option best fit those goals. For once, my design tasks aligned in the correct order. As a result, I replaced two years of iteration in a single long work day.

Of course it's not perfect. Playtesting and iteration will reveal potential improvements, but it's already miles ahead of the old level. As a side note, here's a fun glitch I discovered during the redesign:

But that's not all! Oh no, that was just Monday. I built another level in the "aqua" biome, bringing the count to 18 out of 20.

One more aqua level, then the ending level, and it's a wrap. The rest of development will be tweaking, polishing, and bugfixing, which at least for me is much easier than pulling creative content out of thin air. Hang in there. Thanks for reading!

Mirrored on my blog




Screenshot Saturday 210

I finished last week's map. It has some spinny things.

Then I made this week's map.

Who knew purple and green could look so... not terrible?

Anyway, this puts me ahead of schedule. There are three levels remaining. My goal is to for the game to be playable from start to finish by the end of February. It's ambitious, but I'm confident I can do it!

Today I took a break from level design to do some hardcore coding for the first time in a while. It was a breath of fresh air, which definitely reinforces the realization that I'm a programmer first and foremost. I optimized a ton of stuff, cut the level data size in half, and killed a metric crap-ton of memory leaks, but I'm way too tired to write about it right now. Maybe later!

That's it for this week. Thanks for reading.

Mirrored on my blog




Screenshot Saturday 209

This week was crazy productive. I finished last week's level, finished another level, which looks like this:

...which also included some story-related writing and scripting, and actually started working on NEXT week's level, which looks like this:

I seem to be on a purple streak lately. Actually, purple may rise unintentionally to be the most prominent color in the game. Also, this last level is apparently a subconscious ode to Monument Valley.

Amidst all this I'm constantly tweaking and fixing things, which isn't particularly exciting. However, this week did bring one interesting story: a playtester reported their graphics card overheated. Apparently, the poorly designed card couldn't run at 100% utilization for any significant stretch of time.

The playtester asked for a framerate limiting feature to prevent the card overheating. So now, I cap framerate at 120 FPS, a number which is adjustable via a slider in the options menu. Details like this take up an increasingly large proportion of development time as the project nears completion.

That's it for this week. Thanks for reading!

Mirrored on my blog




Achilles + Screenshot Saturday 208

This past weekend I participated in the CivicHacks "Game Jam for Good". The goal was to raise awareness of the global water crisis and ultimately promote PackH2O, a Columbus-based startup that designs water backpacks for developing water-stressed regions.

The jam lasted 48 hours. My entry is called "Achilles".

Achilles is a multiplayer text-based simulation. You the player must manage a village in a third-world country experiencing a water crisis. You can play it here (bit.ly/waterjamachilles) assuming I haven't stopped paying for the cloud server.

It's incredibly depressing. Really, only bad things can happen. I had plans to add pregnancy and childbirth, but ran out of time. Here's a video of it in action:

The core idea was this: to convince people of the effectiveness of the PackH2O backpack, a promotional game should put players in the shoes of the people who will use it. And I think I accomplished that. In the game, the backpack allows people to carry twice as much water. The player experiences first-hand through gameplay how useful the backpack is.

Despite the game's inherent awfulness, it won an "honorable mention", which actually turned out to be good for a $500 gift card. You can check out the other winning games on the CivicHacks website.

On the backend, I wrote Achilles in Python using Flask as a web server framework (highly recommended).
I used gevent for greenlets, which made it very easy to write time-based procedures for each character. For example, here's how a man builds a hut:if village['build_material'] > 0: village['build_material'] -= 1 notify(world, village['id']) state['state'] = 'building' notify(world, man['id']) gevent.sleep(world_seconds(world, 60 * 60 * 17)) village['huts'] += 1 notify(world, village['id']) man['state'] = None notify(world, man['id']) send(village['id'], { 'event': '{0} finished building a hut.'.format(man['name']) })else: send(village['id'], { 'event': 'Not enough build material for a hut.' })
The "notify" and "send" functions send JSON objects to the relevant clients over WebSockets.
On the frontend, I only used jQuery (no plugins) and Mustache.js for templating.
If you're interested, check out the code here on Github.

Compression-based programming
I used this game jam as an opportunity to try out compression-based programming as advocated by the excellent Casey Muratori. The idea is, instead of spending time upfront designing a complex cathedral-like architecture, you should write straightforward, ugly, even repetitive code (copying and pasting). When it's done, you go back and "compress" the code into something nicer via refactoring.
Of course, some things are straightforward enough that you can compress them as you go. For example I always knew there would be a "send" function.
Casey says one of the big problems with "normal" programming is that you assume you know everything at the beginning of a project, when in reality, you often don't. Compression-based programming has you architecting things only after you've seen the whole picture.
After a weekend of experimentation, I think this pattern of coding results in plain, boring, easy to understand code, which is definitely a good thing. Normally I come up with some super fancy way to write everything very elegantly, which creates more work than it's worth in the end.
Another paradigm Casey rails against is object-oriented programming, so I also tried writing everything procedurally. The result was again boring but simple and easy.
The biggest win was separating state from behavior. On both the client and server, all the state lived in a single object, parts of which were operated on by various procedures.
Normally, I often link state and behavior so closely that they're impossible to separate. Every time I write a closure (probably my favorite bad habit), I squirrel away an opaque bit of state inextricably tied to an anonymous bit of behavior.
Time to wrap this up: you should try coding in a boring, straightforward, state-separated-from-behavior, procedural style. The result is a breath of fresh air in a world of increasingly clever object models and cathedral architectures.

Screenshot Saturday 208

This week's level is not quite done yet, but I have an excuse! Power was out at the incubator for two days, and the internet didn't come up fully until just yesterday.

Still, the level should be done some time this weekend and is already looking pretty good.

I finalized the promotional graphics and published the game to Steam in "coming soon" mode. I'm incredibly grateful to Sam Gebhardt for contributing his Hollywood artistic talent!

I updated the website to feature Sam's artwork and shot a brand new trailer:

The 5 second cuts didn't turn out to be as good of an idea as I hoped, but it works for now.

Progress continues. Lord willing I will finish this game with or without power!

Mirrored on my blog: Achilles

Mirrored on my blog: Screenshot Saturday 208




Screenshot Saturday 207

Records continue to be broken. This week's map was actually done on Wednesday!

Although most of Lemma is a strange hybrid of natural and alien-looking architecture, my design calls for a few "industrial / man-made" themed maps. For story reasons, and also because I just want to parkour through a skyscraper.

So on Monday I asked Twitter this question:
Would people be upset if I do a few levels in the visual style of Mirror's Edge? Would that be tribute or rip-off?[/quote]

Answers varied, but unfortunately I had already started working on it, and it turned out so awesome that I had to keep going. At this point I don't care if people think it's a rip-off.

Reinforcing the notion that 90% of my dev time is spent picking colors, I also revamped the skybox on Valley.

Some friends at imaekgames recommended that I get the Steam page for Lemma up as soon as possible to ensure a spot in the "Coming Soon" list, so I started crafting some assets:

Currently working with an artist to redesign the main capsule (top left), but I'm pretty happy with the other assets. Here's what the page looks like so far:

So yeah, crazy busy. This game might actually get done on time.

That's it for this week, thanks for reading!

Mirrored on my blog




Screenshot Saturday 206

I moved my office into an incubator / art gallery this week.

The move is mostly for my own sanity. Turns out, working alone in your apartment for 9 months isn't the most fun in the world. It's a Herculean effort just to stay motivated. I also lost all semblance of a disciplined sleep schedule.

Productivity has been great since the move, and I'm back on a normal sleep schedule. Having people around is great, even if I mostly tune them out to focus on work (sorry guys).

In keeping with the production schedule, I finished the last frost level today. Unfortunately its massive size doesn't lend itself too well to screenshots. I tried my best:

I also completed the dialogue and cutscene for the first major player decision in the story. Sorry, no screenshots. It isn't much to brag about, but spoilers are spoilers.

Lastly, I updated the visuals for the movement prediction / block creation mechanic. Someone pointed out a while back that the old visuals made it difficult to distinguish overlapping shapes. Here's how it used to look (I always love sneering at old screenshots):

And here's the new effect with edge highlights:

I could have used a texture, but I ended up doing it in the shader based on UV coordinates:const float radius = 0.15f;float2 diff = float2(min(max(uv.x, radius), 1.0f - radius), min(max(uv.y, radius), 1.0f - radius)) - uv;float highlight = 0.6f + (5.0f * (diff.x * diff.x + diff.y * diff.y) / radius);
I seem to recall reading a way to do it with only arithmetic primitives, but I couldn't remember it or figure it out.

A bunch of other stuff also happened that I won't bother listing. If you are for some reason interested in nitty gritty details, the Steam beta tester group has extensive changelogs.

That's it for this week! Thanks for reading.

Mirrored on my blog




Screenshot Saturday 205

For the first time in the history of Lemma, I'm actually keeping up with my self-assigned pace of one new level per week.

These past two weeks I made two more frost levels. The plan calls for one more frost level, then it's on to the other two biomes.

Clicky for giffy

Both of these levels have interesting quirks and unique features. They're probably too tough right now, but I'm scheduling plenty of time to playtest and sand down the sharp edges.

For me, the biggest challenge is to start with a blank slate and form new shapes out of nothing. It's much easier to playtest an existing design and think of ways to improve it. So I'm getting all the hard work out of the way first.

As I build these levels I finally get to implement story elements which have languished on the drawing board until now. I already have code for very simple cutscenes, and one of the five endings is basically done already.
[color=rgb(85,85,85)][font='PT Serif']
I continue to solve old, long-standing gameplay annoyances.

Normally in Lemma, you can build floors by rolling or sliding off an edge, causing a blue platform to appear beneath you.

The problem is, you can spam this move and build an infinitely long blue platform. I originally prevented this with a strict rule: you can't build a platform if you're already standing on a blue surface. This rule gets the job done, but it's clunky, unintuitive, and difficult to explain to players.

I thought about another bothersome exploit that used to plague Lemma. In older versions, you could get in a corner and climb up indefinitely by jumping between the two perpendicular walls. The clunky solution was to nerf the wall-jump, which was admittedly ridiculous. But the over-powered wall-jump was fun.

So I kept the crazy wall-jump and wrote code to specifically detect the corner case (ha). I kept a running counter of spammy corner jumps. After a certain number, it disables wall-jumping in that area.

Now, the proper solution is to design a game with mechanics so elegant and simple that this whole situation never arises in the first place. But it's way too late for me to do that now, so we're stuck with this. Surprisingly, it doesn't bother people at all. Experienced gamers immediately try to exploit the wall-jump, see that it doesn't work, and move on. The rest of the game is unaffected, and I can keep my insane OP wall-jump.

Given the success of this exploit patch, I decided to do something similar to fix the issue with building floors. Now, when you try to build a floor on a blue surface, I do a breadth-first search to find solid ground. You can build on blue surfaces as long as you don't stray too far from solid ground.

Just like the wall-jump, the code is actually much more complicated now, but it boils down to this: you can always build floors unless you're trying to exploit.

And now for some random technical oddities. First, a quick tip if you're developing a game from scratch on Windows: make sure to handle DPI scaling properly. Lemma boots in fullscreen borderless window mode, and if the DPI scale setting is above 100%, Windows automatically scales the entire game window, which pushes most of it offscreen.

Here's a Gist that features an app manifest which will disable auto DPI scaling for your game.

In other news, I stumbled on the TextBelt library this week, which allows you to send SMS messages from a server for free. It's a Node project, but the concept is simple enough to easily replicate in your server framework of choice: blast emails to every single mobile provider gateway, knowing that only one provider owns the phone number and will allow the message through.

I imagine it would be fairly easy to optimize if the gateways send back email errors. At any rate, I'm tempted to take a weekend, write a fun SMS-based party game using this library, and try it out on some friends.

That's it for this week! Thanks for reading.

Mirrored on my blog




Screenshot Saturday 203

Big update this week!

My voxel renderer now has the capability to overlay everything with any texture I want. I'm using it on a new set of interconnected winter levels. This way I don't have to manually come up with a frosty version of each texture.

Without giving away too much, this week I built a new system that has implications for both puzzle solving and movement mechanics.

I also went back to several levels and fiddled with lighting again. Basically 90% of my development time is spent adjusting colors. Before / after:

Clearly, the old version relied heavily on bloom. I'm trying to avoid that a bit more now. Bloom is like crack cocaine to game developers.

Other random things:
When you walk off an edge, there is now a split second of forgiveness during which you can still jump. Just filing down another edge to make player movement less frustrating.
I finally killed an old glitch that subconciously annoyed me for years. Lemma has "bullet time", and up until this week it stuttered noticeably when running in slow-motion, despite maintaining a high framerate. I peeked into the BEPUPhysics source code and realized it runs on a fixed timestep with an accumulator. It updates at 60 FPS regardless of the actual framerate. So when I changed the time scale, that 60 FPS dropped to 30 FPS. But no more! I now scale BEPUPhysics' target framerate as well, and the result is silky smooth slow motion.
I liked using Jekyll for my blog so much, I also migrated the Lemma website, and updated it in the process. The site was already hosted on S3 so half the work was already done.

That's it for this week. Thanks for reading!

Mirrored on my blog




Screenshot Saturday 202

I've come to several realizations this week.
Lemma is going to be good, but not great. I have to accept my limitations and finish the thing to the best of my ability.
Lemma is more of an experience than a traditional video game.

With these two ideas in mind, I am focusing the next few months on making Lemma the least frustrating, most enjoyable experience I can.

So I'm trying to come up with puzzles that seem difficult but are actually simple to solve. The wonderful Monument Valley did a great job of this.

Fun fact: that's actually a public domain composite image of the dark side of the moon. I wrote a custom "sky decal" shader to paste it up there while still cooperating with the distance fog.

I kind of want to live there.

So that's where I'm at. I've been stressing a lot lately but I think I can actually finish the game in April. Whether I think I can or not... I have to.

That's it for this week. Thanks for reading!

Mirrored on my blog




New blog, new app, new screens

Lots of stuff going on this week.

New dev blog

First off, my website got a much-needed overhaul. The horrible slowness of Wordpress.com was driving me nuts, so I switched to a custom-built site.
I used Jekyll, which is a static site generator. It spits out a bunch of HTML files which you can upload to a server, as opposed to Wordpress, which generates fresh HTML every time someone loads your page.
Absolute control. You can customize the site theme without dealing with mountains of horrible PHP. And I can finally post HTML5 videos.
You can host your website on Amazon S3, which is about 50 cents per month, 10x faster than a normal server, and pretty much guaranteed to never go down. Wordpress absolutely crawls in comparison.
Perfect for coders, because you can write articles in raw HTML or Markdown in your favorite text editor, and you can track your site in a Git repository.

No easy way to offer email subscriptions to readers. RSS is easy to set up though.
Jekyll is written in Ruby and thus has a bunch of annoying dependencies to install. Still, the whole process is definitely easier than setting up a fresh Wordpress install.

Migrating the comments to Disqus was super easy. I use s3_website to deploy the site to S3. It only updates diff'd files, so it only takes a few seconds in most cases.
Overall I recommend it if you're a coder.

New app

I got hired this summer to do a mobile game as part of a franchise tie-in. The game is finally out today for free on Android.
Here's some (raw, uncut) gameplay footage:

grepr patch

grepr has garnered a modest but promising amount of attention. Most notably, iDubbbzTV had some great things to say about it. (warning: language)

I mostly agreed with his bigger complaints, so I made a few tweaks:
The terminal menu is now more simple and straightforward to operate
The enemy AWK drone is now larger and easier to spot
The third level, where the enemy AWK first appears, is now greatly simplified
Some glitchiness pertaining to the data node collision volume is now fixed

More importantly, the game now runs on Linux! It's 64-bit only at the moment, but everything seems to run just fine. If the game glitches on you, make sure you have libsdl2-2.0.0 installed.

New screens

Amidst all this craziness you might think I've abandoned Lemma. And you'd be wrong. Here are some fresh new screens:

I also fleshed out a whole ton of writing:

So yeah, lots of stuff happening. Stay tuned.
That's it for this week. Thanks for reading!
Mirrored on my blog




Screenshot Saturday 199

"Level design for days" has been my motto for several months now, and this week is no different.

Behold, new challenge levels! These are timed, bite-sized maps with simple goals that can be completed in under a minute. They're the kind of things you can create in the level editor and share on Steam.

I need to find a new texture for that garish green material.

Lots of other things are happening, but they're more like a million tiny updates rather than a few conveniently screenshot-worthy ones. So that's it for this week! Thanks for reading.

Mirrored on my blog



  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!