Combining the Timeline with OOP AS3 in Flash

There seem to be two types of Flash users: those who do virtually everything on the timeline, with very little code, and those who do virtually everything through code, as if the timeline doesn't exist. In theory, there ought to be a third type: those who are able combine the timeline with well-written OOP code. However, if those people exist they are either not writing on the web or their writings are not easy to find with a search engine.

The nice thing about using the timeline in Flash is that it gives you access to the stage, where you can easily work with Display Objects visually, as FutureWave/Macromedia intended. Additionally, the frames of the timeline allow developers to organize their assets "temporally:" i.e. things that appear first appear in Frame 1, things that appear second appear in Frame 2, etc. It just makes sense to use the timeline in Flash.

Except there's this "new" idea in Flash when you use Actionscript 3, and that's Object Oriented Programming (OOP). In OOP, frame scripts are frowned on, because they make it difficult to find and maintain critical code. So your main timeline will essentially be a Class, and many of your dependent Display Objects will also have their own associated Classes. By default, these Classes don't know anything about what the Flash player is doing on the timeline, which creates a basic problem–figuring out how to reference assets that don't exist on the timeline in Frame 1. Once you get past that hurdle, there are a lot more advantages to using the timeline than you'd think, and these can have real implications for the performance and load time of your application.

Disclaimer: Because I haven't found anywhere written down where someone is saying "this is the best practice way of handling the problem," the suggestions below are the results of my own experiments. Your mileage may vary.

Methods of obtaining references to Timeline objects

Listen for Added Event

When you have an instance variable that refers to an object that does not exist on Frame 1, that instance variable will be null until the object has been created and placed on the stage in whatever frame it first appears.

So you would think that you could just listen for the addedToStage event and do your object setup from there. The problem with this is that addedToStage doesn't bubble, so in order to listen for addedToStage, you have to have a reference to the object.

The good news is that the added event does bubble, so you can add a listener to "this" that will catch objects as they are added. The bad news is that, even though they've been added, they have not yet been added to the stage. So the instance variable will still be null. The object exists, but the Flash player has not yet put a reference to it into the instance variable, which, after all, refers to an object on the stage. Not to worry...the target property of the added event contains a reference to the object that you can use to do further work on it.

At this point, you have several options on what to do with your newly referenced object:

Use a switch() statement to perform setup based on getQualifiedClassName()

Switch statements perform better than if/then, so my preference is to set up constants for the Classes I am interested in using flash.utils.getQualifiedClassName(). I then compare the name of the Class to the name of the Class the new instance belongs to and route the new instance to appropriate setup logic based on what sort of object has been created. I usually have a separate setup function for each class that takes an instance of that class as a parameter.

Use "is" to determine what kind of object has been created

Another variation on this, which is probably more type safe than what I use, is to use the "is" keyword to determine what class has been instantiated. The down side is that you wind up with a series of if/then statements, which don't perform as well as one switch statement. Both this technique and the getQualifiedClassName() technique work well if you just have one instance of each Class, but what happens if you have several?

Use a switch() statement to set up the object based on name

Each DisplayObject has a "name" instance variable, which, not coincidentally, is the same as the instance name. So if you have a MovieClip instance, myMC, then inside your added event handler event.target.name will be "myMC." You can use this to perform different setup depending on what instance it is.

Note that if your multiple objects extend the same base class, but have different names in the "Class" field of the Symbol properties, they will be different Classes for purposes of a Class-based switch statement. If you switch by Class and then by name, you'll need to keep this in mind.

Listen for enterFrame Event

If you mainly tend to advance from one frame to another using goToAndStop, the temptation is to add an enterFrame event listener to wait for the next frame to render, then set up the objects you know ought to be constructed in a particular frame, removing the event listener in the handler. If you're using Flash Player 10 on your development machine, this will work–on your machine. It will not work on machines that have Flash Player 9.

The reason is that goToAndStop() forces a render before Enter Frame in Flash Player 10, but not in Flash Player 9. If you know that you are only targeting Flash Player 10, you have much better events to hook into than enterFrame() anyway (see the Order of Operation reference at the end of this file. Or here.).

Use a Frame Script to dispatch an event

The whole object of this article is how to use the timeline while complying with best practice in Flash, so I'm not going to go into details on this one. If you're interested, see the 5 Ways to Cheat at Actionscript reference at the end of the file. Or here. Note that if you're planning to use addFrameScript(), this is a Flash Player 10 feature.

Dispatch a bubbling event from the object

As I mentioned above, you don't need to have a reference to an object that dispatches a bubbling event in order to listen for that event. In theory, you could add an event listener in the object's constructor that listens for addedToStage and dispatches an event. I am not a fan of adding another event when there's an existing one that can get me most of the way there, so I think I'll stick with the added event.

