Development Blog

Get updates with RSS or Twitter.

Bootstrapping Irrationality

At some point I noticed the best dish on a menu is easy to find: It's where your eyes feel friction.

Today I'm sick in bed, and I've been watching Carl Sagan's Cosmos on Netflix. He was discussing Pythagoras, and he mentioned that the terms "rational" and "irrational" originated with rational and irrational numbers: Those that can or can't be represented by a ratio of whole numbers.

Pythagoreans loved their whole numbers and mystified them. "Irrational" came to mean "untrustworthy" or "foolish" or "not justifiable". But we have all kind of uses for irrational numbers. To ignore them now would be backward. Pi only equals 3 in law books.

I'm thinking there is a very useful order behind irrational thinking too: Modern self-professed rational thinkers ignore impulse and don't notice the value of the passions. Pythgoreans tried to shoehorn every observation into whole numbers. I think these are the same.

If you open yourself to seeing impulse and intuitive thought as valuable, the division between rationality and irrationality might disappear.

For the past year, I've been scribbling in a notebook in bars, between games of Galaga, after typing in coffee shops as long as I've got a plan or patience. I've been trying to engineer an experience. It's working, but it can't be the best way.

I hold games up as an art form, and I see rationality as a means to accomplish anything. But it doesn't make sense to ignore a set of methodologies or a system of thought and just not try them.

Degrees project history

Last August, I got it into my head that a gyroscope could give you intuitive control over three-axis rotation, and I wanted to see it work. I figured I'd try three axes of motion too, and for some days I built up nice flight controls and theorized about direct manipulation.

To test motion I needed an environment to move around inside. So I built a sparse grid of cubes in three axes, and spent ages just flying and tweaking until it felt really good. At the time, I concluded the controls were too clumsy to offer anything but forward motion if you had three axes of rotation, so for a long time Degrees was going to be an outdoor space flight game.

Then one day, I got in.

The cubic grid made these cool visual patterns, kind of like the moiré you see in layered chain-link fences. Technically that's aliasing, and aliasing is usually bad because it's a visual distraction and an accident. But when it's not an accident, aliasing can be interesting and unique. For example, when guitar effects manhandle the signal, roundoff error gets exaggerated and you get a harder sound. So I welcomed aliasing from 3D patterns as the basis of the game's visual style. Everything would be cubes: Voxels.

I spent a few weeks creating arbitrary level geometry in tightly-packed spaces and making it draw quickly. I started with a 3D grid of color values and derived the surfaces. Since every square points one of six directions, there were lots of unique optimizations to try. This established a virtuous cycle where I'd decide I wanted the visuals to improve in some direction, then I'd code it up and see how fast I could make it, and therefore I'd have an idea of the technical limits of that style. The game would get prettier, then faster, then prettier again.

Random voxel data for testing surface generation.

The game needed moving objects next, and they had to be voxel objects. But I needed to do something special, because they needed to stay in rectilinear alignment with the level, or else it would just look messy. They needed to rotate but still be part of the voxel grid. Besides, I wanted more aliasing: Just like a rotating object in a pixelated game creates stairstep patterns at its edges, a rotating object in a voxel game should create those patterns all over its surface. So I needed to build these shapes once and draw them for just one frame.

For basically all of the fall, I spent a day or two every week working out a method of translating a rotated mesh of surface triangles into a solid voxel grid. The core idea is scan conversion, which GPUs do in 2D with every triangle on the screen. But I needed it done in 3D. After weeks of trying and failing to fit the whole problem in my mind at once, I gave up. Instead I learned to build things bottom-up, in this case from 1D to 2D to 3D, with careful planning about what each inner step needed the outer step to set up for it.

A sample shape rasterized in 2D, but not yet in 3D

The voxel rasterization code is long and complex, but it really works. It's only a tiny part of the running time, even compared to the time to create the surface geometry for those voxels. But I'm most proud of realizing a new kind of animation I haven't seen anywhere else. Objects in the world look really interesting in just the way I hoped they would. Success with real-time voxel rasterization is probably the reason I've stuck with this project all year. Actually, this has to be when I decided I was really making this game and not messing around.

When I had the solid grid produced, I could derive renderable surfaces just like I was doing with levels. But now that had to be fast too. I learned a lot about C optimization, in particular, how to inspect the assembler to make better decisions in high-level code. (Did you know there's a big difference between C macros and inline functions when you care which variables get stored in ARM's limited registers?)

The player's ship, rasterized at an arbitrary rotation

