Showing posts with label class. Show all posts
Showing posts with label class. Show all posts

Wednesday, October 26, 2011

Improving MovieClipProxy.as?

This is the fifth post in a series.  I made a couple of  "improvements" to MovieClipProxy.as and found out a thing or two before abandoning the idea due to our sloppy coding practices that would require more fixing than I was prepared to do.

I was also happy to receive a reply from Senocular indicating that there was not a simpler way just to override the _visible setter.  He wrote:
AS3 supports native implementation overrides for getter/setters, but not AS1/AS2
In the last post, I left off wondering whether I was willing to write a getter/setter for all the properties, methods and functions of my ubPod class.

I wrote two functions for MovieClipProxy.as to add additional properties and functions to a MovieClipProxy instance, namely addProp and addFunction:

public function addProp(propName:String){
 var getterFunction:Function = function(){
  return this._mc[arguments.callee.name];
 }
 getterFunction.name = propName;
 var setterFunction:Function = function(newVal):Void{
  this._mc[arguments.callee.name] = newVal;
 }
 setterFunction.name = propName;

 this.addProperty(propName, getterFunction, setterFunction);
}
public function addFunction(propName:String){
 this[propName] = function(a, b, c, d, e, f, g, h, i, j){
  return this._mc[arguments.callee.name].call(arguments.callee.mcp_instance, a, b, c, d, e, f, g, h, i, j);
 }
 this[propName].name = propName;
 this[propName].mcp_instance = this._mc;

 //no luck getting getters and setters to work for functions
    /*
 var getterFunction:Function = function(){
  trace("in mcp getter function with name "+arguments.callee["name"]+" from "+arguments.callee);
  trace("addFunction getter");
  return this._mc[arguments.callee.name];
 }
 getterFunction.name = propName;
 var setterFunction:Function = function(f:Function){
  trace("in mcp setter function with name "+arguments.callee["name"]+" from "+arguments.callee);
  trace("addFunction setter");
  //this._mc[arguments.callee.name] = newVal;
  this._mc[arguments.callee.name] = function(a, b, c, d, e, f, g, h, i, j){return f.call(arguments.callee.mcp_instance, a, b, c, d, e, f, g, h, i, j)};
 }
 setterFunction.name = propName;
 setterFunction.mcp_instance = this._mc;

 this.addProperty(propName, getterFunction, setterFunction);
 */
}

which are invoked pretty simply with code like:

mcp.addProp("side");
mcp.addFunction("scale");

Next, I wondered if there was a way to get my ubPod class to reveal more of its properties and methods in a for..in loop. The new learning here relates to ASSetPropFlags (of which the compiler is unaware) which allowed me to write a function like:

_global["ASSetPropFlags"](ubPod.prototype,null,6,true);

for (var i in ubPod.prototype){    
    var thing = ubPod.prototype[i];
    var typeThing:String = typeof(thing)
    if (typeThing == "function" && i != "__constructor__"){
        mcp.addFunction(i);
    } else if (typeThing == 'number' || typeThing == 'string' || typeThing == 'boolean' || (typeThing == 'object' && i != "__proto__")){
        mcp.addProp(i);
    } else {
        trace("did nothing for "+i);
    }
}

Maybe some of this will help folks still in AS2 land.  I am left still pretty foggy about __proto__, prototype, extends and casting.

Here are two challenges:
  1. Find the error in the code shared in the previous post.
  2. Modify my addFunction method to allow for a getter and setter as well as a function definition to be established.

Monday, October 24, 2011

Actionscript 2: Catching when an instance of a Class that extends MovieClip is made visible

You just don't get blog post titles as precise as that every day!

I have been telling a story today about trying to catch when a movieclip's _visible property is set.  First, I showed how we create classes that extend MovieClip.  Next, I described the problems that I am having writing a set and get function for _visible.  Third, I showed how MovieClipProxy could be used on a simple MovieClip to catch the setting of _visible.  Now, I am going to finish the story and show how it can be done for a class like ubSquare.

The first step is to make a slight adjustment to MovieClipProxy.   Its constructor is written to either take 3 parameters and create an empty MovieClip or 4 parameters and attach a movieclip from the library.  I changed it so that if it is sent more, it will use the first parameter to create the movieclip.

In the first post's example, we created the ubSquare using:

this.mySquare_ubS = ubSquare.create(this, "mySquare_mc", {_x:50, _y:75, _side:10});

So if we change the constructor of MovieClipProxy to read:

 function MovieClipProxy(a,b,c,d,e) {
  var mc, mc2;
  if (arguments.length == 3) {
   mc = __initEmpty(a,b,c);
  } else if (arguments.length == 4){
   mc = __initAttached(a,b,c,d);
  } else {
   mc = a(b,c,d,e);
  }
  __applyPrototyped(mc);
 }

Then we can invoke it with:
 var mcp:MovieClipProxy = new MovieClipProxy(ubSquare.create, container, instanceName, initObj, depth);

and the created MovieClip will now be a ubSquare.

But, ubSquare had a side property and a scale method that are not handled by MovieClipProxy.  I can create a ubSquareProxy Class

import edu.clips.test.MovieClipProxy;
import edu.clips.test.ubSquare;

class edu.clips.test.ubSquareProxy extends MovieClipProxy {
 
 public static function create(container:MovieClip, instanceName:String,
         initObj:Object, depth:Number) {
  if (depth == undefined) depth = container.getNextHighestDepth();
  var mcp:MovieClipProxy = new MovieClipProxy(ubSquare.create, container, instanceName, initObj, depth);

  //define all the ubSquare properties, methods and functions
  mcp["scale"] = function(newScale:Number){
   trace("in ubSquareProxy.scale "+ mcp._mc);
   mcp._mc.scale(newScale);
  }
  mcp.addProperty("side", 
      function(){trace("addProperty getter");return this._mc.side}, 
      function(newSide){trace("addProperty setter");this._mc.side = newSide});
  return mcp;
 }
}

