Saturday, April 18, 2015

Dev.Log - Dungeon 3D Game - Tools Part 1


Recently, I have spent a lot of efforts trying to squeeze as much as possible in the tiny space of the Pebble Watch.
I now have a solid base that I can build content on.

To reach this point, I was only dealing with data in-lined directly in code as static arrays.
Convenient for prototyping, not so much for starting to produce interesting content....

This is what drive the need for tools. In the rest of article, I'm going through the various tools I have developed and used for this project.

Texture Tool - first generation

The first tool developed for this project was to generate textures as C static arrays.
I made a little C# application that takes a PNG file and convert it into a static array that I could paste into my code.

From a tech point of view, really nothing fancy or complicated but this saved a lot of effort importing and testing textures in game.

The tool loads a PNG in memory, goes through the pixels, convert them in grey scale and output the values in text file.

The grey scale conversion is quite simple and is done by weight-summing the RGB values:
grey = (0.21 * red) + (0.72 * green) + (0.07 * blue)
The weights are based on the human eye sensitivity to luminosity of each RGB channel.

So an image like this:



Would generates this code:

static uint8_t stone_texels[8*8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x80, 0x00, 0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x40, 0x80, 0x40, 0x00, 0x40, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x80, 0xFF, 0x80, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x40, 0x00, 0x80, 0x80, 0x40, 0x00, 0x40, 0x80,};

This already saved me quite a lot of time, especially when creating a texture for the "Beholder-like" monster ( please do not judge my pixel coder art :) )




For the time invested in writing the tool (an hour or so), the return on investment has been great. This allowed me to have decent textures to prototype with so that I could focus on developing the features.

But I reached a point where I need to produce a larger amount of content in a more data driven way.


Level Tool - first generation

For levels are also used static arrays first. But no tool to generate those. All hand-typed...

Works great but they are increasing the size of the executable. 
And most of the time, I don't need all this data in memory at the same time. 

So far, I had only one level. As soon as I implemented the next-level-load feature, I needed a way to keep only one level in memory and load it on-demand from the application resources.

This necessity forced me to go write a tool to generate those levels in a binary format compatible with the resource API.

First, I decided to have each of my level as a JSON file. Having human readable and editable format is an important point to me as it helps me to iterate on the source data without having to rely on an external tools/program to generate this file.

To convert from JSON to binary format, I picked up javascript. Why?
  • It parses JSON natively
  • It easy to write
  • There's a large set of available libraries for it
In an couple of hours (including a couple of trial and error cycles), I had my script converting my JSON levels in a binary format that I can add to my Pebble App resources.

(For the record, I do use NodeJS to execute my scripts)

I have now  a mini data "pipeline" that I can use to feed content to my application without affecting its size.

Texture Tool - second generation

With a tool for level, I needed now one to store textures in the application resources.
But ... why not using the SDK provided support for PNG images?

Well, I'm not using GBitmap. I'm using my own format. With my own format, I'm in control of the size, packing and performance... 

In a similar way to the level tool, the texture tool is made with JavaScript and run with NodeJS.

It takes a PNG image and extract the pixel and color information and transform it into my proprietary binary format ready for the application to process once loaded from the application resources.


Next steps


With this set of tools, I have a robust way to channel the content data in the application resources in an optimized way.

Tools are investment, you invest time in them, and you get returned time from using them.
(This can be measured as ROI, how many hours of work are you going to save by spending an hour writing a tool)

From the few hours I spent writing those tools, they certainly saved me my countless of hand editing, byte level tinkering headaches.

So what are the next steps now? 
Well, the level editing is still done in a JSON file. It's simple enough but if I add more features in the future that requires more complex level data, I may need to develop a more friendly "level editor".

on this note, and talking from experience, writing proper level editor can quickly become a time consuming task that take you away from the main feature development.

Conclusion

I hope you enjoyed this article as much as I enjoyed writing it :)

I'm curious about what kind of tools Pebble Watch Game makers are using. 
Please leave a comment to share your experience and give some feedback about it.


You can find the second part of this post here.

Wednesday, April 15, 2015

Dev.Log - Dungeon 3D Game - Source code & Bulk builds

I finally spent found some spare time this week to make some good progress on my Pebble Watch 3D dungeon game.

This article will be about how to keep your code base organized while getting the most of the compiler for the small amount of memory the watch provides.


Source code layout

