AS3 Conditional compilation or #ifdef in Flash

A few days ago I came across the method of conditional compiling in Flash. If you're from a C or a C++ background, you'll probably be familiar with the #define and #ifdef directives. They're most common in a multi-platform engine and usually found deep in the math or render code. They can be highly useful and let you develop specific code depending on how the variables are set up. Up until recently, I didn't think it was possible to do this through Flash.

How does it work?

Basically, you code as normal and when you want something to be conditionally compiled, you wrap it in compile blocks like so:

CONFIG::debug
{
	// your code here
}

FlashDevelop has 3 constants by default (what doesn't it have by default?): CONFIG::debug, CONFIG::release, and CONFIG::timeStamp. debug is defined as true when we're in the Debug configuration (see the dropdown on the top toolbar), release is defined as true when in the Release configuration, and timeStamp we don't care about here. If you want to define your own, you can by simply adding a define line to the Compiler Options of your project:

Adding a define statement to an AS3 project

The syntax is:

-define+=DS::debug,true

Where DS::debug is the variable you want to define (DS can be any namespace (DS is the name of my engine), and debug can be any name), followed by an AS3-valid literal const (true, false, 0.1, "Damian")

If you're using the Flash IDE, you can add it by going through the File > Publication Parameters, hit the Parameters button beside ActionScript 3.0 on the Flash tab, then add your constant in the Configure Constants tab. As you can see, Flash defines a CONFIG::FLASH_AUTHORING for you if you need to target it specifically for something. Also, my Flash IDE is in French; one of the side effects of living in France.

Defining a constant through the Flash IDE

Example time!

I've put together a simple project to show how this works (download it here or from below). It has 2 classes: a Main class and a Ball class. The Ball class is just a simple class that draws a circle and has a getter/setter for it's colour. The Main class uses the amazing AS3 Game Object Editor to edit the colour of the ball in real-time. Obviously I don't want to ship the object editor with this wonderful piece of flashery, so by using conditional compilation, the code for the Main class becomes:

package 
{
	import com.divillysausages.gameobjeditor.Editor;
	import flash.display.Sprite;
	import flash.events.Event;
	
	public class Main extends Sprite 
	{
		private var m_ball:Ball = null;
		
		CONFIG::debug
		{
			// we define the editor variable in a conditional block as well, as
			// otherwise, it'll still be included when we compile. The import
			// we can ignore as Flash strips out all imports that aren't used
			// in the project
			private var m_editor:Editor = null;
		}
		
		public function Main():void 
		{			
			// create our editor
			CONFIG::debug
			{
				// this is using the AS3 Game Object Editor
				// http://code.google.com/p/as3-game-object-editor/
				this.m_editor = new Editor( this.stage );
				this.m_editor.registerClass( Ball );
				this.m_editor.visible = true;
			}
			
			// create and add our ball
			this.m_ball = new Ball;
			this.addChild( this.m_ball );
		}
		
	}
	
}

Running the SWF in debug mode gives us the following results:

The SWF running with the Game Object Editor enabled

Switching over to release mode, we compile and the AS3 Game Object Editor is no longer there:

The SWF running with the Game Object Editor enabled

My face :|

So far, no big shakes, so why not just use a Boolean to do all this for us? Because conditional compiling has one enormous benefit: if the condition is false, the code is removed from the SWF. Compiling in Debug mode (i.e. where CONFIG::debug is true) will yield us a SWF comprised of a number of classes:

The class structure of the SWF when the conditional compile resolves to true

That's a lot of extra crap. Switching to Release mode (i.e. where CONFIG::debug is false so the Editor never gets created) gives us a SWF like this:

The class structure of the SWF when the conditional compile resolves to false

See the amazing benefit yet?

What can I do with all this?

Practically, this is incredibly useful if you're making something like a game. Level editors, cheat codes, the ability to skip levels, test functions, change scores, change properties etc, can all be enabled during Debug mode. Then, when you switch over to Release mode to release your game, all of that is magically removed. There's no need to remember to disable everything when you're finished: in the Release SWF, it simply doesn't exist. There's no worry that somebody might discover the cheat codes you added for testing and destroy your game; they don't exist any more. Want to make sure that someone can't set a property for an object (say, by hacking the SWF)? Conditional compile out the contents of the setter.

If you've ever tried to make money from your game, you'll know all about implementing 100 different versions of Ad serving, highscore tables, event tracking, site-locked content and other paraphernalia. There's no need to keep 15 different projects with all the different changes. Conditional compile it and not only is it a simple recompile to make your changes, but other versions don't include the different libraries that you're using. For example, Kongregate don't show Mochi ads, so why should that SWF include the lib? If you want to connect to the Kong chat, why should the other SWFs need that code in there?

DS::kong
{
	// do some kongregate related stuff
}
DS::mochi
{
	// do some mochi related stuff
}

Troubleshooting

One thing to keep in mind if you don't want debug or cheat classes in your final SWF is to also conditionally compile the variable declaration. If your class is something like:

package 
{
	import com.divillysausages.gameobjeditor.Editor;
	import flash.display.Sprite;
	
	public class Main extends Sprite 
	{		
		private var m_editor:Editor = null;
		
		public function Main():void 
		{
			CONFIG::debug
			{
				// create the editor
				this.m_editor = new Editor( this.stage );
				...
			}
		}
		
	}
	
}

Then the editor classes will still be compiled, as the variable declaration for m_editor is bringing them in. Change the declaration line to:

CONFIG::debug
{
	private var m_editor:Editor = null;
}

And you'll be fine. You can ignore the import statement, as Flash strips out all imports that aren't used in the file.

AttachmentSize
TestConditional.zip105.42 KB

Share:

Comments

Hubert
Mon, 27/08/2012 - 15:37

Indeed, very useful. One I added is CONFIG::editor, independent from debug or release, and in this way I can have a quick optimized editor.

The only problem is that they mess FlashDevelop's code recognition!! and autocomplete stops working on some variables, functions, classess... It doesn't matter if I put the definition in "additional compiler option" or "compiler constants" - FD can't grok them.

Or at least so appears at first sight... perhaps someone found some magical checkbox in options, plugin or whatever that will bring back autocomplete?

Oliver
Tue, 12/04/2011 - 00:20

-define+=DS::debug,true is only needed for old flashdevelop builds, there is no need to add it in there if you have the latest release(like the current 3.3.4)

Good job on talking about this! :)