The shapes looked cool, but the squares are flat and the lighting is uniform, so they lacked detail. To add detail on a per-voxel basis, I tried out ambient occlusion shadowing. You see it used to great effect in Minecraft, a game I love dearly and another great use case of visual aliasing. Of course to test shadowing I needed more interesting shapes in levels, so I threw together some perlin noise shapes like asteroids and caves, and stratified them with rainbow colors. Ambient occlusion is a winner.

The ship, asteroids, and a perlin noise object with fancy shadowing

Coloring and shadowing the voxels in real time became an interesting problem too, and part of the solution was to have a single palette texture with color values on the rows and shadow values on the columns. This made the pixel shader really cheap, and it had the additional effect of making lighting effects nonlinear. That is, the amount of light on a spot doesn't have to multiply into the color and adjust it relative to pure black. Instead, the white and black points can be anything. I played with that and made a bunch of palettes that are essentially photo-filtered versions of the original. Now I've got a bunch of different looks I can swap out in the game in real time. So by this time the visual style was feeling pretty well developed.

A moody palette with a vignette effect

At some point along the way I created a starfield behind the rest of the level, but I had the idea to draw it as two layers. The blue layer moves just like a background would, but the purple layer lags behind according to how you're rotating. The point of this was to make the controls feel more solid: Unlike a joystick, tilt controls have no spring to bring you back to center. By expressing your instantaneous rotation implicitly as a vector all over your peripheral vision, I thought it would be a strong hint to your sensorimotor system. The rotation controls seem more learnable this way.

The starfield, showing that the player is rolling to the left

The cave structures I built as sample levels were interesting enough that I wanted to fly around inside them, but my controls were built for forward motion in graceful arcs. So I built a new scheme of indoor controls, and the closest relative to it is Descent. If you played Descent, you remember the huge list of controls you'd have to configure. Each direction on each axis was an independent function. And I loved that, but I never quite stopped tripping over my fingers. But on an iPad, steering can work as a three-axis aggregate via the gyroscope, and thrust can work as a three-axis aggregate with one thumb on the screen.

In a cave. Indoor controls actually switch to first-person…

I decided it was time to build indoor levels, and that meant I'd need a level editor, and I've been working toward that goal ever since starting this blog.

App-to-app communication with Bonjour

Last time I wrote about my plans for Degrees's level editor. I wanted the editor to act as a server so the game could be used to preview the level live. So I needed my iOS app and Mac app to communicate and send Cocoa objects over wifi, preferably with no configuration. Bonjour came to mind, and I set to work learning about it and implementing a client-server system.

It works! The level editor can push a new level to the game at will, and in about a second the level appears on the screen. Animation never skips a beat. I'll be able to make minor adjustments to the level and test them out without a huge load cycle. This also works with multiple simultaneous clients.

As a result of all this, I've produced a library of classes that you can use to set up communication between Mac and/or iOS apps with very few steps. You just need to get the files from Github, drop them into your app, and then follow the instructions.

I gave a talk at a local meetup about all of the above. The organizer was kind enough to post a video of the talk. You can also get my Keynote slides.

(If you've watched the talk, you'll notice that I said there was a major flaw in KCSession. That has since been fixed, so go nuts.)

Level editor design

Lately I've been designing the level editor for Degrees, and I'm really enjoying it. I'm inventing a creative workflow and a tool to support it. It's rewarding to think that at the end I'll be able to just sit and make levels with no friction. Here's my idea of a nice level editing workflow:

  1. Concept
    • Design a level on paper
  2. Shape
    • Create the shape of the level
    • Define zones with portal planes
    • Add fog volumes?
    • Test and refine for navigability and performance
  3. Gameplay
    • Add enemies
    • Add items
    • Add scripted things?
    • Test and refine for gameplay and performance
  4. Appearance
    • Add lights
    • Create palette transition triggers
    • Add ambient sounds?
    • Paint the voxels
    • Test and refine for appearance

If you've read the second ECGC post, you'll recall that Valve builds levels by testing as they go, saving artwork for the end. I think the idea was to get the play experience right before you dedicate time to the appearance, because sometimes you need to scrap a whole room or a whole level. That sounds like a great place to start. Besides that, I've learned a lot from using UnrealEd and Scrivener. So I'm planning a four-pane 3D editor with a builder brush and properties for each selectable thing.

