Monday, October 24, 2011

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.

No comments: