The Global Game Jam theme for 2012 was this image of the ouroborus:
Since I was helping to coordinate the Cleveland site for the Jam, I didn't want to saddle another group with a member who was potentially unavailable while they were doing administrative tasks during the weekend. As a result I ran with a team of one.
The theme image immediately conjured in my mind the idea of autophagy- a sort of ceremonial consuming of the self. During the Cleveland site's cross-group brainstorming we also saw a lot of motifs related to bleakness, futile repetition, Sisyphean tasks, and so on. I had also tinkered with some procedurally generated looping track structures in 2011 and wanted to revisit and formalize them.
Building the Level
The first crystal of the idea was the track itself- the player would race along an endlessly repeating loop of snake spine. To make it more interesting, instead of a simple circle this would be in the shape of a trefoil, the simplest nontrivial torus knot. It's easy to add complexity to torus knots just by changing the parameters used to generated them but I wanted the track to be simple enough that the structure felt identifiably straightforward as the place caught glimpses of other parts of the track.
I knew I wanted the gameplay to focus on consuming some kind of resource as you raced along the loop, changing the course as you went and making each lap harder than the last- but I also knew there'd be no way to really gauge the fun level until the basic course and movement system was set up, so I focused on laying down the technical foundation for the game.
The first step was to build a rib joint in Blender and duplicate it along the trefoil. The big challenges there were the intrinsic problem of orienting and evenly spacing what were essentially two dimensional objects (the ribcage joints) along a one dimensional structure (the path I built for them) in three dimensional space. This is a nontrivial problem to solve, and we'll be releasing a separate series of articles on the techniques shortly.
Once the ribs were built I made something which would be argued looked like a snake skull by someone who had never seen a skull or a snake. Just we the ribcage is centered around an invisible trefoil path, the skull was parented to an object which also followed the path- this mean that forward movement was a matter of tracking the parent object forward along the curve, and left and right steering just meant rotating that object about its z axis. Hurray for rail games- they have the easiest physics.
Tying in Mechanics
A this stage I had a really nice screensaver; it was time for interactivity. Initially I had intended to set the game inside the loop but that made visibility (and by extension, steering) an issue. I had also come to the conclusion that the obstacles and resources in the game would be scales, which are not typically found inside of a snake. While you could see scales covering the outside of the ribcage from inside, they were obscured by the bones and we facing the wrong way for good lighting, and you really had to look for them. The player was moved to the outside.
Now the player could race along the back of the snake. They had to avoid thorny horns which would cause injury or death. The scales, which were the only safe place to travel, weakened and detached as the player passed over them. Once detached, they would fall to the ground and the next time the player came by they would see the stretch of empty spine that was no longer safe to travel on. This presented one significant problem- they were totally unnoticeable.
Since the scales fell after you passed them, you didn't see them break off. Since you were looping around, the "down" of gravity was effectively looping around too, and sometimes you'd see a stream of scales out of nowhere as you happened to pass below where you had just been. The track was long enough that when you started your second lap it didn't feel like the missing scales were something you had done; they felt like a gradual change in the level.
The gameplay hinged on the awareness that you were destroying the ground your future self would walk on for the sake of your present self, and these factors created a disconnect between the player and the scales falling was preventing that awareness from sinking in.
The first step of the solution was to eliminate the scales falling off the track; since the player wasn't seeing it happen it effectively wasn't happening at all. I changed it so that scales near you (and, crucially, just in front of you)would be visibly sucked into the cavity of the snake skull, spinning as they went and shrinking down to nothing. This made it clear that the player was actively consuming the track since they could see it happen.
I had liked the falling scales mechanic when you could see it, so I decided to add two cameras which would share the screen with the main camera- one showing the scales falling after they had been consumed, and the other showing the growing pile as they landed. At the same time I retooled the game so that your score was determined by the number of scales you consumed. A fourth camera to display the score. Tied the consumption of scales (and by extension, the player's actions) to the end result of the game- their score.
These changes made the gameplay much more immediate and compelling; now the claustrophobic nature of the game became apparent; as you looped back through the track you eventually had to decide to veer off the remaining course in order to avoid the thorns and their massive negative point values. A game over was inevitable, and it felt connected to the cycle of play.
Sound and Music
The sound effects are very minimal- a handful of dry, hollow clinks to be played randomly when a scale was consumed, another for when the player hits a horn, and a music track which was a basic rhythm (also in dry hollow sounds) with some simple synth layered it. To round out the dry/hollow sound theme, I adjusted the lighting and camera effects to give everything a washed out, sun-bleached look.
This game is fairly performance intensive- mostly because there are a LOT of scales to draw on the screen at a given point of time; they also corresponded to a lot of physics calculation happening when they fell and landed in a pile. The draw call issue became especially apparent after I introduced the camera focused on their landing zone; as the player ran the race, scales came in and out of view on the main camera; at most only a small portion of them were in shot. But every scale that was consumed ended up on the landed zone camera, and its physics were running incessantly since new scales were always falling on top, which prevented the rigidbody from going to sleep.
To solve the physics issue, I did some trial and error and determined that removing the rigidbody component (which is what drives physics in Unity) ten seconds after a scale was consumed gave it enough time to fall, bounce, come to a stop, and then be more or less ok not moving again.
The draw call issue was a tougher nut to crack; what I settled on was batching the scales into groups as their rigidbodies were removed. Once a group hit 75 scales I would run the CombineChildren script on them to replace their individual meshes with a single mesh- a reduction in many hundred draw calls per frame. The problem I ran into when I first tried this was that the scales had randomly assigned colors on the track. These different colors meant that as far as the CombineChildren routine was concerned they had different materials and therefore needed different "combined" meshes- so instead of turning 75 meshes of one scale each into one combined mesh of 75 scales, I was turning them into 75 combined meshes of one scale. In short I a paying the upfront cost of the CombineChildren utility (which isn't itself cheap to run) and getting no performance benefits at all- a net loss in framerate.
I fixed this problem with special effects. Now the scales would be "drained" of color as they were sucked into the skull vortex; once that was done it was easy to replace their material with a flat gray one, to highlight their lifelessness. For aesthetic reasons I let the red hued horns keep their color to make them stand out on the pile.
Since the scales had the same material now the CombineChildren script worked and the performance problems largely subsided.
What Didn't Work
The biggest hurdle I faced was that when the game reached its first playable state it wasn't interesting. Play felt too divorced from the story and mechanical implications. At the same time, the ramp to get to that stage had taken a while because I was still splitting time helping with the Cleveland site and had to develop some techniques to smooth out the ribs on the course (I'll go into far greater detail about these issues in the upcoming series of parametric curves).
I also feel like I missed out on a lot of the fun of the jam by going solo, even though I realistically think that the alternative had been to not make a game at all.
On a technical note, this was also the first game I'd even developed on my MacBook and not my iMac- this meant that testing other resolutions was very difficult (the MacBook's screen is less than a quarter the area of the iMac). The downside there is that at some (unfortunately common) resolutions some of the secondary cameras aren't focused on the correct things.
This was a great case study for me in the power of the game camera as a tool for pushing the experience. I'd worked with secondary cameras before (System Protocol One uses one to generate the blurred background), but I had only used them for the technical purposes- rendering GUIs, tying image effects to some layers and not others- that sort of thing. This was the first time I had used multiple cameras in a directorial way. The three secondary cameras (well, the secondary through tertiary cameras, I suppose) work to connect the dots between the player's actions (steering left or right), their immediate mechanical consequences (consuming scales, hitting a thorns, going hungry) and their end results (points, more and more scales piling up in a nameless gray expanse).
This was an eye opener, and something I want to explore more in further games.
You can play Ossiphidia (or download the source) here. I apologize for some of the sloppy code- time constraints being what they were I didn't have time to go and really clean it up. If you have questions please feel free to ask them here.