-
var script:XML=<code>
-
-
<make reference="blur" class="flash.filters.BlurFilter">
-
<set blurX="10" blurY="10" />
-
</make>
-
-
<make reference="filts" class="Array">
-
<call method="push">
-
<arg reference="blur" />
-
</call>
-
</make>
-
-
<make reference="mat" class="flash.geom.Matrix">
-
<call method="rotate" args="1" />
-
<set tx="100" ty="100" />
-
</make>
-
-
<make reference="box" class="flash.display.Sprite">
-
<setRef filters="filts" transform.matrix="mat"/>
-
<call method="graphics.beginFill" args="0x000000" />
-
<call method="graphics.drawRect" args="-50,-50,100,100" />
-
</make>
-
-
<call method="addChild">
-
<arg reference="box"/>
-
</call>
-
-
<make reference="tf" class="flash.text.TextFormat">
-
<set font="_sans" size="12" color="0xFFFFFF" />
-
</make>
-
-
<make reference="txt" class="flash.text.TextField">
-
<setRef defaultTextFormat="tf" />
-
<set autoSize="center" text="XML to AS3" />
-
</make>
-
-
<make reference="circle" class="flash.display.Sprite">
-
<set x="300" y="300" />
-
<call method="graphics.beginFill" args="0xFF0000" />
-
<call method="graphics.drawCircle" args="0,0,100" />
-
<call method="addChild">
-
<arg reference="txt" />
-
</call>
-
</make>
-
-
<call method="addChild">
-
<arg reference="circle"/>
-
</call>
-
-
</code>
-
-
// parse and run
-
runCode(this, script);
-
-
function runCode(target:*, code:XML):void{
-
var children:XMLList = code.children();
-
for (var i:int = 0; i<children.length(); i++){
-
var child:XML = children[i];
-
var type:String = child.name();
-
if (type == "call"){
-
runMethod(target, child);
-
}else if (type == "set"){
-
setProp(target, child);
-
}else if (type == "setRef"){
-
setRefProp(target, child);
-
}else if (type == "make"){
-
makeInstance(child);
-
}
-
}
-
}
-
-
function makeInstance(code:XML):void{
-
var ClassRef:Class = getDefinitionByName(code.@["class"]) as Class;
-
// nothing can be passed to the class constructor
-
// its not possible with function.apply
-
var instance:* = this[code.@reference] = new ClassRef();
-
runCode(instance, code);
-
}
-
-
// set a property to a reference
-
function setRefProp(target:*, code:XML):void{
-
var attributes:XMLList = code.attributes();
-
for (var i:int = 0; i<attributes.length(); i++){
-
var prop:String = attributes[i].name();
-
var o:Object = dotSyntax(target, prop, 1)
-
if (prop.indexOf(".") != -1){
-
o.obj[o.prop] = dotSyntax(this, attributes[i]);
-
}else{
-
o.obj[o.prop] = this[attributes[i]];
-
}
-
}
-
}
-
-
// set a property to a value such as a Number, Boolean, String etc...
-
function setProp(target:*, code:XML):void{
-
var attributes:XMLList = code.attributes();
-
for (var i:int = 0; i<attributes.length(); i++){
-
var prop:String = attributes[i].name();
-
var o:Object = dotSyntax(target, prop, 1)
-
o.obj[o.prop] = valueType(attributes[i]);
-
}
-
}
-
-
function runMethod(target:*, code:XML):void{
-
var i:int = 0;
-
// get a reference to the function
-
var method:Function = dotSyntax(target, code.@method);
-
// call the function if there are no arguments
-
var attributeArgsLength:int = code.@args.toXMLString().length;
-
var childArgsLength:int = code.arg.length()
-
if (attributeArgsLength == 0 && childArgsLength == 0){
-
method();
-
}else{
-
-
var args:Array;
-
if (attributeArgsLength> 0){
-
args = code.@args.toString().split(",");
-
} else {
-
args = [];
-
}
-
-
for (i = 0; i<childArgsLength; i++){
-
var val:String = code.arg[i].@value;
-
if (val){
-
args.push(val);
-
}
-
}
-
for (i = 0; i<args.length; i++){
-
if (args[i] != ""){
-
args[i] = valueType(args[i]);
-
}
-
}
-
for (i = 0; i<code.arg.length(); i++){
-
var rs:String = code.arg[i].@reference;
-
if (rs){
-
args.push(dotSyntax(this, rs));
-
}
-
}
-
-
// run the function
-
method.apply(null,args);
-
}
-
}
-
-
// parse dot syntax and return the last property or method
-
function dotSyntax(target:*, str:String, offset:int = 0):*{
-
var path:Array = str.split(".");
-
var curr:* = target;
-
for (var i:int = 0; i<path.length - offset; i++){
-
curr = curr[path[i]]
-
}
-
if (offset != 0){
-
return {obj:curr, prop:path[i]}
-
}
-
return curr;
-
}
-
-
function valueType(val:*):* {
-
if (isNaN(Number(val))) {
-
// remove leading and trailing white
-
// remove "" around strings
-
val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
-
// see if it's a boolean
-
if (val == "true"){
-
val = true;
-
}else if (val == "false"){
-
val = false
-
}
-
} else {
-
val = Number(val);
-
}
-
return val;
-
}
WARNING: There area few small bugs in this snippet. If you'd like to use this, check out the AsXML mini-library
This is the next version of yesterdays snippet. As you can see it isn't really a snippet anymore... This code parses a specifically formatted xml file to allow the following features to be achieved from XML at runtime:
1) call methods of the main timeline
2) read and write properties on the main timeline
3) instantiate classes on the main timeline
4) call methods on these classes
5) read and write properties on these classes
By implement those five feature a great deal becomes possible. The above XML creates this somewhat crappy looking thing:
While that doesn't look like much, it's actually doing quite a bit... It has a BlurFilter, an Array (for the filters property), two Sprites, a TextFormat and a TextField. addChild() is called on the main timeline and on one of the sprites (to nest the TextField in the circle).
Uses For This
You could use this to create levels for a simple game.
You could generate this XML based on user input to create e-cards and mini-apps.
If you have a medium sized app you could use it to create a sort of advanced config file that helps to ease your pain as the client decides they need 10 subtly different versions of the app. Every time the client decides they need a subtly different version you'd just need to create a different config file. THIS is probably the thing that I'll be using it for - could be a huge time saver...
Uses With Libraries
This could be used with TweenLite and/or Papervision. You could just add a bunch of import statements to the timeline or to a dynamic document class. Then you'd be able to do some basic authoring from the XML. I think I'll post an example of this either tonight or tomorrow.
I did originally start writing this thinking about QuickBox2D - but I already have an editor for QuickBox2D that just needs some UI (currently its key controlled). So I don't really need it. The QuickBox2D editor generates an ActionScript file - which makes sense because usually you need to go in and manually do a bunch of logic that couldn't really be done easily with an editor. That said, this will work with Box2D or QuickBox2D as an XML format.
The Downside
The downside is that this is dynamic and is therefore untyped. All class instances created by reading the XML will be dynamically typed. For something where your only adding 10 classes this way, its no big deal - but for a game editor it could be a problem - at the very least there are interesting advanced techniques implemented in this snippet that could be repurposed for lots of different things.
More Examples
Tonight or tomorrow I'll upload a few examples - it would be fun to do a TweenLite/Papervision example... would also be good to show what I mean by an advanced config for a medium sized app.
2 Trackbacks
[...] XML to ActionScript #2 [...]
[...] XML to ActionScript Et bud på hvordan en xml-to-as engine kan skrues sammen, og selv hvis man ikke har brug for lige netop det, så er koden et kig værd. [...]