初級
サンプルファイル(約4.59MB)
本連載ではActionScript 3.0の初心者の方を対象に、ActionScriptを使って表現を広げるためのテクニックを紹介していきます。ActionScript 3.0初心者向けに考え方や原理を解説するという目的のため、基本的には次のようなスタイルをとります。
・フレームアクションを使う。
・ActionScriptライブラリは使わない。
第1回 スクリプトによるアニメーションの基本01
第2回 スクリプトによるアニメーションの基本02
第3回 複数のオブジェクトの制御方法
連載第3回ですが、今回の内容に入る前に少し先のことに触れておきます。第1回と第2回では、移動のアニメーションについて扱ってきましたが、それに加えて今後数回の内容を組み合わせることで「複数のオブジェクトのレイアウトを変化する」コンテンツを作成する予定です。
オブジェクトを元の位置から変化後の位置に移動してレイアウトを変化させる際に、移動のアニメーションを行ないます。その際、複数のオブジェクトを移動させることになるので、複数のオブジェクトを制御する必要が出てきます。今回と次回は複数のオブジェクトの制御について扱っていきます。複数のオブジェクトの制御にフォーカスを当てるために、その他の部分(アニメーションなど)のスクリプトは便宜上簡単なもので代用します。
まずは、2つのオブジェクトを用意し、それぞれにenterFrameイベントハンドラを使用する方法を解説します。以下のサンプルでは、ボタンクリックで2つのオブジェクトの回転/停止を制御します。
スクリプト:03_01.fla
//メインタイムライン:フレームアクション
//▼ボタンのイベント処理
play_btn.addEventListener(MouseEvent.CLICK, play_btn_clickHandler);
stop_btn.addEventListener(MouseEvent.CLICK, stop_btn_clickHandler);
//▼関数定義
//clickイベントハンドラ定義
function play_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション開始
star1_mc.addEventListener(Event.ENTER_FRAME, star1_mc_enterFrameHandler);
star2_mc.addEventListener(Event.ENTER_FRAME, star2_mc_enterFrameHandler);
}
function stop_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション停止
star1_mc.removeEventListener(Event.ENTER_FRAME, star1_mc_enterFrameHandler);
star2_mc.removeEventListener(Event.ENTER_FRAME, star2_mc_enterFrameHandler);
}
//enterFrameイベントハンドラ(★今回の焦点)
function star1_mc_enterFrameHandler(eventObj:Event):void {
star1_mc.rotation += 5;
}
function star2_mc_enterFrameHandler(eventObj:Event):void {
star2_mc.rotation += 5;
}
この方法はオブジェクトとイベントハンドラが1対1の関係になっているので理解しやすい構造ではありますが、オブジェクトの数に比例してイベントハンドラも増えてしまいます。そのためスクリプトコードの行数が増えやすい形といえます。特にこの例のように、イベントハンドラの処理内容が同じでインスタンス名のみ異なる場合などはなおさらです。
イベントオブジェクトのtargetプロパティを使うと、複数のイベントハンドラを効率よく制御できることがあります。サンプル1のように基本的に同じ処理内容でインスタンスだけが異なる場合などはその代表例です。まずは、イベントオブジェクトの基本を確認しましょう。
イベントハンドラは通常の関数と異なり、イベント発生時にFlash Playerが実行します。その際Flash Playerは、発生したイベントに関する様々な情報を持ったオブジェクトを引数として関数に渡します。この「発生したイベントに関する様々な情報を持ったオブジェクト」を「イベントオブジェクト」と言います。
イベントオブジェクトが保持する情報は、発生したイベントの種類によって異なります。例えば、マウス操作によって発生するマウスイベントでは、イベントが発生した時のマウスカーソルの座標位置情報などが含まれます。キーボードのキーを押したり放したりした時に発生するキーボードイベントでは、どのキーが押されたかといった情報が含まれます。
このようなイベントごとの特有の情報の他にどのイベントでも含まれる共通の情報もあります。その1つに、発生したイベントの対象となるオブジェクト(イベントターゲット)を表す情報があります。分かりやすい例では、クリックイベントが発生した場合のクリックされたオブジェクト(ボタンインスタンスなど)がイベントターゲットにあたります。
enterFrameイベントのイベントオブジェクト
| プロパティ | 説明 |
|---|---|
| type | イベント名を表す文字列 |
| target | イベントターゲットとなるオブジェクト |
clickイベントのイベントオブジェクト
| プロパティ | 説明 |
|---|---|
| type | イベント名を表す文字列 |
| target | イベントターゲットとなるオブジェクト |
| localX | イベント発生時における、マウスカーソルのイベントターゲットから見たX座標 |
| localY | イベント発生時における、マウスカーソルのイベントターゲットから見たY座標 |
| stageX | イベント発生時における、マウスカーソルのグローバルなX座標 |
| stageY | イベント発生時における、マウスカーソルのグローバルなY座標 |
| shiftKey | Shiftキーが押されているかどうか |
※白地は全てのイベントオブジェクトで共通のプロパティ。グレー地はマウスイベント特有のプロパティ。
イベントオブジェクトはイベントハンドラの引数として渡されるので、イベントハンドラに定義した引数を受け取る変数を通してアクセスできます。イベントオブジェクトの持つ情報はプロパティとして保持されているので、次の書式でアクセスできます。
【イベントオブジェクトのプロパティを参照する書式】
イベントオブジェクト.プロパティ
本連載ではイベントオブジェクトを受け取る変数は一貫して「eventObj」という名前を使っています。イベントターゲットを表す情報はtargetプロパティとして保持されるので「eventObj.target」という形でアクセスすることができます。
サンプル1のスクリプトをtargetプロパティを使って書き換えると次のようになります。
スクリプト:03_02.fla
//メインタイムライン:フレームアクション
//▼ボタンのイベント処理
play_btn.addEventListener(MouseEvent.CLICK, play_btn_clickHandler);
stop_btn.addEventListener(MouseEvent.CLICK, stop_btn_clickHandler);
//▼関数定義
//clickイベントハンドラ定義
function play_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション開始
star1_mc.addEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
star2_mc.addEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
}
function stop_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション停止
star1_mc.removeEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
star2_mc.removeEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
}
//enterFrameイベントハンドラ
function star_mc_enterFrameHandler(eventObj:Event):void {
eventObj.target.rotation += 5;
}
この場合、enterFrameイベントのターゲットは、addEventListener()メソッドでイベント処理を設定した各ムービークリップインスタンスとなります。そのため、インスタンス名の代わりに「eventObj.target」を使うことでイベントハンドラを共通化できます。
※enterFrameイベントは、addEventListener()メソッドを設定したオブジェクトとイベントターゲットが同じオブジェクトになりますが、clickイベントなど多くのマウスイベントなどは必ずしもそうとは限りません。
targetプロパティを使うことでイベントハンドラを共用できることが確認できました。しかし、実際の制作現場では、全く同一のイベントハンドラを複数のオブジェクトで共用することはほとんどありません。
例えば、レイアウトを変更するための移動アニメーションでも、オブジェクトによって移動先の座標はそれぞれ異なるものになります。この場合、移動のためのアルゴリズムは共通でも移動先が異なるために、そのままではイベントハンドラを丸ごと共用することはできません。このような場合、オブジェクトごとに異なる値はそのオブジェクトのカスタムプロパティとして設定しておくことで対応できます。
例えば、今回の回転アニメーションでムービークリップインスタンスごとに異なる回転速度にすることを考えてみます。この場合、回転速度はオブジェクトごとに異なる値なので、rotationSpeedという名前のカスタムプロパティとして保持するようにします。ここではstar1_mcの回転速度を5、star2_mcの回転速度を8にしています。
サンプル2のスクリプトを書き換えると次のようになります。
スクリプト:03_03.fla
//メインタイムライン:フレームアクション
//▼カスタムプロパティの設定
star1_mc.rotationSpeed = 5;
star2_mc.rotationSpeed = 8;
//▼ボタンのイベント処理
play_btn.addEventListener(MouseEvent.CLICK, play_btn_clickHandler);
stop_btn.addEventListener(MouseEvent.CLICK, stop_btn_clickHandler);
//▼関数定義
//clickイベントハンドラ定義
function play_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション開始
star1_mc.addEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
star2_mc.addEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
}
function stop_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション停止
star1_mc.removeEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
star2_mc.removeEventListener(Event.ENTER_FRAME, star_mc_enterFrameHandler);
}
//enterFrameイベントハンドラ
function star_mc_enterFrameHandler(eventObj:Event):void {
eventObj.target.rotation += eventObj.target.rotationSpeed;
}
2つのムービークリップインスタンスに独自のrotationSpeedプロパティを設定していますが、ほとんどのクラスではあらかじめActionScriptで用意されたプロパティしか使うことができません。このような形でカスタムプロパティを追加できるクラスはMovieClipクラスをはじめわずかしか無いことを覚えておいてください。
ムービークリップの特徴の1つとして、「シンボル内にスクリプトを記述できる」という点があります。シンボルは通常、全てのインスタンスでグラフィックを共用するために使われますが、グラフィックだけでなくスクリプトも共用できます。ムービークリップシンボル内でスクリプトを定義する場合、次のような特徴があります。
この点を踏まえて、ムービークリップのプロパティやメソッドとするのに相応しい処理をシンボル内に定義します。なお、シンボル内ではインスタンス自身を参照する場合「this」が使えます。結果としてtargetプロパティは使う必要はありません。
スクリプト:03_04.fla シンボル内のスクリプト
//ムービークリップ「mcStar」:フレームアクション
//▼カスタムプロパティの定義
var rotationSpeed:int;
//▼関数定義
//アニメーション再生/停止
function xStart():void {
//アニメーション開始
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
function xStop():void {
//アニメーション停止
this.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
//enterFrameイベントハンドラ
function enterFrameHandler(eventObj:Event):void {
this.rotation += rotationSpeed;
}
スクリプト:03_04.fla メインタイムラインのスクリプト
//▼カスタムプロパティの設定
star1_mc.rotationSpeed = 5;
star2_mc.rotationSpeed = 8;
//▼ボタンのイベント処理
play_btn.addEventListener(MouseEvent.CLICK, play_btn_clickHandler);
stop_btn.addEventListener(MouseEvent.CLICK, stop_btn_clickHandler);
//▼関数定義
//clickイベントハンドラ定義
function play_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション開始
star1_mc.xStart();
star2_mc.xStart();
}
function stop_btn_clickHandler(eventObj:MouseEvent):void {
//アニメーション停止
star1_mc.xStop();
star2_mc.xStop();
}
ここでのポイントはシンボル内にアニメーション再生用/停止用のメソッドを定義し、それをメインタイムラインから呼び出すようにした点です。こうしておけば、アニメーションの処理を変更する場合、アニメーションするオブジェクト自身に定義されているスクリプトを変更すればよく、メインタイムラインのスクリプトは変更する必要がありません。このような構成はコンテンツのメンテナンス性を高めることに繋がります。
サンプル4ではメインタイムラインからカスタムプロパティを直接変更しました。これはプログラミングの作法としてはあまり歓迎されないようです。代わりにカスタムプロパティを操作するためのメソッドを用意し、そのメソッドを通してプロパティを操作します。
メソッドを通してプロパティの値を設定することで、プロパティの値のルールができた(あるいは変更になった)ときに、メソッド内で制御できるからです。例えば、回転速度を-5~5の範囲に制限したい場合、メソッドを通してプロパティを設定するようにしておけば、範囲外の値が設定されてもメソッド内で範囲内の値に調整できるわけです。
サンプル4のスクリプトを次のように変更します。
スクリプト:03_05.fla シンボル内の「▼関数定義」ブロックに追加
//カスタムプロパティ設定
function xSetSpeed(speed_:int):void {
rotationSpeed = speed_;
}
スクリプト:03_05.fla メインタイムラインの「▼カスタムプロパティの設定」部を修正
//▼カスタムプロパティの設定
star1_mc.xSetSpeed(5);
star2_mc.xSetSpeed(8);
今回は、ほぼ同じ動作をするサンプルを構造を変えて作成しました。サンプルの内容がシンプルなのでサンプル5のスクリプトなどはかえって回りくどいように感じられるかもしれません。ですが、コンテンツの規模が大きくなったり複雑になってくるとこのような手法が重要になってきます。このような手法では、カスタムクラスを利用するのが合理的なケースが多くなります。本連載ではカスタムクラスは扱わないスタンスですが、興味を持った方は別途学習してみてください。
今回はどちらかというと制御される側のオブジェクトに焦点を当てましたが、次回は制御する側に焦点を当ててみようと思います。