Introduction


I started creating "Snake on a Sphere" (from here on, SoS) during my spare time for the fun of creating a game freely and for the fun of learning. I also had in mind that once I finished the game, it would be nice to put together all the things I learned in a "post-mortem", basically for my own reference, but I'd be happy if this "post-mortem" helped others as well.

In this document I will explain the key design concepts of the game, the main steps of the development process, and some rendering techniques for developers. If you haven't played the game, and you want to explore it by yourself, I recommend that you play it first before reading this document, since this text is full of "spoilers".

Game design


Main concept

First of all, these are the requirements I had in mind:
  1. Target casual mobile phone gamers. Mainly, because this was a 1-man project, so I couldn't make anything bigger. Plus, thanks to Apple publishing games for portable devices is now easy.
  2. Super simple controls, but in 3D. Since I was targeting casual gamers, I wanted intuitive controls. Just point where you want to go to move the character. Personally, I hate those virtual joysticks used in many mobile games. 2D games are usually easier to control, because you directly move something on the projected plane, so basically what you see and touch is what you get. But since one of my motivations for creating this game was having some programming fun, I really wanted to go 3D to play with the lighting. Then I obviously got inspired by "Super Stardust" on PS3. It was basically the same game I remembered playing on Amiga, but the 2D controls were just mapped onto the surface of a sphere! Of course! And I love spheres, and it sounded like a nice camera programming challenge. So the sphere has been there from the start.
  3. Embed a message within the game. I like deep gaming experiences, specially games that not only they have a story, but that they also have some important message they try to communicate through gaming. This is the case of "Metal Gear Solid" series, where the central theme may be war, but the game encourages you to avoid killing, so it teaches you not that killing is bad, but rather that not-killing is good. Plus, the game emphasizes the horror of nukes, so the ultimate goal is not defeating someone in particular, but getting rid of the nukes. But having worked myself on the "Metal Gear" series for 3 years, I know that creating such game is a cumbersome task that needs a big team and many resources. On the other hand, once the game is finished, many players feels it's tiresome ("mendokusai" would be the appropriate word in Japanese) to start playing it. There's like an invisible barrier that separates gamers from good games, and I think this barrier is in effect inexistent in casual games. So I decided I wanted to create a casual game, that everyone could start playing right away, and short, so people wouldn't worry about having to invest many hours in clearing it, and so I wouldn't need so many resources to develop. However, this game needed to contain a message, anyway. Some people may not realize it, but if the message can get through at least a couple of persons, that would mean that my game has been good for something else rather than just killing free time.

So, always with those 3 points in mind, I thought of borrowing the classic snake game (which seemed right at a time I was also close to another Snake...), and map it onto a sphere. You simply click on the direction that you want to go, and objects can be placed using just two dimensions using spherical coordinates. Any user should be able to quickly understand the mechanics without having to read explanations.

Message

At the beginning, I didn't have a clear idea of the message I could embed in the game, but I wanted to make it a learning experience: if you play the game doing something else of what it's obvious, you'll end up with a different outcome that will naturally become the moral of the game. This is, however, not a very good design because the obvious goal is the only goal that follows naturally with the gameplay. In the end, I had to fix the gameplay to make the non-obvious goal a bit more interesting, but without compromising the original idea (still not completely satisfied...)

As you may have already seen, the user controls a big snake that grows longer every time it eats humans. Because I wanted the snake to be god-like, I designed it to resemble an asian dragon, those with very long bodies. However, I got rid of the legs, the horns, and hair, to make it appear more snake-like. The game is set on planet Earth and it starts with the world full of water. The first humans appear in an island that represents Africa. As the game advances, the water recedes and more continents show up and different types of people appear. You actually don't need to do anything to clear the game: it will be cleared in around 30 minutes, if you don't die first. There's a progress bar on the bottom showing how far you are from the ending. I wanted a continuous experience, so there's no clear divisions between stages, although internally the game is divided in "ages", each age having people and buildings with a bit more modern looks. I also mentioned above that I wanted a short game, so for a continuous experience I decided that more than 30 minutes it could start to get a bit tiring.

