速いFlash/ActionScriptチューニング入門
連載インデックスへ
速いFlash/ActionScriptチューニング入門(3)

Flashで怒涛のごときイベント処理を捌きまくる3技

チームラボ株式会社
河北 啓史
2010/7/22

【第1技】使い終わったイベントは削除

- PR -

 不要になったイベントリスナをきちんとremoveEventListenerすることは、イベントをチューニングするうえでの基本中の基本です。なかでもEvent.ENTER_FRAMEのイベントリスナは、使用頻度も多いため、しっかりと管理する必要があります。イベントリスナの有効期限は、removeEventListenerされるか、オブジェクトもしくはリスナそのものが削除(ガベージ・コレクションなど)されるまでです。

 注意しなければならないのは、「addEventListenerした対象がremoveChildされたとしても、ENTER_FRAMEイベントは通知され続ける」という点です。これらをしっかりとremoveEventListenerしておかないと、処理負荷の原因となってしまいます。

 ですから、Event.ENTER_FRAMEやTimerEvent.TIMERなど、継続的に通知され続けるようなイベントに関しては、removeChildされたときや、不要になったときは必ずremoveEventListenerをする癖を付けましょう。

 以下に、その実装例を示します。

addEventListener( Event.ENTER_FRAME, trace );
addEventListener( Event.REMOVED_FROM_STAGE, _onRemoved );

// ステージから削除された際にremoveEventListenerする function _onRemoved(e:Event){ removeEventListener( Event.ENTER_FRAME, trace ); removeEventListener( Event.REMOVED_FROM_STAGE, _onRemoved ); }

注意!
Event.REMOVED_FROM_STAGEとEvent.REMOVEDはまったく別ものです。REMOVED_FROM_STAGEは自分自身がstageからremoveChildされた際にdispatchされますが、REMOVEDは、自分自身であろうと自分にaddChildされた子要素であろうと、stageからのremoveChildであろうとなかろうとdispatchされます。

 適切に削除されず残ってしまったイベントリスナは、目に見えないところでCPUにかなりの負荷を掛けます。removeEventListenerを徹底することは、サービス全体のパフォーマンスだけでなく、ユーザーのほかの作業への影響を減らすことにもつながります。

【第2技】リスナ関数の一括実行

 イベント関連の高速化手法で、特に描画速度に効果があるのが、リスナ関数の一括実行です。特に、Event.ENTER_FRAMEを使って大量のDisplayObjectを同時に動かす場合に効果を発揮します。

 おさらいでも説明したとおり、毎フレームで何かを実行する際にはEvent.ENTER_FRAMEを使います。しかし、大量のMovieClipなどをアニメーションさせる際に、オブジェクト指向的発想でEvent.ENTER_FRAMEを個々にaddEventListenerしてしまうと、かなりの処理負荷となってしまいます。

// 各オブジェジェクトが個々にaddEventListenerするサンプルコード

import flash.display.MovieClip;
import flash.events.Event;

class ExMovieClip extends MovieClip {
public function ExMovieClip():void {
addEventListener( Event.ENTER_FRAME, _onEnterFrame );
}
private function _onEnterFrame(e:Event):void {
x += 10;
}
}

 このような実装自体に問題はないのですが、インスタンスが1000個など大量になってくると、「自分に登録されたリスナ関数を調べて実行する」という処理も大量に実行されてしまうのです。そのため、描画うんぬんに処理負荷が掛かるという話以前に、イベントの処理部分の時間が増加してしまい、結果としてFPSが低下してしまいます。

おのおのにENTER_FRAMEを設定した場合(Flash以外の部分をクリックすると、停止します)

 そこで、おのおのでaddEventListenerを扱うのではなく、「実行したい関数(Listener)を内部的にVector.<Function>などの配列に格納し、少数(可能であれば1つ)のイベントリスナ内でfor文などを用いて一気に実行する」というやり方にすると、イベントの処理部分が大幅に減るため、FPSがかなり改善されます。

// イベントリスナを一括実行するサンプル

import flash.display.*;
import flash.events.Event;

class ExMovieClip extends MovieClip {

// ENTER_FRAMEを共通して扱うためのSprite
private static var _dispatcher:Sprite = new Sprite();
// ENTER_FRAMEを共通して扱うためのSprite
private static var _listeners:Vector.<Function> = new Vector.<Function>();

public function ExMovieClip():void {
// リスナ登録がない場合に登録
if( !_dispatcher.hasEventListener(Event.ENTER_FRAME) ){
_dispatcher.addEventListener(Event.ENTER_FRAME, function(e:Event):void{
var len:uint = _listeners.length;
for( var i:int = 0; i < len; i++ ){
_listeners[i](e);
}

})
}
addEventListener( Event.ENTER_FRAME, _onEnterFrame );
}

// addEventListenerをoverrideし、ENTER_FRAMEの場合は_listenersに登録
override public function addEventListener( type:String, listener:Function, useCapture:Boolean = false,
priority:int = 0, useWeakReference:Boolean = false ):void{
if ( type == Event.ENTER_FRAME) {
_listener.push(listener);
}else {
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
}
}

// removeEventListenerをoverrideし、ENTER_FRAMEの場合は_listenersから削除
override public function removeEventListener( type:String, listener:Function,
useCapture:Boolean = false ):void{
if ( type == Event.ENTER_FRAME) {
for (var i:int = 0; i < _listener.length; i++) {
if ( _listener[i] == listener ) _listener.splice(i, 1);
}
}else {
super.removeEventListener(type, listener, useCapture);
}
}

private function _onEnterFrame(e:Event):void {
x += 10;
}
}

ENTER_FRAMEを一括処理した場合(Flash以外の部分をクリックすると、停止します)

注意!
for文などで一気に処理する場合、addEventListenerのpriorityやuseCaptureの設定を加味しつつ処理させることは非常に難しいので、それらの設定を無視できるような処理で使いましょう。。

 余談ですが、このイベントリスナをまとめるやり方は、TimerEvent.TIMERでも使えます。同一のイベントで、かつ大量の処理が必要なイベントに関しては、このイベントリスナの一括実行を検討するのもいいでしょう。

 次ページでは、MouseEventのチューニングを解説します。

1-2-3

 INDEX
速いFlash/ActionScriptチューニング入門(3) 
Flashで怒涛のごときイベント処理を捌きまくる3技
  Page1
Flash高速化は“仕組み”の理解から始める!
ActionScript 3の「イベント」の仕組み
Page2
【第1技】使い終わったイベントは削除
【第2技】リスナ関数の一括実行
  Page3
【第3技】MouseEventの最適化
小さな処理をいかに少なく、いかに最適化していくか


「デザインハック」コーナーへ


TechTargetジャパン

リッチクライアント & 帳票 フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

キャリアアップ

@IT Sepcial

イベントカレンダー

PickUpイベント

- PR -
もっと見る

お勧め求人情報

ホワイトペーパーTechTargetジャパン

@IT Sepcial
ソリューションFLASH