I may not have mentioned  but, I'm using CloudPebble to develop on Pebble.
This is a fantastic development tool for few reasons:


  • First of all, it makes my code accessible from any device. Knowing that I'm coding on 3 different PC/Laptop and occasionally on a tablet, this makes my life a lot easier.
  • Secondly, it's platform independent. The above hardware list I'm using to code, features Windows, Ubuntu and Android operating systems. Cloud Pebble has no trouble there :)
  • It supports multiple source files projects
I know the last one may sound obvious for any dev IDE (even web-based) but a lot of Pebble Watch projects I've seen are still using one big monolithic file.

There's few reasons for that:
  • Editing preferences: you can find everything in one place without having to switch from one file to the other and every piece of code can be accessed by any other one.
  • Compiler friendly: reduced link time and the ability for the compiler to be able to globally optimize the final executable.
The second argument is, in my opinion, the most valid. Especially in a resource-constrained platform like the Pebble Watch.
And i measured it: with the compiler setup to optimizing for size, one big file results in a smaller executable than having the exact same code spread in multiple source files.

But ... I like organizing my code in multiple files (.h and .c):
  • First, because of my long API developer experience. I write libraries of functions that I can reuse in future programs so it makes each iterations faster.
  • Second, because I want to find quickly where the monsters related code is: in monsters.c :)
  • Third (related also to the API point), I want my header files to be the public side of each libraries and the .c file to have all the internal business hidden from outside.

There's a simple way to get the benefits of both worlds. It's used professionally on large projects (and particularly in professional video game development): bulk builds.


Bulk builds

The concept is rather simple:
  • Spread your source code in multiple source files
  • Exclude them from compiling
  • Have a "bulk.c" file that includes all the above source files and compile only this one.
CloudPebble doesn't give you the ability to exclude files from the build. To go around this you could use a different (non compilable) extension for your files. For example .inc files.
Since I already had all my .c files in place, I decided to use the pre-processor to help me. Each file is shaped this way:

#include "config.h"#ifdef BULK_BUILD

// source code 
#endif // BULK_BUILD

Then the bulk.c file looks like:


#include "config.h"#ifndef BULK_BUILD#define BULK_BUILD
#include<a.c>#include<a.c>// more includes
#endif // !BULK_BUILD

Now I can easily switch from bulk builds to non ones.
By default they are on. If I want to compile normally, I simply add #define BULK_BUILD in config.h file and now all the individual files are compiled separately.

This solution makes it easy to port any project to support bulk builds without having to deal with compiler settings.


Results

On my dungeon 3D game project this allow me to gain a fair amount of free space to add more features and data.
Here's the comparison on both generation of the watch ( using SDK 3.0 ).
  • Regular build
-------------------------------------------------------
APLITE APP MEMORY USAGE
Total footprint in RAM: 24016 bytes / 24KB
Free RAM available (heap): 560 bytes
-------------------------------------------------------
BASALT APP MEMORY USAGE
Total footprint in RAM: 43572 bytes / 64KB
Free RAM available (heap): 21964 bytes
-------------------------------------------------------
  • Bulk builds
-------------------------------------------------------
APLITE APP MEMORY USAGE
Total footprint in RAM: 23672 bytes / 24KB
Free RAM available (heap): 904 bytes
-------------------------------------------------------
BASALT APP MEMORY USAGE
Total footprint in RAM: 42916 bytes / 64KB
Free RAM available (heap): 22620 bytes
------------------------------------------------------
If we look at the size of the executable, the bulk build version is only a few percent smaller than the regular build. 
This may not seem significant, but in that case this translates to 344 and 656 bytes of extra space that can be used to add more code or content. 
And believe me, once you reach the limit of the device, every byte counts.

On this note, I will certainly talk more about how I manage to squeeze all the features of my dungeon game in the tiny memory of the Pebble Watch.
There was a great article from David Drake on this subject related to his game:
http://davidcdrake.com/2015/03/27/pebblequest-25kb-of-awesome/


Conclusion


Developing for the Pebble Watch is a very challenging but extremely fun experience. There's many roads that are just waiting to be explored which makes it exciting. 
You need to experiment, invent and think out of the box to define what you've designed to finally see it coming to live on the device.

I'll try to up the pace of my posts. Let me know if this article was useful or if even share your own experience with this device.

Dev.Log - Dungeon 3D Game - Tools Part 2

I'm finally back to producing content for my pebble dungeon game. As the sole programmer/artist/designer of this game, I tend to kee...