When I was younger I would fool around with Unreal Tournament, and there were a lot of things I loved about UnrealEd. But I had no idea what order to proceed in, and I'd have to go back and redo things. That was a lot of unnecessary pain. To clarify the right order, I'm building in a global mode switch for the stages of the editing workflow. You're either working on the shape of the level, or the gameplay within that environment, or the appearance of the environment. These modes really just limit which options are available at any given time, but I think it will pay off, especially if I end up releasing the editor to users.

I also want to support a really fast testing cycle. Since the game loads levels on the fly anyway, and since the editor will run on a Mac and the game runs on different iOS devices, I may as well do it live over the network. I'll make a separate build configuration for the game to act as an editor client. The editor will act as a server for multiple clients, so I can test gameplay and performance on different iPhones and iPads at the same time. After connecting to the editor, the game will poll for changes and refresh itself whenever the level is rebuilt. The game can also send profiling data to the editor, which the editor can show as a buffer of messages that you can filter.

Things learned at ECGC 2012, part two

This is the latter post of a two-part series on what I learned at the East Coast Game Conference on April 25th and 26th. See part one for more.

Bioware keynote — Paul Barnett

Making games that you don't ship doesn't count as making games. Ship something, and then you can talk to game makers as peers.

The golden age

When you're young, you go through a golden age of playing games. You have endless time, patience, and curiosity. You eventually lose all of these things, but during this time you're imprinted with all the reasons you love games. You come to view games made afterward as just rehashing your old favorites.

You have to work with other generations of gamers, and you will have two communication problems:

First, you and they don't obsess about the same games, that is, your golden ages happened at different times or in different cultural contexts. For instance, Paul grew up in the UK where there was no Nintendo. What? Exactly.

Second, throughout your career you'll meet The Power. Sometimes it's a person you need to get through to. Other times it moves through an organization. You need The Power on your side, and they tend to be older than you and from an older golden age. You can't extend your golden age back in time by playing old games, but it's worthwhile to play them just so you can relate to The Power. This opens them up to you because you get it.

Knowing the limitations of your golden age and others' will help you appeal to other people for support.

Love

Game careers are love affairs. They make you stupid, and they go well and then badly seemingly at random. Love makes you put up with turbulence. If you lose the love, get out. But love fuels your desire, and it's desire that you end up mining for creativity. Nothing else in your career matters as much as that.

You have to stay in love with making games and keep your desire up. Even if you fail you can still have a great time, because that's how love works. It's a great way to live.

You also have to be able to filter the information and feedback you get and become hardened to it. Everything that people casually say goes on the web now and can hurt your feelings, whereas in the past it would have just disappeared and not reached you. So get that dirt off your shoulder.

Types of people

People from Generation Y believe they can do absolutely anything, and so they build their skills up on their own and become specialists or commandoes. (Sounds about right. I kind of live for that.) Millennials like to work in teams and do big things together. They need to be built up because they don't make themselves into superheroes. I don't understand this, but at least now I know there's something I need to learn about Kids These Days.

Besides generational differences, there are three types of people. You're either actually fabulous, or you're well-meaning with desire and worried that you're not that great and you sometimes feel like an impostor, or you're smart and lazy. You want to be and work with everyone but the latter kind.

Computational geometry for game programmers — Graham Rhodes, Applied Research Associates

I think it was Red Faction that first allowed players to destroy terrain in a 3D world. Half-Life 2 impressed me with its realistic wood splintering. Spore and Minecraft create the bulk of their geometry procedurally and in response to player actions. The common thread in these games is that the game world can change in response to player actions. They are also unique experiences even the first time you play them, perhaps because their gameplay is harder to replicate.

And that's the whole problem: This is really hard to implement such that it will perform well at runtime. When I started on voxel rasterization, I hardly knew where to begin.

Graham Rhodes presented us with some basic data structures for geometry that let you walk the vertices and edges in a mesh and conveniently modify them by swapping pointers. I won't go into it here, but you can view his slides on Essential Math — Find "Computational Geometry" on that page.

Designing better levels through human survival instincts — Chris Totten, Pie for Breakfast

This talk was my favorite of the whole conference. I have a lot of notes so this will be fairly staccato.

Level design is where your abstract game mechanics are worked into concrete experiences for players on a moment-to-moment scale.

Types of spaces

Chris suggests that every level designer read Origins of Architectural Pleasure, in which Grant Hildebrand writes that our survival instincts create our idea of what a pleasing space is. But consider that in games, you create not just pleasing but also threatening spaces.

Narrow spaces limit your motion and create a sense of vulnerability because you can't avoid things. Zombies can be an "alternative architecture" because by surrounting you they create a narrow space.

