Monthly Archives: June 2009

XML to ActionScript #3 (AsXML)

XML:
  1. <code>
  2.   <make reference="w" class="BasicView" args="stage.stageWidth, stage.stageHeight, false"/>
  3.   <call method="addChild" args="w"/>
  4.  
  5.   <make reference="wireMat" class="WireframeMaterial" args="0x000000" />
  6.  
  7.   <make reference="sphere" class="Sphere" args="wireMat, 100" />
  8.  
  9.   <call method="w.scene.addChild" args="sphere" />
  10.  
  11.   <make reference="animation" class="Object">
  12.     <set z="-500" rotationY="360"  rotationX="360" ease="Back.easeOut"/>
  13.   </make>
  14.  
  15.   <call method="TweenLite.to" args="sphere, 3, animation" />
  16.  
  17.   <call method="setInterval" args="w.singleRender, 32" />
  18.  
  19. </code>

This snippet shows XML that the mini-library AsXML can read and run - in this case AsXML is set up to run with Papervision

A few days ago I had the idea to write some code that would run ActionScript based on XML. I spent some time getting rid of a few bugs and setting up some demos with TweenLite, Papervision and QuickBox2D. I wrapped everything up into a mini-library called AsXML.

Check out the demos here.


Download AsXML and demo files here.

AsXML Features:
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
6) store references to return values from functions

Posted in Box2D, Graphics, Math, QuickBox2D, XML, dynamic, external data, instantiation, misc, motion, return values, string manipulation, strings | Tagged , , | 11 Comments

Funny Parameterized Factory

Actionscript:
  1. package{
  2.  
  3.     import flash.utils.getDefinitionByName;
  4.    
  5.     public class Creator{
  6.        
  7.         public function Creator():void{
  8.             // classes we can create
  9.              ClassA;
  10.              ClassB;
  11.         }
  12.  
  13.         public function create(type:String):ISomeInterface{
  14.              var ClassRef:Class = getDefinitionByName(type) as Class;
  15.              return ISomeInterface(new ClassRef());
  16.         }
  17.     }
  18. }

Actionscript:
  1. package{
  2.     public interface ISomeInterface{
  3.         function sayHi():void;
  4.     }
  5. }

Actionscript:
  1. package{
  2.    
  3.     public class ClassA implements ISomeInterface{
  4.        
  5.         public function ClassA(){
  6.             trace("new ClassA()");
  7.         }
  8.        
  9.         public function sayHi():void{
  10.             trace("hello I am a ClassA instance");
  11.         }
  12.     }
  13.      
  14. }

Actionscript:
  1. package{
  2.    
  3.     public class ClassB implements ISomeInterface{
  4.        
  5.         public function ClassB(){
  6.             trace("new ClassB()");
  7.         }
  8.        
  9.         public function sayHi():void{
  10.             trace("hello I am a ClassB instance");
  11.         }
  12.     }
  13.      
  14. }

... and lastly on the timeline:

Actionscript:
  1. var creator:Creator = new Creator();
  2.  
  3. var a:ISomeInterface = creator.create("ClassA");
  4. a.sayHi();
  5.  
  6. var b:ISomeInterface = creator.create("ClassB");
  7. b.sayHi();
  8.  
  9. /*outputs
  10.  
  11. new ClassA()
  12. hello I am a ClassA instance
  13. new ClassB()
  14. hello I am a ClassB instance
  15.  
  16. */

Unfortunately this has some downsides compared to a switch or if statement parameterized factory. Still fun though...

Posted in Uncategorized | Tagged , , | Comments closed

XML to ActionScript #2