If you do not eat a person, eventually the person will build a house and take cover inside it. Every time a person pops in or out, they scream some sentence in the form of text that pops on top of the screen. Apart from this, when they are about to disappear, they blink so you are warned that a building may appear. When you collide with a building, your body shrinks and become a bit shorter. When you collide with yourself, your body becomes even shorter. After collision, the snake flies for a while, in order to avoid continuous collisions when you are trapped in an area full of buildings. You can safely look for a place to land while you are flying.

Buildings have an internal state that tracks the number of times they've been hit. This is hidden to the user because I want the users to find by themselves. So hopefully users will get surprised the first time a building flies away after being hit a couple of times. A few buildings contain some hidden items, and there are a few of other gimmicks around the end, like airplanes (internally, they are just moving "buildings"), or some weirdo hidden inside a cardboard box...

The game contain two main messages. The first one becomes obvious once you clear the game a couple of times: "virtue is the mean between two vicious extremes" (Aristotle). Both eating too many people, or going on a diet during the whole game, will lead to "bad" outcomes. The "good" ending will be unlocked when you keep a balanced "diet". I put "good" and "bad" in quotations because I do not want to imply that they are the correct or the wrong things to do. Every different outcome unlocks a different game mode, so there's a reward for any type of play. I'm just trying to say that the world is not Manichaean, so what's good and what's bad can be sometimes fuzzy.

The second message involves the concept of Gaia and the use and abuse of resources. It's related to the first message, so it just means that it's important for survival that we use the correct amount of resources: do not overuse them, but do not perish either trying to preserve every living thing on the planet.

There are 3 hidden manuscripts inside some buildings. If you find them, you can read 3 different short stories, each explaining one of the 3 possible outcomes.

No goal

When I talked about the learning experience, I tried to explain that I want the user to explore freely the game. There's no much to do within the game, other than eating and crashing, but these 2 actions can be exploited in different ways. I explain that continuously crashing on buildings will help you to destroy them, for instance. Also, continuously eating people triggers combos, and accumulating combos lead to unlock the Earthquake weapon. When you press the Earthquake button, the energy of the surrounding buildings will decrease. I do not explain these mechanics anywhere on purpose. I believe that, up to some point, discovering these things by yourself makes a game fun (even if I didn't quite manage to get it right, or even if some players are happier with extensive tutorials for dummies).

So the goal is not clear from the beginning, and even after you clear the game, you may feel some void. This is actually part of message of fuzziness I mentioned above: your goal in life isn't clear either, but you have to grow and survive.

But I promise you that If I have the time to create another experimental game like this, I'll try to satisfy those people who complained about not understanding the goal, while keeping this type of fuzzy game philosophy that it's always in my mind.

In any case, there are many small goals that are defined through the Game Center achievements. Again, users have to actively explore the Game Center by themselves, and see what kind of achievements and rankings are there. You can unlock 3 endless modes, where the sole purpose is to get a higher score in the rankings.

No pause

The continuous play I mentioned above is one of the reasons why I decided not to put a pause button in the game (feature that some friends asked me to include). If you have an incoming call, of course, the game will automatically pause, but other than that, you have to press the home button to interrupt the game. When you come back, you can resume the game.

The lack of a pause button is also part of the game play. I believe the game is easy enough for everyone to clear it without much effort. You can crash many times, and still survive as long as your body is long enough (your body shrinks with every crash). Having a handy pause button whenever you feel tired during the 30 minutes of the main gameplay will make the game even easier. In order to pause with the home button, on the other hand, requires that you move the snake to a safer location, in order to prevent a possible crash during the time it takes to press the button, since the snake keeps moving by itself. So, yes, pausing the game with the home button is a bit inconvenient, and it's supposed to be like this.

Also, I tried to keep the game screen as much free of UI as possible, so a pause button would disrupt this minimalistic design.

About the name

The first name I thought of was "Spherical Snake", because it was the Snake game, but using Spherical Coordinates. Maybe simple, but the snake itself is not "spherical". I also thought of some funky names, but making the concept obvious in the title I thought it could be a good thing. "Snake on a Sphere" seemed obvious enough, plus I thought it was funny as it reminded me of the infamous movie title, "Snakes on a plane" (I was thinking of "plane" as in "2D plane" here, instead of airplane 😁).

