Optimising symbols that rarely change
One of the biggest drains in Flash are symbols that rarely change, but need to be re-rendered frequently. The most common examples are scene-backgrounds, as they occupy the whole scene, and are partially redrawn every time something on top of them (such as characters etc.) are updated, or even worse can be scrolling backgrounds that update fully every time they move!
Fortunately there is a dead simple solution to this, in the first frame of your symbol, simply add the following:
- Code: Select All Code
this.cacheAsBitmap = true;
Quite simply, this causes the symbol to be rendered into an image, removing the need to continually redraw the vectors. You should use this sparingly, as it won't improve everything, and for rotating or transforming objects it can look significantly worse, however for backgrounds it can greatly reduce the rendering cost, and speed up side-scrollers, even ones where the background is static (doesn't move).
Oh, and don't use this on animating backgrounds! If you have an animated background then try to separate out the animating and non-animating pieces into separate symbols, and apply this feature to the non-animating pieces separately. This will give you the benefit of caching, with the effect of animating. Remember as well that for our purposes, objects that are simply moving without any rotation or transformation are "static", but should be cached separately if they are not moving with the other objects. For example, the illusion of a realistic cloud-layer may use three layers of clouds, one near, one normal, and one far. All three of these can be cached, but should be cached separately, as they will move at different speeds.
It's worth noting that fancy effects such as filters (blur, etc.) will automatically transform any affected symbol(s) to bitmaps. For this reason, if you are using such filters then it is worth being very careful what you use them on, as any animated objects being blurred will kill performance very easily; for such objects it's a good idea to pause their animation (using the stop() function), then perform the blur, and use play() to start them again once the blur effect has been removed.
The other major advantage of this often overlooked feature is that you don't need to do anything to your artwork! You can work with vectors just as normal, and Flash itself will produce the bitmap automatically, so you get the benefits of a vector's low file-size, and crisp-detail, but can keep the performance nice and manageable.
Optimising your vectors
A simple option that not everyone is aware of. After opening a symbol and selecting some or all of its contents, simply go to Modify -> Shape -> Optimise. This handy little tool will attempt to simplify any vectors, while retaining as much of the original shape as possible, usually resulting in a bit of a performance boost by making the shape easier to draw. It's generally good-practise to do this on artwork as you work on it in Flash, and you'll need to try out different settings to get the right balance of optimisation and graphical detail, as optimising all the way can ruin your object(s).
As a general rule, if you're using the above cacheAsBitmap feature, then you don't need to do this. However, for symbols that are commonly used, and cannot be cached as a bitmap (because you want them to rotate, transform etc.) then it's worth trying the optimise tool to see if you can get any improvement.
The following are some useful improvements, aimed mainly at optimising ActionScript 2, as ActionScript 3 is built more like a "proper" programming language so these issues are usually covered in most good existing AS3 tutorials and introductions to the language.
Polling in ActionScript
The most common method of creating updating objects in Flash, such as Krystal's changeable mess-layers and armour, is to use a simple loop that checks every frame to see if a variable has changed, and respond accordingly. When a script is periodically checking the status of a value, we call this polling, and it can be extremely costly in terms of performance.
While it's a super-simple method to code, and nice and easy to understand, every single item you add to your Flash that polls for values will increase the cost on performance. So if you have five objects checking then that might not be too bad, but if this increases to twenty objects, then suddenly you're spending four times as much processor power on checking values that don't change all that often! The result is that the more complex your Flash becomes, the more and more checks are being performed, which can result in a lot of wasted time.
Event-based updates
The way to address this is to essentially flip the problem around; instead of your symbols asking for data all the time, we simply give them the data when they need it. This is dead simple in theory, but unfortunately is fairly complex in practise, as your script needs to know which objects are in the scene, and have some way of accessing them. Fortunately some of this can be boiled down into simpler code that, while not really best practise, can start to improve performance by eliminating unnecessary polling.
To start with, in ActionScript 2, you will need a link to the EventDispatcher, now, some guides recommend more complex methods, but the following is a nice simple one. Somewhere at the start of your flash (such as a "play" button on your pre-loader), stick the following:
- Code: Select All Code
_root.eventSource = {};
_root.eventDispatcher = EventDispatcher.initialize(eventSource);
You now have an event dispatcher! Simple right? Now, in each symbol you have that needs to know about some important value, simply add something like this in the first frame, making sure that the first-frame does not loop, or if you prefer you can use the onClipEvent(load) event. In this example we're doing a simple handler for a character to detect when/how they are moving:
- Code: Select All Code
_root.eventSource.addEventListener("MovingEvent", this); // Copy this line for each event type you're interested in
this.onUnload = function() {
_root.eventSource.removeEventListener("MovingEvent", this); // Copy this line for each event type you're interested in
}
this.MovingEvent = function(event:Object) {
if (event.data == "left") gotoAndPlay("left");
else if (event.data == "right") gotoAndPlay("right");
else gotoAndPlay("idle");
};
this.MovingEvent({ type: "MovingEvent", data: "idle" }); // Pass in a fake event to make sure the symbol starts with the correct data, point this to a variable for even better results
This is understandably confusing looking, but quite simply the first two lines add the symbol as a listener for a type of event which I've named "MovingEvent", and then creates a function that will handle the event once received. The last line is used to trigger the event right away, so that a moving character will appear to move immediately, rather than waiting until an event is received.
Now, this is all well and good, but to make use of it, we need to actually trigger some events! So, in our movement code we might do something like the following:
- Code: Select All Code
if (!player.movingLeft) {
_root.eventSource.dispatchEvent({ type: "MovingEvent"; data: "left" });
}
This will be received by our character symbol(s), and cause an update accordingly! Suddenly we've removed 20+ checks per second, and replaced it with code that only triggers when an update is actually needed! The if statement is used to ensure we only send events when they're needed, which can improve performance even further.
Detecting Key Presses and Moving Symbols
A common use of the EnterFrame event is to detect key-presses and behave accordingly; however, there are some issues with this, as doing-so every frame can be wasteful when several frames will usually elapse without changes in key-state, so constantly testing the state of each key that you care about can be a drain on performance, especially if you have a complex control system.
In such cases; use of the onClipEvent(EnterFrame) should be restricted to objects that ware capable of movement, testing the simplest possible movement parameters like so:
- Code: Select All Code
onClipEvent(EnterFrame) {
if (this.isMoving) {
var speed = 0;
if (this.movingLeft) speed -= 5;
if (this.movingRight) speed += 5;
if (speed) this._x += speed;
else this.isMoving = false;
}
}
While triggering this event is still a small burden, it's a necessary one in this case, and is greatly reduced by the simple this.isMoving check, which allows the event to exit as quickly as possible. An alternative that is more complex, is to have a central source with an EnterFrame event, that moving objects can attach themselves to, and will in turn have movement commands sent to them on-demand. This way an object that is not moving can simply unregister itself, resulting in it costing nothing while it sits idle; you may wish to consider this method if your game has a lot of moving objects that are capable of being idle.
In any event, once the EnterFrame event has been simplified or removed, we can focus on getting the key presses that we need, and this couldn't be simpler! In much the same way as event-based dispatch of symbol update to replace polling, we can simply do the same thing but with the Key object, like so:
- Code: Select All Code
Key.addListener(this);
this.onKeyDown = function() {
if (Key.isDown(Key.LEFT)) player.isMoving = player.movingLeft = true;
if (Key.isDown(Key.RIGHT)) player.isMoving = player.movingRight = true;
}
this.onKeyUp = function() {
if (!Key.isDown(Key.LEFT)) player.movingLeft = false;
if (!Key.isDown(Key.RIGHT)) player.movingRight = false;
}
Of course this is grossly over-simplified, but hopefully you get the idea! This will likely need a good cover on collision detection, but that's a very complex subject!