Damian Connolly
Tue, 12/04/2011 - 10:46

You need to define DS::debug because I made that one up :D

CONFIG::debug is the one that's defined by default in FlashDevelop

Martial C.
Mon, 04/04/2011 - 10:47

Hi,

I'm wondering if such a solution could be used with the FLASH IDE.

I use Flash Develop to do all the ActionScript 3, but I still have to use Flash IDE for all the graphics-heavy part.

I really could use conditional compiling, but I don't think it's really possible, unfortunately.

Does anybody have an idea about that ?

Damian Connolly
Mon, 04/04/2011 - 11:18

Hey Martial,

This is easily done in the Flash IDE if you want to use it. I've updated the post with the steps to get it done. Have fun!

JimmyDeemo
Mon, 01/08/2011 - 13:40

Is there any way to make it so that the Flash IDE can see the config constants that FlashDevelop has declared? For example I, like the poster above, use FD for code and FlashIDE for graphics. Currently if I wanted to switch between 'debug' and 'release' in FD, then i would also have to reflect that change in the FlashIDE adding in extra steps and the convenience of just changing the drop down box in FD.

I fear there may not be a way of transfering this over... :(

Damian Connolly
Mon, 01/08/2011 - 14:31

While Flash lets you load a custom publish profile from a XML, and FD can accept a config XML file, they aren't the same format. You'd need to create a small tool which would update both XML files when you wanted to change a compile constant. And in any case, you'd need to reload it into Flash; it doesn't recognise when the XML has changed, so it's more trouble than it's worth. You'd need to keep changing it manually.

Do you need to have the constants in the Flash IDE? When you say you use Flash just for graphics, it shouldn't need any compile constants. Are you compiling through Flash, or FD?

thibaud
Mon, 04/04/2011 - 10:41

why not simply use the "Compiler Constant" entry right below the "Additional compiler Options" ?
with just (as your example):
DS::debug,true
instead of
-define+=DS::debug,true
is there any difference ?

Damian Connolly
Mon, 04/04/2011 - 10:55

I just tried it, and there's no difference, it works both ways. Nice call :)

Jackson Dunstan
Mon, 04/04/2011 - 00:40

You can also use compile-time constants to avoid runtime calculation and lookups. For example, if you do this:

-define+=MATH::twoTimesPI,6.28318531

Then in your code:

var circleCircumference:Number = MATH::twoTimesPI * radius;

This avoids:

- Looking up the Math class
- Accessing a static field of the Math class (Math.PI)
- A multiplication (2*Math.PI)
- The bytecode necessary to do all of this

Instead, it's like you used the literal directly:

var circleCircumference:Number = 6.28318531 * radius;

This is obviously even more useful when you have complicated (but still constant) mathematical values. Here, it's even more useful to base them on each other:

-define+=GAMETILESIZES::width,100
-define+=GAMETILESIZES::halfWidth,GAMETILESIZES::width/2
-define+=GAMETILESIZES::quarterWidth,GAMETILESIZES::width/4
-define+=GAMETILESIZES::eighthWidth,GAMETILESIZES::width/8
-define+=GAMETILESIZES::widthTimesTwo,GAMETILESIZES::width*2
etc.

The possibilities are many! :-D