Actionscript:
  1. var script:XML=<code>
  2.  
  3.    <make reference="blur" class="flash.filters.BlurFilter">
  4.      <set blurX="10" blurY="10" />
  5.    </make>
  6.    
  7.    <make reference="filts" class="Array">
  8.      <call method="push">
  9.        <arg reference="blur" />
  10.      </call>
  11.    </make>
  12.    
  13.    <make reference="mat" class="flash.geom.Matrix">
  14.      <call method="rotate" args="1" />
  15.      <set tx="100" ty="100" />
  16.    </make>
  17.    
  18.    <make reference="box" class="flash.display.Sprite">
  19.        <setRef filters="filts" transform.matrix="mat"/>
  20.        <call method="graphics.beginFill" args="0x000000" />
  21.        <call method="graphics.drawRect" args="-50,-50,100,100" />
  22.    </make>
  23.    
  24.    <call method="addChild">
  25.      <arg reference="box"/>
  26.    </call>
  27.    
  28.    <make reference="tf" class="flash.text.TextFormat">
  29.     <set font="_sans" size="12" color="0xFFFFFF" />
  30.    </make>
  31.  
  32.    <make reference="txt" class="flash.text.TextField">
  33.      <setRef defaultTextFormat="tf" />
  34.      <set autoSize="center" text="XML to AS3" />
  35.    </make>
  36.  
  37.    <make reference="circle" class="flash.display.Sprite">
  38.        <set x="300" y="300" />
  39.        <call method="graphics.beginFill" args="0xFF0000" />
  40.        <call method="graphics.drawCircle" args="0,0,100" />
  41.        <call method="addChild">
  42.          <arg reference="txt" />
  43.        </call>
  44.    </make>
  45.    
  46.    <call method="addChild">
  47.      <arg reference="circle"/>
  48.    </call>
  49.  
  50. </code>
  51.  
  52. // parse and run
  53. runCode(this, script);
  54.  
  55. function runCode(target:*, code:XML):void{
  56.     var children:XMLList = code.children();
  57.     for (var i:int = 0; i<children.length(); i++){
  58.         var child:XML = children[i];
  59.         var type:String = child.name();
  60.         if (type == "call"){
  61.             runMethod(target, child);
  62.         }else if (type == "set"){
  63.             setProp(target, child);
  64.         }else if (type == "setRef"){
  65.             setRefProp(target, child);
  66.         }else if (type == "make"){
  67.             makeInstance(child);
  68.         }
  69.     }
  70. }
  71.  
  72. function makeInstance(code:XML):void{
  73.     var ClassRef:Class = getDefinitionByName(code.@["class"]) as Class;
  74.     // nothing can be passed to the class constructor
  75.     // its not possible with function.apply
  76.     var instance:* = this[code.@reference] = new ClassRef();
  77.     runCode(instance, code);
  78. }
  79.  
  80. // set a property to a reference
  81. function setRefProp(target:*, code:XML):void{
  82.     var attributes:XMLList = code.attributes();
  83.     for (var i:int = 0; i<attributes.length(); i++){
  84.         var prop:String = attributes[i].name();
  85.         var o:Object = dotSyntax(target, prop, 1)
  86.         if (prop.indexOf(".") != -1){
  87.           o.obj[o.prop] = dotSyntax(this, attributes[i]);
  88.         }else{
  89.           o.obj[o.prop] = this[attributes[i]];
  90.         }
  91.     }
  92. }
  93.  
  94. // set a property to a value such as a Number, Boolean, String etc...
  95. function setProp(target:*, code:XML):void{
  96.     var attributes:XMLList = code.attributes();
  97.     for (var i:int = 0; i<attributes.length(); i++){
  98.         var prop:String = attributes[i].name();
  99.         var o:Object = dotSyntax(target, prop, 1)
  100.         o.obj[o.prop] = valueType(attributes[i]);
  101.     }
  102. }
  103.  
  104. function runMethod(target:*, code:XML):void{
  105.     var i:int = 0;
  106.     // get a reference to the function
  107.     var method:Function = dotSyntax(target, code.@method);
  108.     // call the function if there are no arguments
  109.     var attributeArgsLength:int = code.@args.toXMLString().length;
  110.     var childArgsLength:int = code.arg.length()
  111.     if (attributeArgsLength == 0 && childArgsLength == 0){
  112.         method();
  113.     }else{
  114.      
  115.      var args:Array;
  116.      if (attributeArgsLength> 0){
  117.          args = code.@args.toString().split(",");
  118.      } else {
  119.         args = [];
  120.      }
  121.    
  122.      for (i = 0; i<childArgsLength; i++){
  123.           var val:String = code.arg[i].@value;
  124.           if (val){
  125.             args.push(val);
  126.           }
  127.       }
  128.       for (i = 0; i<args.length; i++){
  129.           if (args[i] != ""){
  130.             args[i] = valueType(args[i]);
  131.           }
  132.       }
  133.       for (i = 0; i<code.arg.length(); i++){
  134.          var rs:String = code.arg[i].@reference;
  135.          if (rs){
  136.              args.push(dotSyntax(this, rs));  
  137.          }
  138.       }
  139.      
  140.       // run the function
  141.       method.apply(null,args);
  142.     }
  143. }
  144.  
  145. // parse dot syntax and return the last property or method
  146. function dotSyntax(target:*, str:String, offset:int = 0):*{
  147.     var path:Array = str.split(".");
  148.     var curr:* = target;
  149.     for (var i:int = 0; i<path.length - offset; i++){
  150.         curr = curr[path[i]]
  151.     }
  152.     if (offset != 0){
  153.         return {obj:curr, prop:path[i]}
  154.     }
  155.     return curr;
  156. }
  157.  
  158. function valueType(val:*):* {
  159.     if (isNaN(Number(val))) {
  160.            // remove leading and trailing white
  161.            // remove "" around strings
  162.            val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
  163.            //  see if it's a boolean
  164.            if (val == "true"){
  165.                val = true;
  166.            }else if (val == "false"){
  167.                val = false
  168.            }
  169.     } else {
  170.         val = Number(val);
  171.     }
  172.     return val;
  173. }

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.