and create it using:

this.mySquare_ubS = ubSquareProxy.create(this, "mySquare_mc", {_x:50, _y:75, _side:10});

Now the questions that I am left with are these:
  1. Is it worth the time to apply this method to my ubPod class with 80 documented properties, 23 methods and 7 event handlers just to capture when the one MovieClip property, _visible, changes?
  2. Is there a way to use __proto__ or prototype or constructor to access the MovieClip's setter for _visible and just change that to broadcast an event?
  3. Should ubSquareProxy extend MovieClipProxy, MovieClip or nothing?

Actionscript 2: Extending MovieClip class without using a Library Object

Thanks to Wouter Verweirder, we are creating all of our classes for the CLIPS project without requiring library objects.

The following is a short example of a class that creates a red square, the size and scale of which can be changed.  The size is changed either through the private property _side or the virtual public property, side.  The scale is changed by a public method, scale().

/**This class creates a square on the stage.
It is purely illustrative.  

*/

class edu.clips.test.ubSquare extends MovieClip {
 //used to register this class as though it was in the Library
 static var symbolName:String = "__Packages.edu.clips.test.ubSquare";
 static var symbolOwner:Function = ubSquare;
 static var symbolLinked = Object.registerClass(symbolName, symbolOwner);

 /**the length of each side, in pixels.  Default: 20 */
 private var _side:Number = 20;
 
 public static function create(container:MovieClip, instanceName:String,
         initObj:Object, depth:Number) {
  if (depth == undefined) depth = container.getNextHighestDepth();
  trace("attaching ubSquare using the manufactured symbolName");
  container.attachMovie(symbolName, instanceName, depth, initObj);
  trace("casting MovieClip as ubSquare");
  //this avoids compiler errors in other classes involving ubSquare
  return ubSquare(container[instanceName]);
 }

 function ubSquare() {
  trace("in ubSquare");
  this.constructor = ubSquare;
  this.constructor.name = "ubSquare";
  this.drawSquare();
 }
 public function onLoad():Void {
  trace("ubSquare loaded");
 }
 public function get side():Number{
  trace("in ubSquare get side");
  return this._side;
 }
 public function set side(newSide):Void{
  trace("in ubSquare set side");
  var numSide:Number = parseInt(newSide, 10);
  if (!isNaN(numSide)){
   this._side = newSide;
   this.drawSquare();
  }
 }
 public function scale(newScale:Number){
  //this will use set side
  this.side *= newScale;
 }

 private function drawSquare():Void {
  //this won't use the set and get functions, since _side is used
  trace("in drawSquare");
  this.clear();
  this.lineStyle(2, 0xFF0000, 100, true);
  this.moveTo(0, 0);
  this.lineTo(0, this._side);
  this.lineTo(this._side, this._side);
  this.lineTo(this._side, 0);
  this.lineTo(0, 0);
  trace("finished drawSquare");
 }
}

and here is the code in the first frame of a new actionscript 2 .fla that uses it.  Note that you have to save the class in a path ending with edu/clips/test/ubSquare.as and set your Actionscript preferences to be able to find it.

import edu.clips.test.ubSquare;

this.mySquare_ubS = ubSquare.create(this, "mySquare_mc", {_x:50, _y:75, _side:10});

trace("tester settting virtual property side");
this.mySquare_ubS.side = 40;

trace("tester calling public function scale");
this.mySquare_ubS.scale(3);

trace("the side ends up being: ");
trace(this.mySquare_ubS.side);

Here is the output:

attaching ubSquare using the manufactured symbolName
in ubSquare
in drawSquare
finished drawSquare
casting MovieClip as ubSquare
tester settting virtual property side
in ubSquare set side
in drawSquare
finished drawSquare
in ubSquare get side
tester calling public function scale
in ubSquare get side
in ubSquare set side
in drawSquare
finished drawSquare
in ubSquare get side
the side ends up being: 
in ubSquare get side
120
ubSquare loaded

In most of our stuff, we don't use setters and getters at all. We initialize the properties by passing them in the third parameter of create, as I did to initially set the side to 10 px  (see Paul's post).  We change properties using a setProp method that would look something like:

public function setProp(prop:String, val, doDraw:Boolean):Void {
 if (doDraw == undefined) doDraw = true;
 this[prop] = val;
 if (doDraw) this.drawSquare();
}

Senocular's FAQs were also very useful when we were trying to figure out how to extend the MovieClip class.

Thursday, March 11, 2010

Regular Expressions in Actionscript 2

Regular Expressions are a powerful method of working with text strings based on dynamic patterns. For example, you might want to extract all the numbers from a string like "Michael travels 550km in 6.2 hours using 40L of gasoline", or replace any occurrence of "INV" followed by an arbitrary number of alphanumerics followed by a space with "- reference removed -".

Regular Expressions are useful in parsing user input or creating grammars for strings.

Flash decided not to support a RegExp class in Actionscript 2, though it is implemented in Actionscript 3, Javascript, and JScript.

Pavils Jurjans
bemoaned this fact and created his own RegExp class for Flash 5, which was encapsulated by Joey Lott into a .as file for Actionscript 2. Pavils also created a very handy tester, which together with websina's page, helped me to get started with regular expressions.


The CLIPS programmers have started to use our version of the class, which has been documented with ZenDocs, and includes a trim, rtrim, ltrim and slightly modified replace method. A simple tester .fla is available to interact with the class.

I probably feel as excited learning this new skill as the fellow in this xkcd.com comic.