OOP を使って Flash サイトを制作するとき、簡単なアニメーションならば ActionScript で書いてしまう事が多い。でも、複雑なトゥイーンアニメーションを表示させる部分も必ずあるわけで……。トゥイーンアニメーションこそが Flash の面白い部分であり強みなのだけど、OOP で書いたコードと トゥイーンアニメーションは、結合・連携がなにかと面倒だ。
また、MovieClip の Object 的な「何でもできる」部分が扱いづらい。なので、僕は MovieClip には、単なるアニメーションの表示と、マウスのイベントを受け取るだけの View としての役目のみを割り当てることにしている。中途半端に MovieClip 内に変数や関数を持たせると保守の範囲が広がってしまう。
その延長で、MovieClip のサブクラスや Object.registerClass 等も使わない。以下、EAS2 の Chapter13 (MovieClip Subclasses) より。
MovieClip subclasses are an anigmatic aspect of object-oriented Flash development. As we've seen, given the option, it's often better to use composition to associate a class with a movie clip symbol rather than to extend MovieClip.
MovieClip を継承するより、コンポジションで持たせた方が良い。と言っている。「継承よりコンポジション」は OOP の原則にも当てはまる。
例えば「犬」を Flash でモデリングする場合、MovieClip の継承版だと
class Dog exnteds MovieClip { //走る public function run():Void { //アニメーションを再生 this.gotoAndPlay("RUN"); } }
となる。一方、コンポジション版だと
class Dog { private var dog_mc:MovieClip; //走る public function run():Void { //アニメーションを再生 this.dog_mc.gotoAndPlay("RUN"); } }
となる。表示部分をプロパティとして保持することで「犬」のグラフィックの変更が容易になったり、場合によっては MovieClip ではなく、TextField で「犬」を表示させることも可能になる。
では、上記のコンポジション版「犬」クラスで、run メソッドを呼び出して (複雑な) 走るアニメーションが再生されたはいいけど、アニメーションの終了をオーナークラスが確認するにはどうすればいいのか?つまり、冒頭で書いた OOP と トゥイーンアニメーションの連携の問題になる。
1 つの解決方法は、MovieClip に owner 変数を割り当てて、MovieClip 内のアニメーション終了のフレームからオーナークラスのメソッドを呼ぶやり方。「犬」が走った後に必ず鳴く仕様、と、偉い人が決めたとする。
class Dog { private var dog_mc:MovieClip; //走る public function run():Void { //オーナー変数を割り当てる this.dog_mc.owner = this; //アニメーションを再生 this.dog_mc.gotoAndPlay("RUN"); } //鳴く public function bowwow():Void { //ワンワン } }
dog_mc 内、アニメーションの終了フレームに、
//オーナークラスの bowwow メソッドを呼ぶ this.owner.bowwow();
この方法で意図する通りに動くけれど、dog_mc とオーナークラスの関係が密結合になる、という副作用が生じる。もし、走った後に bowwow メソッドを呼ばない仕様に変更されたら辛い。
では、2 番目の方法。こちらは EventDispatcher を使って、MovieClip が、アニメーションの終了イベントを発生させる。
class Dog { private var dog_mc:MovieClip; //走る public function run():Void { //dog_mc をイベント配信可能にする EventDispatcher.initialize(this.dog_mc); //アニメーションの終了を委譲する this.dog_mc.addEventListener("onRun", Delegate.create(this, bowwow)); //アニメーションを再生 this.dog_mc.gotoAndPlay("RUN"); } //鳴く public function bowwow():Void { //ワンワン } }
dog_mc 内、アニメーションの終了フレームに、
//イベントを作成 var event:Object = new Object(); event.type = "onRun"; //イベントを配信 this.dispatchEvent(event);
こうすることで、アニメーションの終了も、マウスイベントと同様に実装をオーナークラスに任せることが可能になる。コードが冗長になる分、変更や拡張に強くなる。
結論としては、トゥイーンアニメーションは、それ自体をカプセル化して、クラスがコンポジションで保持し、連携はイベントで。といった所かな。あと、作業を分担しているなら、アニメーションを作る人とコードを書く人同士で、仕様についての適切な意志疎通が行えるかどうかだなあ。