Use a getter/setter pair instead of a public variable for your instance variable

To be honest, I only thought of this the day after I'd completed the rest of the article, and I wish I'd thought of it first. If you use a getter/setter pair, then the setter (of course) has a reference to the newly created instance just as soon as that instance is populated into the public property, and you can do any initialization logic to it there. I'm sure there are still cases where you might want to use the other methods, so I'm leaving them in place, but this one is by far the simplest and easiest.

Cleaning up timeline objects

Another thing you need to think about when you're letting the Flash Player manage creating and destroying your objects for you is that you need to guard against memory leaks caused by keeping objects in memory that have no further use. After all, if you run through that same section of time line again, Flash is not going to reuse the same objects again–it will create new ones for you.

So the last thing you want is to have listeners and other references keeping your objects from being garbage collected. How can you detect the class is no longer being used?

Listen for Removed Event

It turns out that the added event has a bubbling counterpart on the teardown side, removed. You can listen to this event to determine that your class is just about to be removed from the display list. The nice thing about the removed event is that it is fired while the instance variable is still populated by a reference to the object, so you can do things like if (e.target=myObject){myObject.removeEventListener("someEvent", someHandler)}. Since removed is a bubbling event, you can have a global listener that doesn't have to be removed from each object.

Check for null value in setter

If you use a getter/setter pair, then when the value is nulled after being set, you can remove event listeners on your private copy of the variable. This variable will still contain a reference to the instance that has been removed and is in the process of being set to null. Of course, once you have removed the event listener, you should set the private variable to null to ensure you don't "surprise" the Flash Player.

Self-cleaning Classes

I usually set up my classes intended for use with the timeline to add an event listener for removed on themselves, which calls a cleanup() function on remove. This function will remove any event listeners and other dependencies internally. Potentially, I could override addEventListener to keep a list of all listeners added and remove them, but I think it's better to allow objects that add the listeners to also remove them.

Note that if you sometimes instantiate a Class through code and other times instantiate it through the time line, you'll probably want to modify this concept to make sure that objects only remove internal dependencies if they are not going to be reused.

Advantages to Using the Timeline

This seems like a lot of work, though for those of us who like to lay things out visually it's worth it. But that's just the beginning of the advantages you gain from making use of the timeline, even if you're a total code head.

Lazy Loading of Class Definitions

Have you ever wondered what would happen if you unchecked "Export in Frame 1" that automatically gets checked when you select "Export for Actionscript" on your symbol definition? If you're instantiating everything through ActionScript, your whole project would blow up, because there would be no other place it could be exported.

But if you're using the timeline and you uncheck that box, what you'll find is that your movie loads somewhat faster (how much faster will depend on how large the assets and code are for your class). Other than that, your file will execute perfectly normally, because Flash compiled the definitions for your classes on the frames where they were first used. Yes, you read that right–if you use the timeline, the load time for your movie will be amortized over all the frames of the movie, rather than loaded up front. If you play your cards right, you can probably entirely avoid the need for a preloader, because you may not need a lot of assets just for Frame 1.

Avoiding "Export for Actionscript"

If the timeline is instantiating and positioning your assets, you may find that you don't have to make so many of them into scriptable assets, just so you can get hold of them to create new ones. This further increases the efficiency of how the movie loads at runtime. The Flash compiler does a good job with this stuff–let it.

This also works for fonts. If you've created a font definition in the library and you're not using it through ActionScript, you don't have to export it for ActionScript. The Flash IDE will compile the font in at the right place to be useful, without being loaded too early.

Considerations for Using the Timeline

One thing to keep in mind is that, no matter how your objects are created, instantiation is the most expensive part of the object life cycle. So if you're going to go through the same segment of timeline more than once, you may want to consider either instantiating your classes through code or using the "visible" property of different objects to get an effect that's similar to changing frames without asking the Flash Player to create and destroy those objects multiple times.

Another thing to consider is that there are a few "edge" cases where added fires, but a new object may not have been instantiated, including where it is masked or unmasked, or the layer is changed. You should be aware of the possibility that the methods outlined above may need modification in cases like these.

Additional References

Here are some cool references I found while researching this blog post:

About this Entry

This page contains a single entry by Amy Blankenship published on April 7, 2010 7:12 AM.

QOW: Has the open sourcing of Flex affected your adoption? was the previous entry in this blog.

Debatable Future for Flash CS5 iPhone Exporter? is the next entry in this blog.

Find content using the provided navigation or look in the archives to find all content.

Authors

Archives

This content archive is licensed under a Creative Commons License.