より大規模なFlashプログラミングのために

タイムラインにスクリプトを書かない事についてまとめてみました。

ゲームなどある程度複雑なFlashのコーディングを行うとき、なるべくオブジェクト指向にそった物になるように、タイムラインにあまりスクリプトを書かず、クラスで処理を行うようにしています。なぜそうしているかや、どうやってやっているかなどを書き出してみました。
一般的にどうしているかは知らないですし、そもそもタイムライン派は少なくなっているようなので、的外れなことを書いているかも知れませんが、参考になれば幸いです。

タイムライン上でのプログラミングの問題点

ActionScriptはタイムライン上でスクリプトが書けるのが手軽で便利なのですが、規模が大きいプログラムを書く場合にはいろいろと問題が起こってきます。まずどんな問題が発生するかを簡単に説明します。

変数や関数のスコープを変更できない

たとえば、タイムライン上で
private var a:int = 0;
と書くと、
1013: private 属性はクラスプロパティの定義でのみ使用できます。
というコンパイルエラーが発生します。関数に対してスコープを指定しても同様のエラーが発生します。指定しない場合はコンパイルエラーは出ませんが、その変数や関数は外部から参照可能になります。
その上addClildされたMovieClipはすべてrootからパスを通せば参照可能であるため、理屈の上ではタイムラインで定義したすべての物はプログラムのどこからでも参照可能になります。つまり、タイムライン上で変数や関数を定義するスタイルは、グローバル変数や関数のみでプログラムを行うのと同じになってしまいます。そのようなプログラムはどこで変数や関数が呼ばれているかの管理が非常に難しくなるため、小規模の開発なら問題ないかも知れませんが、ある程度以上複雑な開発には向いていません。オブジェクト指向の利点の一つであるカプセル化を利用できないと言うことでもあります。

継承ができない

たとえば、ライブラリの中にAというクラスのMovieClipをつくり、そのAというクラスを基本クラスとするBというMovieClipを作ることは出来ません。継承させるには外部に対応するクラスを用意してそれとライブラリ内のオブジェクトをリンケージの設定で対応させ、クラスファイル内で継承関係を定義する必要があります。

このようにflaのみを使ってタイムラインにスクリプトを書くという方法はActionScriptのオブジェクト指向としての機能を著しく損ないます。それゆえこの方法は規模の大きなプログラミングには向いていません。またソースコードの再利用性も低くなります。かといって、タイムラインにスクリプトを書くなと言うことでもありません。変数や関数をタイムライン上で定義してしまうのは確かにスコープなどの問題を引き起こしますが、関数のコールや変数の参照などは比較的問題がないと思います。もちろん、クラスファイルとタイムラインに処理が分散してしまうという管理上の問題がありますが、あるタイミングで処理行いたい時などには、タイムライン上のスクリプティングは非常に便利で処理を大きく簡略化してくれる場合があり、必要以上に忌避することはないと思います。

リンケージの設定

FlashのCSシリーズでタイムラインにスクリプトをあまり書かず、オブジェクト指向の機能を損なわずにプログラムを行うにはライブラリのリンケージを使うのが便利です。リンケージとはライブラリ内のオブジェクトをクラスファイルに対応させる機能です。ここではリンケージについて説明します。

リンケージの概要

ライブラリのオブジェクトを右クリックしてプロパティを選択し、「リンケージ」の項目の「ActionScript用に書き出し」をチェックします。そして「クラス」または「基本クラス」にクラスを指定すれば、そのクラスとライブラリのオブジェクトが関連づけられます。「クラス」と「基本クラス」にクラスファイルを指定する違いは、

クラス:そのオブジェクト自体が指定したクラスになる
基本クラス:そのオブジェクトは指定したクラスを継承したサブクラスとなる

です。たとえば「クラス」の項目に指定した「test.TestClass」の中で、
private function hoge():void{
    trace(“hoge”);
}
と書き、ライブラリのオブジェクトのタイムライン上から関数hogeを呼び出すことは可能です。一方、同じクラスを「基本クラス」に指定した場合ではオブジェクトはサブクラスになるため

1180: 未定義である可能性が高いメソッド hoge の呼び出しです。

というコンパイルエラーが発生します。これを回避するにはアクセス修飾子をprotected以上のスコープを持つ物にします。

また、「クラス」の項目にクラスを指定した場合、クラス名は一意でないといけないため、同じクラスを他のオブジェクトの「クラス」の項目に指定することはできません。
一方で「基本クラス」に指定したクラスは、クラス名を変える限り何度でも「基本クラス」に指定することが出来ます。対応するクラスファイルが無い場合は、Flashがコンパイル時に勝手にクラス定義を作成してくれます。

それゆえ、処理は同じでグラフィックなどだけが違うオブジェクトを複数使うような場合には「基本クラス」の方にクラスを指定した方が便利です。他方そのような使い方をする可能性のないオブジェクトについては、タイムラインに処理を書く場合にもprivate属性でより厳密にスコープの管理が可能な「クラス」の方にクラスを指定する方が良いでしょう。

swf自体にクラスファイルを関連づける場合には ドキュメントクラスという物を使います。ドキュメント自体のプロパティの中で設定することが出来ます。こちらはクラスを直接指定します。リンケージの「基本クラス」に当たるような物は指定できません。

リンケージの注意点

リンケージを行う場合には以下のような点に注意する必要があります。

1.指定するクラスファイルは必ずそのオブジェクトの元のクラスを継承している必要がある。
 ライブラリ内のMovieClipにリンケージするクラスは、MovieClipをextendしている必要があります。他の画像やサウンドについても同様です。

