Sam MacPherson

Flash, Haxe, Game Dev and more…

Monthly Archives: July 2011

Lighting and Particle Demo

Here is a demo of the effects that I have been working on in my upcoming game – Zed. The video is intended for the gamers, but you can also get a glimpse at the tech side of things.

To Batch or not to Batch

Once again I’ve revisited my rendering system. This time with the intention of switching over to a batched setup instead of a one-draw-call-per-sprite setup. Overall the switch was relatively painless, but I did learn some lessons along the way. I wanted to share some of the pitfalls that I experienced.

First, I will briefly explain what a batched sprite rendering system is. The basic idea behind batched sprite rendering is that everytime you call drawTriangles() you incur some overhead. If you are using a naive setup (Like I was) you are probably calling drawTriangles() once for every sprite. This is okay for a few sprites, but once you get a couple thousand it becomes extremely inefficient. Basically the name of the game is to minimize the number of drawTriangles() calls. To do this, we don’t immediately render the sprite when render is called. Instead we batch the sprite’s verticies onto a global vertex buffer with the intent of rendering everything at the end with one drawTriangles() call. Simple. Well not really. Using this method has some implications:

1. We can no longer use the GPU to apply sprite-specific transforms.
2. A batch of sprites must share the same texture.

The implications of (1) mean that we must do the coordinate transforms on the CPU. This not the best, but it is unavoidable. If you have a reasonable amount of sprites (Say a couple thousand) then this should be fine.

Because of (2) we must make seperate drawTriangles() calls everytime we need to switch textures. But wait hang on. If we need to make another call everytime we switch textures then doesn’t that leave us where we started seeing as different sprites will likely have different images? The answer is yes — and no. First and foremost you can group sprites of the same image into the same draw call. However, you can even go one step further.Instead of allocating a texture for every image we allocate a global store of massive textures (2048×2048 pixels). We then stamp in all the smaller textures and give the render jobs appropriate U,V coordinates. Very nice! If your game has a lot of small sprites this will be lightning fast. Probably under 3 draw calls.

Ok so we go ahead and do this and oops we have another problem. Because we are grouping the sprites by texture they will no longer necessarily be sorted by depth. Ok, so this is a set-back, but perhaps we could just batch the sprites until we encounter a texture change then flush the buffer and start again. This will of course work, but it is only efficient if sprites of similar depth also share the same texture which may not be true. For example when testing this with my game I went from 3 draw calls to about 300. This is unacceptable.

Well we are rendering on a 3D graphics card so why not use the depth buffer to do the sorting for us! Every frame update we assign a global depth value to each sprite accordingly and enable the depth buffer. This may appear to be the best solution possible, but it does have one major flaw. You can’t use translucent textures. The reason behind this is the depth buffer does not understand alpha compositing. All it understands is geometry. Either a triangle is blocking something behind it or it is not. This is where we have to make a decision. There are four equally valid solutions that I have come up with.

General Purpose Solutions

1. Fall back to the render on texture switch method and do some optimizations to try and group depth-locality with texture-locality. (Could be optimal depending on setup)

2. Render all opaque images first entirely on the GPU. Then do transparent images after using method (1). (Works very well if there isn’t many transparent images)

Specific Solutions

3. Only use opaque textures. (Optimal)

4. Only use textures with quantized alpha values of either 0 or 1. (Optimal, but requires an extra instruction in the shader)

All of these methods have there strengths and weaknesses. Personally I decided to go with method (4) for my game. Method (4) is very similar to method (3). They only differ by one instruction in the shader. For (4) you include a KIL opcode (kill() in hxsl) which when the alpha channel is less than one will abort the pixel and depth buffer writes.

There are definitely other solutions out there, but these were the best ones I could come up with after working on this for several hours. Hope this helps.

Asset Management

Here is a quick overview of how we (My game designer and I) deal with managing assets for flash development. Due to our small team size of just 2 people we historically had a very programmer-centric setup which put a lot of pressure on me, the programmer, to hard code things into the game as they were developed. This worked fine for our previous projects which were relatively simple, but our latest project requires much more content. So naturally I had to start developing tools for my designer. Specifically an asset management tool.

Starting last November I began work on an Asset Management tool written in Java. Eight months later this is what we have come up with.

The above image is a screen shot of the asset manager with our latest game’s asset file open. Let me go over each of the sections briefly. But first some terminology:

  • A directory is basically a grouping of attributes with a (potentially empty) asset attached to it. I may use the word object or game object interchangeably with directory.
  • Assets are binary data which can be images/animations/sounds/fonts/etc.
  • Attributes are just simply key/value pairs. I will sometimes call these properties.

