AS1 OOP: Inheritance
by senocular
ASBroadcaster and Events for Class
Instances
Previously, in using static properties of a class, we were
able to keep track of the number of instances that were
created for a specific class. That can be expanded on by not
only counting instances, but controlling them. If, for each
call of the class constructor, you were to place that
instance within an array defined in that constructor,
events, or calls to instances’ methods, could then be sent
to each instance in that array just by cycling through and
calling the appropriate method for each. There is, however,
a better way. That way is through using ASBroadcaster.
ASBroadcaster is an inbuilt object in Flash that gives
the capability of sending events to a list of listeners of
that event to specific objects in your movie. Some objects,
like Key and Stage, are already defined to behave this way.
ASBroadcaster lets you create your own broadcaster objects
which have their own listeners and can send them all the
event of your choice. With OOP, you’d typically have a class
broadcast events to all instances of that class.
A common mistake is assuming that such functionality can
be achieved by calling a method directly from a prototype.
That, of course is not the case. But why not? All instances
have those prototype methods? If I call it from the
prototype, won’t it call it for all of those instances?
Nope. Remember, the prototype object is just a shared
object. It’s a normal object in every other respect. Calling
a method from that object would be no different than calling
a method from some other single class instance. It will only
run for that one object and no others. This is even the case
if you try something like class.prototype = new MovieClip().
It’s just not going to happen. ASBroadcaster is the way to
go.
If you don’t already know its use, you can find more
about what ASBroadcaster is and how it works
here. Otherwise its time to jump right in and start
using it.
For a class making full use of ASBroadcaster
capabilities, it would need to be a class where every
instance needs to have a method called for it at any one
time for some particular reason. One example might be giving
raises to all employees of a company. Each employee has
their own specific weekly income. A raise for every employee
would mean an increase would have to be applied to each one
of those incomes in every employees of the company.
- // employee class definition
- Employee = function(weeklyIncome){
- this.weeklyIncome = weeklyIncome;
- // make each created instance a listener of
- // the class constructor
- Employee.addListener(this);
- };
- Employee.prototype.getPayCheck = function(){
- trace("Cha-ching! $"+ this.weeklyIncome);
- };
- Employee.prototype.getRaise = function(percent){
- this.weeklyIncome += this.weeklyIncome*percent;
- };
- // initialize Employee to be able to send events to
its listeners
- ASBroadcaster.initialize(Employee);
- Employee.giveRaise = function(percent){
- // send all listeners the getRaise event and
- // pass to it the desired percent argument
- this.broadcastMessage("getRaise", percent);
- };
- humanReceptionist = new Employee(200);
- monkeyTypist = new Employee(10);
- humanReceptionist.getPayCheck(); // traces Cha-ching!
$200
- monkeyTypist.getPayCheck(); // traces Cha-ching! $10
- // give all employees a 5% raise
- Employee.giveRaise(.5);
- humanReceptionist.getPayCheck(); // traces Cha-ching!
$210
- monkeyTypist.getPayCheck(); // traces Cha-ching! $10.5
Using a static method of the Employee constructor, all
instances of that class can then be sent a similar event, in
this case, getRaise. If you want to be more consistent with
other Flash events, you could instead use a method name such
as onGetRaise, though it’s not necessary. The “on”, does
help you see that the method is an event method, so it might
be something to consider in naming your events.
Now, if you didn’t already realize this, normal inbuilt
Flash event’s aren’t coming to the class above. As it exists
now, the only event any employee will ever receive is the
getRaise event. The onEnterFrame event, for example, isn’t
even close to being called for any employee. In fact, its
reserved solely for movieclip instances. Any object you
make, either generic or from one of your classes, will not
be able to receive the onEnterFrame event on their own as
movieclips do. With ASBroadcaster, though, you can get
around that.
All you need is a movieclip host – some movieclip willing
to spread the love of its onEnterFrame event down to the
lowly objects of your choice. If none of your current
objects are willing to volunteer, its ok. We can make a new
one just for this purpose with createEmptyMovieClip. Just
make sure you have a safe depth to keep it. The idea is to
take the onEnterFrame received by the host clip and
broadcast it to a list of listeners who need to receive it,
those listeners being those “objects of choice”. These
objects can be either individual object instances or, better
yet, especially if all instances of a class are to receive
an onEnterFrame event, class constructors. In using class
constructors, you would have a static onEnterFrame method of
the class constructor which would then be used to broadcast
its onEnterFrame method to its own instances.
[ onEnterFrame from movieclip to class to class instances ]
This adds an extra step in the onEnterFrame execution,
but it allows for individual classes to better regulate
their own instances and the onEnterFrame events being called
on them.
The following example has a single class which is defined
as a listener of the onEnterFrame broadcasting movieclip
created in depth 1000 of the current timeline. The event
from the movieclip is sent to this class which then uses its
onEnterFrame method to send it to all of its own listeners –
or all the instances created by class.
- // setup a movieclip to send onEnterFrame events to
objects
- this.createEmptyMovieClip("onEnterFrameEvent",1000);
- ASBroadcaster.initialize(onEnterFrameEvent);
- onEnterFrameEvent.onEnterFrame = function(){
- this.broadcastMessage("onEnterFrame");
- };
- // define a child class
- Child = function(name){
- this.name = name;
- this.ageInSeconds = 0;
- // child will be setup as a broadcaster. Each
instance
- // can receive events when added as a listener
- Child.addListener(this);
- };
- // method to be run every frame for instances
- Child.prototype.grow = function(){
- this.ageInSeconds += 1/20; // assuming movie at 20
fps
- trace(this.name + "’s age: "+ this.ageInSeconds); //
traces every frame
- };
- // make child a listener to the onEnterFrameEvent
movieclip
- // so that it will receive an onEnterFrame event
- onEnterFrameEvent.addListener(Child);
- // initialize child to be able to broadcast events
- // so it can project its onEnterFrame to its instances
- ASBroadcaster.initialize(Child);
- // define an onEnterFrame
- Child.onEnterFrame = function(){
- // send the grow event to all child
- // instances, and do so every frame
- this.broadcastMessage("grow");
- };
- // make new instances of child and watch them grow
- son = new Child("Sam");
- daughter = new Child("Jessica");
- /* Example output:
- Sam's age: 0.05
- Jessica's age: 0.05
- Sam's age: 0.1
- Jessica's age: 0.1
- Sam's age: 0.15
- Jessica's age: 0.15
- Sam's age: 0.2
- Jessica's age: 0.2
- */
Note that aside from the broadcasting movieclip, the
onEnterFrame event doesn’t even have to be named
onEnterFrame anymore. In fact, here, the child class sends
the “grow” event to all instances as opposed to an
“onEnterFrame” event, which it essentially is.