2.タイムラインに配置するオブジェクトはコンストラクタに引数を指定できない。
 タイムライン上に配置したオブジェクトはFlashが自動でインスタンス化するため、引数が指定できないようです。

3.「基本クラス」にクラスを指定し、クラス自体はFlashに自動生成させる場合、「基本クラス」に指定したクラスにはコンストラクタに引数を指定できない。
 Flashに自動生成させるクラスの「基本クラス」に引数のあるコンストラクタは定義できないようです。

4.クラスファイルを作る時、デフォルトパッケージを使わない。
 パッケージはクラスの名前が重複しないように利用する物で、もともとパッケージ名を指定しない、いわゆるデフォルトパッケージの使用は推奨されていません。その上Flashの場合、ライブラリのオブジェクトにリンケージ設定をすると、初期状態ではそれらのクラスがデフォルトパッケージに書き出されます。そのためデフォルトパッケージを使うと気づかない間に勝手にクラスファイルがリンケージがされていた、ということが起こりえます。クラスファイルを作る時は出来るだけデフォルトパッケージを避けた方が無難です。

5.プリローダーが必要な場合は、容量が大きなオブジェクトや、タイムラインに配置するオブジェクトは「1フレーム目に書き出し」にチェックをしない。
 swfの容量が大きくなると、自分自身のロード状況を表示するプリローダーを最初につけることがありますが、リンケージの設定で「1フレーム目に書き出し」をチェックしていると、これが正常に動かない場合があります。Flashではすべてのオブジェクトはフレームに書き出した状態でないと使うことが出来ません。たとえば、Hogeというクラスを持つオブジェクトをタイムラインに配置せず、リンケージの設定で「1フレーム目に書き出し」を外していると、そのオブジェクトはタイムラインに書き出されていないためスクリプト上で「new Hoge()」と書くと実行時エラーが発生します。これを避けるためにリンケージの設定の「1フレーム目に書き出し」はデフォルトではチェックが入っています。しかしそうすると、1フレーム目の書き出しの後にswfの処理が行われるため、プリローダーが正常に動かなくなる場合があります。これを避けるには、タイムライン上にのみ配置しているオブジェクトをリンケージしている場合には、いずれにせよその時点でフレーム書き出しが行われるので、「1フレーム目に書き出し」のチェックを外します。タイムライン上に配置せず、プログラム上でコンストラクタを使ってインスタンスを作成するオブジェクトについては、「1フレーム目に書き出し」のチェックを外した上で、ローダの実行が終わった直後など、まだそのオブジェクトを使っていないフレームでタイムライン上に手で配置しておきます。画面外に配置したり、そのようなオブジェクトばかりまとめたMovieClipを作っておいて、それのアルファを0としておけば目立たないでしょう。サウンドの場合はタイムラインに配置した上で、再生を止めておく必要があります。また、そのような用途で使うことを前提として、コンストラクタ内の処理を書く必要があります。どのフレームで書き出しが行われているか、どのオブジェクトの容量が大きいかは、パブリッシュ設定の「サイズレポートの作成」にチェックを入れていると簡単に確認できます。
 これらの設定をプログラムが完成した後で行うのはかなり大きな変更となるので、出来るだけ最初からプリロード処理の事まで見越してプログラミングした方がよいでしょう。または、swfを1ファイルで作成するのをあきらめて、メインのswfをロードするswfを別に作った方がよいかも知れません。ただし、海外のゲーム投稿サイトなどでは複数ファイルを受け付けない場合も多いので、もしどこかに投稿するゲームを作っているなら注意が必要です。

6.CS3などではリンケージするライブラリのオブジェクト内で使っているクラスをクラスファイルでimportしておく必要がある
 CS3で、たとえばTextFieldを含むMovieClipをリンケージする場合、クラスファイルでTextFieldをimportしていないとコンパイルエラーが出ます。CS5は出ないようです。

7.なるべく処理がクラスファイルとライブラリ内のオブジェクトに分散しないようにする。
 リンケージを行っても、MovieClipのタイムラインに処理を書くことは可能です。しかし、クラスファイルとタイムライン双方にスクリプトを書くと管理が面倒になります。なるべくクラスファイルに処理を書き、タイムラインには関数のコール程度しか書かないようにしたほうが良いでしょう。また、クラスファイルとライブラリ内のシンボルの対応がわかりやすくなるように気をつけましょう。私の場合はクラスのパッケージ構成とライブラリのフォルダ構成が大まかに対応するようにしています。

8.非常に大きなプログラムの場合にはメモリ不足のエラーが発生する場合がある。
 これはリンケージの問題という訳ではないかも知れませんが、クラスを数百以上使うような非常に大きなプログラムの場合、Flashが利用しているjavaの仮想マシンのメモリが足りなくなりコンパイル時にエラーが出る場合があります。大変まれな事象ですが発生してしまった場合にはjavaのメモリの割り当てを増やすことで回避できます。

まとめ

リンケージの機能を使ってクラスファイルを利用したプログラムをすると、最初に指摘した問題点がすべて解決されます。まず、クラスファイル内ではprivateやpublicといったアクセス修飾子を使えますから、カプセル化が可能です。もちろん継承もできます。他にもスクリプトがテキストファイルになるので、FlashDevelopやFDTといった強力なエディタも使用可能となり、diffやgrep、バージョン管理システムなど、プログラム開発でよく使われてきたツールも使いやすくなります。ソースコードの再利用もより簡単になります。
もちろん、リンケージには注意すべき点もあり、ある程度慣れが必要です。小規模な開発ならタイムラインに処理を書いた方がずっと手軽です。
しかし、FlashのCSシリーズで大規模なプログラムを作成するなら、リンケージ機能とクラスをうまく使うことをおすすめします。

Share

コメントする