Unfortunately, "Snake on a Sphere" is too long for the icon name, and I didn't like it getting cut. So I thought of the abbreviation "SoS". I like it because the Ss remind me of snakes, and because the people in the game cry for help, SOS (· · · – – – · · ·). But then again, "SoS" is too short, and the name is already taken anyway, so I ended up with this weird combination of name and abbreviation, "Snake o.S.". This is the name used for the icon, and in the Game Center, but in the game I still use "SoS: Snake on a Sphere".

Monetizing

Some people asked me to make this game free. Even though this is a pet project, I thought that I should try to get some revenue in order to understand also the business side of making games. Trying to get some revenue from a free game means that your game has some kind of in-app purchases, or that you use the game to promote some other product. In my case, there's no other product, and I'm not an artist who can make new assets to sell within the game from time to time. I am a programmer, so the only thing I can sell is the program. And that's what I decided to do: sell the game. And personally, I still like this old style of selling a game: you pay once, and you know there's no hidden surprises later.

But most important, making users pay for it was important for the concept of the game. The goal of the game is not clear, and it requires that the user explores the game a bit to find the small surprises that make it interesting. If you pay for a game, you may expend some time checking it out, since you want to make the most of your dollar. But if the game is free, I assume people that do not understand it in one minute may as well be quick to erase it.

Art & Sound


I created all the sounds and graphics of the game, both the 2D and the 3D art. Apart from the fun of making everything freely, I thought it would be good for me in later projects in order to estimate how much of the project and budget should go to 2D art, how much to 3D art, and how much to sound.

Graphics

I like drawing, and even I knew is not going to look professional with my style, I thought it could be funny to have this hand-made feeling to the game. And recently I've seen many games that have that kind of hand-made looks. So the 2D art was quite fun most of the time. I even bought a very cheap pen tablet to color my drawings with GIMP.

The 3D graphics were a bit more challenging, because last time I used a 3D modeler was back in the Amiga days, the last Lightwave Pro release on Amiga. But I thought it would be a good exercise to try to understand the pain and the needs of 3D artists. As a graphics programmer, I've been working closely with artists in order to try to answer to their needs, while trying to keep the program within the performance budget. But I felt I needed a deeper understanding of what they do.

However, I can't make everything by myself without sacrificing quality. The first thing I decided I would kill was animation. Rigging would take a lot of time for me to learn, and implementing it later in the game engine would require also an extra effort. It turns out it was a good decision, because when I was profiling the game on iPhone5 I found that my main bottleneck was sending matrices to the GPU. If I had also the rigging matrices to send, the game wouldn't probably run at 60 FPS on iPhone5.

Because of the lack of animation, I decided to design the human characters as inverted cones and without arms. I think that with this kind of stylized design, characters don't look too awkward when they rotate or start running around without any animation. And when the snake eats someone or it crashes into some building, I decided to hide the head of the snake behind a big icon so the lack of animation doesn't look that unnatural.

All the models have been created using Blender. Basically because it is free, and because I had an exporter script to create static header files for my project. The only problem is that the export generates too many vertices. I would like to export to indexed arrays in the future, but right now the GPU can still handle it. Still, some of the models are created programmatically: the body of the snake, the planet, and the background.

In order to paint the models, I use Blender to get some rough result, and then I clean up the texture using GIMP. In the end, all the textures are put in a giant texture atlas. In order to increase performance, I've tried to avoid texture changes as much as possible.

Since the camera follows the head of the snake, the head is always seen from above. So I made sure the eyes were always visible from up, but if you actually see the snake from one side, it looks quite funny, with the eyes looking up. These are some screen captures of the models in Blender while I was making them:

Sound

3 years ago I started going to piano lessons. I have never had musical training whatsoever, so I still struggle to read scores, but I thought it could be fun to try to create some simple rhythms with my digital piano and then switch between those rhythms within the game regarding the state of the snake or the game. For the sound effects, I tried different sounds, but in the end the piano was also the best match for the overall melody. I've mixed everything using GarageBand, and finally I've also used other instruments apart from the piano, to give some different color at some points. For instance, I think the electric guitar matches better the earthquake effect, or the synthesizer sounds I used in the intro help with the dreamy feeling I wanted to express in this "limbo" state, where you can move the snake, but you are not really playing.