1. Directory Explorer

Displays the logical structure of the game elements. For example I have highlighted a directory called “regular0” which is inside the zombie directory so even with no knowledge of the game internals you can probably tell it is a common zombie.

2. Directory Preview

This is somewhat preliminary. As of now only images will display. Everything else will just display text to let the designer know there is an asset attached to this directory.

3. Directory Properties

Every directory can be assigned as many properties as it needs. As you can see in the above image this is useful for attaching game-specific meta data to the object.

4. Asset Library

The asset library stores references to all external binary data. By keeping the asset library and the directories separate we are able to separate the game logic from the raw data. This is useful if for example you want to make two different enemies but don’t want to duplicate the animation data.

5. Asset Preview

Similar to the directory preview except for the asset library.

Now for some technical details on the file structure. Everything in the above image is stored into a GigaBlast Asset Package file format (.gap extension). On the flash side I can then load everything in using an in-house resource management class. The file format is as follows:

-- GigaBlast Asset Package Format Specification V2.00 --

-- Data Types --
String - 2 byte length followed by 'length' 1 byte ascii characters  
Integer - 4 byte integer  

-- Overall structure --
[Header]  
[Library]  
[Directory]  
[Binary Data]  

-- Header --
"GAP" (3 byte file format identifier)  
Major Version (2 bytes)  
Minor Version (2 bytes)  
Flags (1 byte) {  
	1 - Compressed (All non-header data is compressed with zlib)  
	2 - Signed (All non-header data is signed with RSA after compression and signature is appended to file)  
}  
ABSOLUTE Library offset (Integer) (Does not include header)  
ABSOLUTE Directory offset (Integer) (Does not include header)  
ABSOLUTE Binary Data offset (Integer) (Does not include header)  

-- Library --
Asset Count (Integer)  
Assets {  
	Name (String)  
	Data Type (Integer) [0 = Any, 1 = Image, 2 = Animation, 3 = Sound, 4 = Font)  
	RELATIVE Binary Data offset (Integer)  
}  

-- Directory --
Name (String)  
Library ID (Integer)  
Meta Data Count (Integer)  
Meta Data {  
	Name (String)  
	Value (String)  
}  
Directory Count (Integer)  
Directories {  
	Dir (Directory)  
}

-- Binary Data --
Data Size in bytes (Integer)  
Data (Binary Data)

-- Formats Supported by GigaBlast Asset Manager --
Binary Data (Anything -- even empty data)
Images (PNG, GIF, JPEG)
Animations (SWF)
Sound (MP3)
Fonts (TTF)

Once I have a GAP, say ‘lib-sam.gap’ from above, I can then load this into flash by doing this:

Assets.setCompletionCallback(onAssetsLoaded);
Assets.load('/path/to/lib-sam.gap');

function onAssetsLoaded ():void {
	var asset:Asset = Assets.get('zombies.regular0');
	trace(asset.getProperty('speed'));                    //Will print '5:10'
	var displayObject:FlashDisplayObject = asset.get();   //Get the attached asset on the directory
	canvas.add(displayObject.newInstance());              //This will create a new instance of the animation and attach it to the canvas
}

As I was designing this, the terminology between the Java and Flash counterparts became a little fuzzy. What I am in fact calling an asset in flash is actually equivalent to a directory in the Java manager.

You are able to load as many GAP files as you want. They will all be gracefully merged into the Assets class.

But wait! There’s more!

You may have noticed in the file format structure there is the allowance for compression and package signing. The compression is completely transparent. The package signing is useful if you don’t want people tampering with the GAP file (Messing with enemy stats and bad stuff like that). I’ve also included a key generation tool.

When the keys are set (As seen in the above image) then the file will include an RSA signature at the end of the GAP file. You then hardcode the public key somewhere very hidden inside your swf file. When loading the file you do this:

var publicKey:BigInteger = BigInteger.ofString("30819F300D06092A864886F70D010101050003818D0030818902818100B79E8D7D9B538BDCBFF4EDD0E4BB0AB3031D49FFD8759F7FEDF5D171BF9969EBC148325F3FF9246A2E489F6FE7506329B72DD3477134FB6D7098487261B8F9A6E96DDF21DCB0062C9931737C9924A6CFC64DAC06A8F6DB1F363F54FBA763330EB2A8DBEB52D9DD62AB998D2C7BC63CAE2D8D4E2A5A8A13E0B362F44410134DFB0203010001", 16);
Assets.load('path/to/lib-sam.gap', publicKey);