Intimate spaces are neither confining nor overly large. You can move freely, but it's not so big that there are places you just can't get to. Intimate spaces are the right size and shape for you to feel like you control what happens to you.

Prospect spaces are wide open areas where you're visible and likely to be attacked. Boss rooms in Mega Man, for instance, are usually just four walls. There is nowhere the boss can't hit you if you hold still.

Neighboring a prospect space you will usually have refuge spaces. These are small areas in which you're comparatively safe. You can look out across the prospect space, find another refuge, and plan how you're going to get to it.

On the antlion beach in Half-Life 2, all the sand is threatening, and all the rock is safe. Prospect and refuge spaces are the foundation of some sneaking games like Metal Gear Solid, as well as cover shooters like Gears of War.

Features of spaces

Height can give the player an advantage and make them feel powerful, or it can threaten them with the prospect of falling. In Half-Life 2, the Combine's architectural theme uses lots of tightly-packed vertical lines. This draws your eye up a wall or down a chasm so that you get a stronger sense of height.

Shadows produce progressively scarier spaces as you move from light to dark areas. Complete shadow builds negative space. When enemies live in shadow, you become afraid of the shadow itself. Shadow can also be a refuge if you're the master of it, as in Splinter Cell.

Shadows can also create a visual rhythm across a space, which can encourage players to move that direction. Players will follow the shadows extending from a set of columns, for instance.

Diffuse shade creates a sense of atmospheric ambiguity. Modern Zelda games do this well: You never know what's going to happen in the next room of the dungeon. It could be an item or an ambush. This builds the player's anticipation.

Etc.

Other good reads for level designers are Christopher Alexander's Pattern Language and Lyndon & Moore's Chambers for a Memory Palace.

Color can give you a sense of danger or safety too. Color is an effective way to build symbols. Then you want to teach the player to read those symbols early on in the game.

Reflective surfaces can help make a narrow space feel like a prospect space.

Vistas are visual rewards. They give you a bit of relief, so long as you're not being hurried through.

Counter-Strike had simple maps with particular places where you could expect action to happen, with a few outlying territories and corridors leading toward them.

A level design sketch is only a sketch. You need to get it into 3D to really make it concrete and form judgements. Prototype soon, and involve sound design early.

Valve designs an experience for the player, implements the shape of a level in the game, tests with just the shape, and then adjusts the shape toward the desired experience. Art is done only after the mechanics are right.

Optimizing AAA Games for mobile using UDK — Aaron Jones, Epic

I'm not using UDK, but I'm building a game engine and a level editor, so there was plenty for me to learn at this talk.

The major bottleneck on iOS is the draw call count. If you have an indoor world with specialized geometry in certain rooms, you can group all of the things in the room into a single object and make it one draw call. You want to make a single call for every part of the level that will be visible at a time.

In UDK on iOS, a good upper limit for scene complexity is 42,000 vertices for everything. I imagine this has to do with the parameter buffer size. To keep detail, you can balance a low polygon count with higher texture resolution, but mind your memory usage.

Level editors create level-building workflows. Performance optimization needs to be possible at the end of level production. I'll want to put a live vertex count on the screen in the 3D preview window, so I can feel out a heuristic for a simple scene, a complex scene, etc. and establish objectives for good performance.

To profile level performance and test it before and after you make changes, create a simple flythrough. I'm reminded of the timedemo command in Quake.

World Building 101: If you want to make an apple pie from scratch, you must first invent the universe — Kevin Human, Digital Roar

Kevin gave us a general rundown of how to create the story for a game. I needed this.

When creating a story, you must express yourself in it. This is the only way to create something genuine and believable.

Games should begin as late in the story as possible and still maintain coherency, and should end early too. You want to be left wanting more.

In games, the gameplay itself is the narrative. The story is dressing that augments the narrative. So the story must not get in the way of the telling. It should be the story fitting into the gameplay and not the other way around.

To make the story simple enough to fit into gameplay, express it in simple terms:

  • The game is about A
  • It takes place at B
  • The main character wants C
  • It starts when D
  • It ends when E

Your story will deepen if there is a real chance that bad will win out over good. You want the audience leaning forward and watching, not leaning back and waiting.

Don't sit and make up a plot, instead, sit and study people. Characters and their motivations can create a plot by bouncing off one another.

Don't put anything anywhere in the game unless it has some meaning to the game world. As in Bioshock, the environment becomes a character.

End

As I wrote in part one, this conference was a lot more valuable to me than I expected it to be. You've got to come out next year.