Posted in XML, dynamic, external data, instantiation | Tagged , , | 2 Comments

XML to ActionScript

Actionscript:
  1. // this xml is inline but could easily be in an external file
  2. var script:XML=<code>
  3.  
  4.   <!-- // call some methods of the main timeline graphics property -->
  5.   <call method="graphics.beginFill" args="0xFF0000" />
  6.    
  7.   <call method="graphics.drawRect" args="0, 0">
  8.      <!-- // use this to access properties of the main movie -->
  9.      <!-- // and use them as arguments -->
  10.      <arg reference="stage.stageWidth" />
  11.      <arg reference="stage.stageHeight" />
  12.   </call>
  13.  
  14.   <call method="graphics.endFill" args=""/>
  15.  
  16.   <call method="graphics.beginFill" args="0x0000FF">
  17.      <!-- //regular non-reference arguments can be passed like this -->
  18.      <arg value="0.5" />
  19.   </call>
  20.  
  21.   <call method="graphics.drawCircle" args="100,100,20" />
  22.  
  23.   <call method="customFunction" args="hello">
  24.      <arg reference="root.loaderInfo.bytesTotal" />
  25.   </call>
  26.  
  27. </code>
  28.  
  29. function customFunction(a:String, b:String):void{
  30.     trace("I am a custom function called from xml");
  31.     trace("Here is an argument: " + a);
  32.     trace("Here is another argument, (total bytes of this swf): " + b);
  33. }
  34.  
  35. // parse and run
  36. runCode(script);
  37.  
  38. function runCode(code:XML):void{
  39.     var children:XMLList = code.children();
  40.     for (var i:int = 0; i<children.length(); i++){
  41.         var child:XML = children[i];
  42.         var type:String = child.name();
  43.         if (type == "call"){
  44.             runMethod(child);
  45.         }
  46.     }
  47. }
  48.  
  49. function runMethod(code:XML):void{
  50.     var i:int = 0;
  51.     // get a reference to the function
  52.     var method:Function = dotSyntax(code.@method);
  53.     // call the function if there are no arguments
  54.     if (code.@args.toXMLString().length == 0 && code.arg.length() == 0){
  55.         method();
  56.     }else{
  57.      
  58.      var args:Array = code.@args.split(",");
  59.      var childArgsLength:int = code.arg.length()
  60.      for (i = 0; i<childArgsLength; i++){
  61.           // add another one with dot syntax
  62.           var val:String = code.arg[i].@value;
  63.           if (val){
  64.             args.push(val);
  65.           }
  66.       }
  67.       for (i = 0; i<args.length; i++){
  68.           if (args[i] != ""){
  69.             args[i] = valueType(args[i]);
  70.           }
  71.       }
  72.       for (i = 0; i<code.arg.length(); i++){
  73.          var rs:String = code.arg[i].@reference;
  74.          if (rs){
  75.              var ref:* = dotSyntax(rs);
  76.              args.push(ref);  
  77.          }
  78.       }
  79.       // run the function
  80.       method.apply(null,args);
  81.     }
  82. }
  83.  
  84. // parse dot syntax and return the last property or method
  85. function dotSyntax(str:String):*{
  86.     var path:Array = str.split(".");
  87.     var curr:* = this;
  88.     for (var i:int = 0; i<path.length; i++){
  89.         curr = curr[path[i]]
  90.     }
  91.     return curr;
  92. }
  93.  
  94. function valueType(val:*):* {
  95.     if (isNaN(Number(val))) {
  96.            // remove leading and trailing white
  97.            // remove "" around strings
  98.            val = val.replace(/^s+|s+$/g,"").replace(/^"|"$/g,"");
  99.            //  see if it's a boolean
  100.            if (val == "true"){
  101.                val = true;
  102.            }else if (val == "false"){
  103.                val = false
  104.            }
  105.     } else {
  106.         val = Number(val);
  107.     }
  108.     return val;
  109. }

WARNING: There area few small bugs in this snippet. If you'd like to use this, check out the AsXML mini-library

A few days ago I had the idea to write some code that would parse ActionScript from XML. The features that I realized would be possible are:

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

So this morning I wrote the first part. This snippet has the ability to read an XML file and call methods accordingly. It also has the ability to read properties of the main timeline and pass them to these methods.

The XML in this snippet draws a box using the stageWidth and stageHeight. It draws a transparent circle and then calls a custom function. The custom function gets passed the root.loaderInfo.bytesTotal property.

I've done something similar a few times where I specifically targeted the Graphics API. This takes that to the next level by potentially working with any ActionScript classes (excluding Vector maybe... need to think about that).

I'll post another version with more features tomorrow. At the very least it will have class instantiation.

Posted in XML, dynamic, external data, string manipulation | Tagged , , | 6 Comments