What does this do? Without going into details it will disallow anyone but the person who knows the private key to edit the GAP file. The BigInteger class you see is provided by caffine-hx which is a haxe library.

But wait! There’s even MORE!

Included in the asset manager is a generic map editor which is capable of drawing in directories onto a map.

The map editor is in its infancy, but as of now it is capable of drawing three distinct types of images: surfaces (grass, dirt), wall-like things (walls, railings, roads) and doodads (chairs, tables). The nice thing about combining this with the asset manager is that for example as long as you add in a ‘type’ property on your directory and the directory is an image anything can be drawn onto the map.

To make things more specific to a particular game you can add in any property onto the drawn directory. For example Zed (Our zombie game) has collision areas which I’m sure is fairly common. To make any object collidable you simply add the property ‘inteact’=’collidable’ to the directory and begin drawing it in.

My flash-side library is capable of loading maps in a WYSIWYG fashion. More complicated constructs such as lighting and localized sound is achieved by having game logic pull out specific drawn-in objects instead of rendering them. You can see an example of this in the above example with all the faint blue/green/red areas. Those areas are in fact markers for lighting and event based actions.

There is a bunch more features but I won’t get into them right now. As of now our asset manager is not open to the public, but I hope to release it into the open source community soon. I am not particularly skilled at GUI design so perhaps someone out there would be willing to help out with that aspect.

Why Haxe is Just Awesome Pt 2

Ok, so as always I end up doing way more work then I need to due to my lack of knowledge. After some development, the javascript interface I was explaining in my previous post ended up getting a little sloppy. Turns out for more AJAX intensive parts of my site resolving html elements was getting harder and harder. A lot of the time I ended up just navigating the dom by relative parent/child pointers which of course is terrible if you want to have a manageable long-term code-base. Having the static event handlers just wasn’t cutting it. So I did some looking around and it turns out Haxe already has a solution!

Serialization is the answer and the haxe.Serializer and haxe.Unserializer classes provide just the solution. Now instead of transmitting the html (

tags, etc) from the server I just send the client a serialized version of the dom representation and the javascript on the client can recreate the exact same structure on the client! A webpage ends up looking like this:

<html><head><title>Home</title><link type='text/css' href='/style.css' rel='stylesheet'></link><script type='text/javascript' src='/client.js'></script><meta name='data' content='cy12:www.dom.HTMLy8:childrenlcy12:www.dom.Heady6:parentr0R1lcy13:www.dom.Titley4:texty4:HomeR3r2R1lhy3:tagy5:titley7:attribsbhgcy18:www.dom.StyleSheetR3r2R1lhR7y4:linkR9by4:typey10:text%2Fcssy4:hrefy12:%2Fstyle.cssy3:rely10:stylesheethgcy18:www.dom.JavascriptR3r2R1lhR7y6:scriptR9bR12y17:text%2Fjavascripty3:srcy12:%2Fclient.jshghR7y4:headR9bhgcy12:www.dom.BodyR3r0R1lcy11:www.dom.DivR3r14R1lcy12:www.dom.LogoR3r16R1lcy11:www.dom.ImgR3r18R1lhR7y3:imgR9bR21y20:%2Fimages%2Flogo.pnghghR7y3:divR9by5:classy4:logohgcy13:www.dom.LoginR3r16R1lcy17:www.dom.form.FormR3r24R1lcy19:www.dom.form.HiddenR3r26R1lhR7y5:inputR9by4:namey1:aR12y6:hiddeny5:valuey5:loginhgcR35R3r26R1lhR7R36R9bR37y4:hashR12R39R40y32:2h4i27uutas7l02dild8t8hzbi0489f7hgcy18:www.dom.form.InputR3r26R1lhR7R36R9bR37y4:userR12R5y2:idy2:e1R31y72:input-empty%20__clswww.dom.form.Input%20__ed.e1.onfocus%20__ed.e1.onblurR40y8:Usernamehgcy21:www.dom.form.PasswordR3r26R1lhR7R36R9bR37y4:passR12R5R46y2:e2R31y75:input-empty%20__clswww.dom.form.Password%20__ed.e2.onfocus%20__ed.e2.onblurR40y8:Passwordhgcy21:www.dom.form.CheckboxR5y16:Remember%20Me%3FR3r26R1lhR7R36R9bR37y8:rememberR12y8:checkboxR40y4:truehgcy19:www.dom.form.SubmitR3r26R1lhR7R36R9bR12y6:submitR31y15:__ed.e3.onclickR40y5:LoginhghR7y4:formR9by6:actiony0:y6:methody4:posty7:enctypey35:application%2Fx-www-form-urlencodedR46y2:e3R31y18:__clswww.dom.Loginhgcy9:www.dom.AR5y24:Register%20an%20account.R3r24R1lhR7R38R9bR14y11:%2FregisterhghR7R30R9bR31y7:accounthgcR25R3r16R1lcy15:www.dom.NavListR3r51R1lcy10:www.dom.LIR3r53R1lcR73R5R6R3r55R1lhR7R38R9bR14y3:%2FhghR7y2:liR9bhgcR78R3r53R1lcR73R5y5:GamesR3r61R1lhR7R38R9bR14y8:%2FgameshghR7R80R9bhgcR78R3r53R1lcR73R5y7:ProfileR3r67R1lhR7R38R9bR14y10:%2FprofilehghR7R80R9bhgcR78R3r53R1lcR73R5y6:GroupsR3r73R1lhR7R38R9bR14y9:%2FgroupshghR7R80R9bhgcR78R3r53R1lcR73R5y5:ForumR3r79R1lhR7R38R9bR14y8:%2FforumhghR7R80R9bhghR7y2:ulR9bhghR7R30R9bR31y10:navigationhghR7R30R9bR31y6:headerhgcR25R3r14R1lcR25R3r88R1lcR25R3r90R1lhR7R30R9bR31y5:errorhgcR25R3r90R1lcy22:www.dom.home.GameSmallR3r95R1lcR25R3r97R1lcy10:www.dom.H1R5y3:ZedR3r99R1lhR7y2:h1R9bhghR7R30R9bR31y16:game-small-titlehgcR25R3r97R1lcy19:www.dom.AnchorImageR5R66R3r105R1lcR27R3r107R1lhR7R28R9bR21y25:%2Fimages%2Fgames-zed.pnghghR7R38R9bR14y3:%23hghR7R30R9bR31y34:game-small-image%20__ed.e0.onclickhgcR25R3r97R1lcy9:www.dom.PR5y229:Pick%20a%20class%2C%20kill%20zombies%20and%20level%20up%20your%20character.%20Features%20will%20include%20an%20extensive%20item%20system%2C%20very%20customizable%20skill-sets%2C%20large%20re-playable%20worlds%20and%20much%20more.R3r114R1lhR7y1:pR9bhghR7R30R9bR31y15:game-small-deschghR7R30R9bR46y2:e0R31y33:game-small%20__clswww.dom.GameDOMhghR7R30R9bhghR7R30R9bR31y7:contenthghR7R30R9bR31y29:content-wrapper%20columns-onehgcR25R3r14R1lcR102R5y6:FooterR3r124R1lhR7R104R9bhghR7R30R9bR31y6:footerhgcy13:www.dom.CoverR3r14R1lhR7R30R9bR37y5:coverhgcy15:www.dom.GameDOMR3r14R1lhR7R30R9bR37y4:gamehgcy12:www.dom.DockR3r14R1lcy19:www.dom.FriendsListR3r136R1lcR73R5y7:FriendsR3r138R1lhR7R38R9bR14R100R31y15:__ed.e4.onclickhghR7R30R9bR46y2:e4R31y43:dock-friend-list%20__clswww.dom.FriendsListhghR7R30R9bR37y4:dockR31R122hghR7y4:bodyR9bR46R123hghR7y4:htmlR9bhg' id='__DATA__'></meta></head></html>

As you can see the html is fairly boiler-plate save for the massive meta tag. The meta tag contains the entire webpage along with the back-end to make AJAX stuff a LOT easier. I can then run this on the client:

ROOT = Unserializer.run(js.Lib.document.getElementById("__DATA__").getAttribute("content"));

What does all of this give me? Very intuitive code that seamlessly integrates the server and the client. For example I can write this:

var body:Body = new Body();
var button = new Submit("MyButton");
button.addEventListener(Event.CLICK, onClick);
body.add(button);

function onClick (e) {
	body.remove(button);
}

Amazingly this code will add a button to the body of an html document then (when clicked) will remove itself. How does this work? The body and button tags are generated on the server and transmitted through the serialized data meta tag then unserialized and javascript sets up the listeners on the client. Once the button is pressed the function onClick() is called (In javascript) and the body element removes the button child. Of course some code is missing but you get the idea. Very cool stuff!