This article is part of our Android at Play: Android Game App Contest and has different requirements and requires less strict guidelines.
This article covers developing 2D cross-platform game for Windows, Android X86 and Android ARM.
Some time ago I saw the game Dwarf Fortress and I liked it very much. I wanted to make a similar game, which will have a world with wide opportunities, where dwarves will build a strong fortress and organize their settlement with the development of production.
Game "Diggers" is a 2D-platformer. Player controls a few dwarves to build strength and production. When you start the game, world is randomly generated.
Picture 1. General overview.
The world consists of chunks, each chunk has a size of 64x64 block. Chunk is one of several biomes listed in the file boimes.json: "BiomePlain.json", "BiomeDesert.json", "BiomeHill.json", "BiomeShallowMetalls.json", "BiomeLake.json", "BiomeCave.json"
Each biome consists of certain mineral blocks which are listed in the corresponding JSON-file biome.
Dwarfs have mood and needs, which are shown in the thinking bubble, for example idle dwarf falls sad mood.
Picture 2. Dwarf.
Production and enemies in the game is not over yet.
The game running on Windows, Android ARM (tested on HTC EVO 3D and Samsung Galaxy SII) and Android x86 (tested on emulator). Building for Windows uses Visual Studio 2010 and the .sln files, building for Android used .mk files with the desired targets:
APP_ABI: = x86 for x86,
APP_ABI: = armeabi armeabi-v7a for ARM
The game using SDL library and many other dependencies - SDL_ttf and libFreetype for rendering text, SDL_Mixer and libTremor for sound, libJPEG and libPNG for rendering images, JSONCPP to use JSON-files. Thanks God, all dependencies builds successfully on all platforms - Windows, Android ARM and Android x86.
Picture 2. Diggers run in the emulator Android X86.
Let's look at some interesting technical points.
The game uses OPEN GLES 2.0 for rendering.
One of the things I've come across - is optimized rendering. Simultaneously on the screen can be up to 200,000 mineral blocks or even more, they must be quickly drawn. If we will use glDrawElements for draw each block, the drawing will be very slow.
Picture 3. Reveale mode on, many mineral blocks rendered, some caves shown.
For fast rendering the same blocks merges together in batches (class
SpriteBlockBatch). Each batch consists of the blocks of the same type - for example, for all blocks of the soil rendering creates a single batch, for all blocks of limestone rendering creates another batch etc. In addition, since the number of elements in the call glDrawElements in the OPEN GLES 2.0 is limited to unsigned short, then we have to break batch into several smaller internal batches. Rendering can be divided into two phases - the creation of batches and dispay batches.
When the batch has changed, we need to re-create it. For example, when a dwarf dig soil block, there are three things happend: soil block must be deleted, background soil block must be created and mined soil block must be created too. In this case, we need to re-create three butches - batch of soil, batch of background soil and batch of mined soil.
Batches created in the method
void SpriteBlockBatch :: MakeBatch (void)
In this method, we run through all mineral blocks of the desired type (eg all blocks of the soil) and create a soil blocks batch.
Batches displayed in method
bool Render :: Update (float _dt)
In this method, we call update method for each batch:
bool SpriteBlockBatch::Update(float _dt)
There is a smooth change of the time of day and lighting. In addition, the game has a light sources - such as lightning during the rain or glowing plants.
Picture 4. Night time, you can see stars and glowing plants
Lighting is realized mainly in the shader
fragmentShaderMineralsText. This complex shader that has a lot of input parameters - the time of day, ambient light (eg lightning), the depth of the mineral, backgound or foreground mineral type.
const char * fragmentShaderMineralsText =
# Ifndef WIN32
"Precision lowp float; \ n"
"Varying float vDeleted; \ n"
"Varying vec3 vDeep; \ n"
"Varying vec3 vDay; \ n"
"Varying vec3 vLighting; \ n"
"Varying vec2 vTC; \ n"
"Uniform sampler2D s_texture; \ n"
"Void main () \ n"
"Float alpha = texture2D (s_texture, vTC). A; \ n"
"Float deep = 1.0; \ n"
"If ((vDeep.x + vDeep.y + vDeep.z) <0.001) deep = 0.0; \ n"
"Float red = texture2D (s_texture, vTC). R * (vDay.x * deep * (vDeep.x - vTC.y * 0.8) + vLighting.x) * vDeleted; \ n"
"Float green = texture2D (s_texture, vTC). G * (vDay.y * deep * (vDeep.y - vTC.y * 0.8) + vLighting.y) * vDeleted; \ n"
"Float blue = texture2D (s_texture, vTC). B * (vDay.z * deep * (vDeep.z - vTC.y * 0.8) + vLighting.z) * vDeleted; \ n"
"Gl_FragColor = vec4 (red, green, blue, alpha); \ n"
"} \ N";
Also there are
fragmentShaderStarsText shader for stars rendering in the night time and
fragmentShaderSurfaceObjectsText shader for rendering surface objects that do not depends on the deep.
In the game, we need interaction between Java and native code, for example to save and load the game. This interaction is happened in the files JavaBridge.h and JavaBridge.cpp. For example, lets look to saving the game.
Native-method ( in the file JavaBridge.cpp)
unsigned int JavaBridge :: WriteSave (const char * _buffer, unsigned int _size)
call this Java-method (in the file SDLActivity.java)
public static void writeSave (byte  _buffer, int _size)
It looks a little complicated, but if you take a closer look at the source code, all will become clear.
3-d party content
For this game, i used some resources with CC license:
music from http://www.jamendo.com/en/artist/355362/marc-teichert
and dwarf model from http://opengameart.org/content/dwarf-fixed