I feel the sound is noisy at times, so I decided to add the option to turn it off (the piano sound used as a sound effect when eating someone can't be disabled, unless you put the phone in manner mode). However, I've received some positive feedback so far so I guess it was not as bad as I expected.

Game engine


I wrote all the code mostly in C++ and with some Objective-C, all within Xcode. I actually started this project a couple of years ago, but it was just a simple Xcode project with some cubes moved using OpenGL ES 1.0. Here are some screenshots of the early development,

Around that time Unity was not as big as it is now, and you had to pay to get iPhone support. If I had started the project right now, probably I would have started with Unity, but there's also many advantages to create everything from scratch:

  1. Memory usage. I am a bit paranoid about memory and Garbage Collection. I actually have a bad experience with some unexpected behavior in the current Mono version of Unity. I gave a talk about this in Unite Japan 2013. So having control of the memory allocations is a good thing if you know what you are doing. Plus being able to use the Leak Detection within Instruments is also very useful. If you try to debug some Unity game with Instruments, Mono appears to be leaking all the time (it is not, it's the GC), and you have to debug within Unity, which is not always easy when you are not creating Unity objects.
  2. Disk usage. The iOS binary for distribution of an empty Unity project takes around 14MB of space, while my whole game is just 11MB, including those huge iPad splash screens that are bundled with it. For a small casual game like this, I want to keep it as small as possible.
  3. Raw fun. It's fun to program all the math functions and rendering engine by yourself. I wanted to practice some low-level stuff and it's very good to be in control of the whole program when you are analyzing performance with Instruments or through frame capture. The frame capture feature in Xcode is better than the capture tools I have seen so far in consoles, since in Xcode you can even jump to the line of code that generated a given OpenGL command, or modify shaders on the fly and see the changes. Debugging a Unity game with Xcode's frame capture is also useful, but then you are not in control of the actual graphic commands, so it's hard to translate what you are seeing into game code.
And the main disadvantages are, of course,
  1. Long development process, since I needed to create everything from scratch. Also, I worked on this only sporadically, so I kept forgetting what I was doing... I took the game seriously from September 2012, when I decided to use virtually all my weekends to work on this. I was actually motivated after watching a WWDC 2012 video where they showed the frame capture feature in Xcode 4.1. I bought a dev license, tried it, and loved it. So the hard work started by porting my old project (many months without touching it) to OpenGL ES 2.0, which meant creating some basic shaders at the beginning... Of course with time it got fun creating my own shaders, but the start is a bit painful.
  2. No stage editor. Unity is very good for debugging your stage, modifying it on the fly, and exploring your scene easily. For this project I had to create some basic tools and scripts to place the objects on stage (the locations where humans appear are actually fixed).

If I keep comparing Unity development versus raw Xcode development I could of course complain about the Mono Develop editor, that keeps crashing on Mac, and the step debugger, that makes both Unity and MonoDevelop to crash. I haven't encountered such problems in Xcode, and the debugger is very good. The only problem I had with Xcode is that at some point GDB started ignoring my breakpoints, and I had to switch to LLDB since then...

Math library

One of the first things I created was a small math library, with simple Vector operations. I also created a special 3D vector that holds spherical coordinates, and that I use to easily convert back between cartesian and spherical.

Since I knew all the free camera rotations around a sphere would be a nightmare using matrices, I also created a quaternion class first. I only added matrices once I started passing matrices to OpenGL (at first I was working with a fixed view).

Later on in the development, I added optimized vector operations using DSP, when I started testing the performance on the device.

I open-sourced part of this library as part of another project. You can see the classes here: github endavid Harmoniker/math

Everything is a C++ intrusive list

My idea was to keep everything in the program in lists that could be looped quickly, so I created a C++ template for sorted double-linked intrusive lists, and virtually every object in my game inherits from that template. By keeping it double-linked, inserting and removing objects in order is a bit more convenient. And the links are intrusive so looping through them is cheap. There are basically 4 main types of lists:
  • Game object lists, from the main game logic. This would be the main game scene.
  • Simulation lists, from the kinematics engine.
  • UI lists, from the UI engine. Basically, clickable buttons, labels, and scrollable text.
  • Render objects, from the rendering engine. 3D primitives, models, 2D primitives, and text. There are some special objects as well, like the body of the snake which is also a dynamic list.
  • Render plugins, from the rendering engine. I use the sorting index to determine the priority in which the plugins are executed. Each plugin contains a list of render objects.

Rendering: lighting

As a graphics programmer, this was where I expected more fun. I had quite clear from the beginning that I wanted all diffuse ambient lighting. I wanted a realistic "toy'ish" look, without shadows. Shadows are expensive to compute, and they look ugly in the end. I'd rather expend my time trying to get some kind of ambient occlusion working, which in my opinion would give things a stronger attachment to the ground than directional shadows.

So in order to create a realistic ambient light, I decided to use Spherical Harmonics (everything is spherical in this game!) to encode the diffuse irradiance. My implementation is based in two famous papers, Green's "Spherical Harmonic Lighting: The Gritty Details" (SONY) and Ramamoorthi's An Efficient Representation for Irradiance Environment Maps. The dreamy and vivid appearance of light in the game is because I used colorful photographs as reference for the environment maps.

I created a tool to compute Spherical Harmonics from a couple of pictures. I open-sourced it in github: github.com/endavid/Harmoniker. There's a screenshot below. You just need to drag&drop two images, one per each hemisphere, and it maps them onto a sphere assuming a simple 2D spherical mapping. The application so far accepts only LDR pictures, and I achieve a simple HDR effect by linearly scaling some of the environment maps programmatically.

I didn't want to fill the space with light probes, but at least the two sides of the planet needed to be different. So I represented all the ambient light with 2 light probes: one for the hemisphere perpendicular to the sun's direction; and another for the opposite side, the dark side of the planet. The lower part of each light probe is the indirect light coming from the ground, while the upper part of each probe is the skylight. The light probe on the side of the sun is multiplied by some factor that depends on the time of the day, and I interpolate between the dark and the bright probes as the user moves around the planet.

As time advances, different pairs of probes are interpolated, so the sky and the illumination changes smoothly. The sky is just a blurred version of the set of pictures that I used for computing the ambient light. The direction of the sun also changes with time, and it basically orbits around the equator.

Rendering: Ambient Occlusion

In the previous section I have explained that I wanted all my lighting to be diffuse, and that I didn't want to render any shadows except for the ambient occlusion. Computing Screen-Space Ambient Occlusion (SSAO) is costly for mobile devices, but since I am not rendering any shadows, I thought I could afford it.

As it turns out, all the algorithms I've tried were quite heavy on the fragment shader, so in the end I created a very simple solution that sometimes, instead of ambient occlusion, looks more like brush strokes around the character, but I think it looks good to emphasize the toy'ish looks of the game.

The algorithm is very simple:

  1. Render depth in an early pass. A very simple shader is used to render all the models in a very small buffer.
    • The width of this buffer is fixed (the height changes with aspect) --this is important so the width of the lines created by the SSAO looks the same in every screen.
    • The depth is stored in a RGBA color texture, and I had to encode it linearly in order to be able to apply convolution later, that is, depth can be reconstructed just by using a weighed sum. So I can encode at most 1024 depth values in the texture (8-bit per each channel).
    • The near and far planes for encoding depth are set regarding the distance from the camera to the planet.
  2. Blur the depth texture. Use 2 passes, for X and Y directions, and sample between pixels, so thanks to bilinear filtering we can average more pixels at once. This is the reason why we need a linear encoding of depth.
  3. When rendering the scene, sample the blurred depth and simply compute occlusion as the difference of the real depth of a given pixel and the blurred depth value stored in the depth texture.

For the sky shader, the occlusion value is used in conjunction with a uniform color to compute the halo around the planet.

Ambient occlusion should not appear in far away objects, but because I am simply computing the different, the farther away 2 objects are, the stronger the shadows this algorithm generates. That's the reason why, when the snakes flies around the planet, it looks more like brush strokes than shadows. But because I am not projecting shadows, I am happy quite happy with this. Because the planet is round, it's is hard to understand the important depth cue that this gives, but here's a screenshot with a "broken" planet (the sphere vertices are scrambled), and I think you can understand well where each face is thanks to the SSAO effect,