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:
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.
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
// https://bitbucket.org/divillysausages/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:
Switching over to release mode, we compile and the AS3 Game Object Editor is no longer there:
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:
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:
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.
Comments
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
Ha, I like the cut of your gib! I'm actually looking at a hex tile engine at the minute and to get exact tile collision (for mouse picking), it involves a lot of constants that could be defined. It's all a balance between speed and flexibility (as always) :D
i have to use CONFIG::DEBUG instead of CONFIG::debug , which produces syntax error at my side.
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 ?
I just tried it, and there's no difference, it works both ways. Nice call :)
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 ?
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!
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... :(
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?
-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! :)
You need to define
DS::debug
because I made that one up :DCONFIG::debug
is the one that's defined by default in FlashDevelopIndeed, 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?
Submit a comment