OOP と トゥイーンアニメーションの連携

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);

こうすることで、アニメーションの終了も、マウスイベントと同様に実装をオーナークラスに任せることが可能になる。コードが冗長になる分、変更や拡張に強くなる。

結論としては、トゥイーンアニメーションは、それ自体をカプセル化して、クラスがコンポジションで保持し、連携はイベントで。といった所かな。あと、作業を分担しているなら、アニメーションを作る人とコードを書く人同士で、仕様についての適切な意志疎通が行えるかどうかだなあ。