![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|||||||||||||||||||||||||||||||||||||
![]() |
Flash OOP for ActionScript 3.0 第2章 ActionScript 2.0から3.0へのガイド
|
// MovieClip: アニメーションさせるインスタンス |
MovieClipインスタンスに定義したonEnterFrame()という名前の関数は、イベントハンドラメソッドとして毎フレームつまりフレームレートで描画が更新されるたびに呼出されます。したがって、[ムービープレビュー]を確かめると、関数内に記述したインスタンスの座標を移動する処理が毎フレーム行われ、水平に揺れるアニメーションになります(図2-2-001)。
フレームアクションを設定したMovieClipインスタンスは、具体的にはステージ中央のx座標nCenterXを中心として、水平に左右各nRadiusXの幅で揺れるように動きます。
▲図2-2-001■インスタンスが水平に揺れる
△揺れの中心のx座標がnCenterX、揺れ幅が左右各nRadiusXの範囲で、バネや波のように揺れ動く。
[MEMO]2-2-001 イベントハンドラメソッドの名前のない関数による定義 onEnterFrame = function ():Void { 名前のない関数については、筆者のサイトFumioNonaka.comの「名前のない関数(匿名関数/関数リテラル)」<http://www.fumiononaka.com/TechNotes/Flash/FN0108016.html>をご参照ください。 |
[MEMO]2-2-002 cosを使った周期的な値の変化 周期的に揺れる座標値 = a + r×cosθ なお、角度θは一般にラジアンが単位で、Mathクラスの三角関数も引数にラジアン値を渡します。 |
イベントハンドラメソッドは、決まった名前の関数を定義するだけなので、関数をとくに宣言したり登録したりする必要もなく記述が簡単です。しかし他方で、名前が決まっているということは、ひとつのインスタンスの同じイベントに、複数のメソッドを定義することができません。同じ名前の関数は、後の定義によって上書きされてしまうからです。
●2-2-2 ActionScript 3.0のイベントリスナー
ActionScript 3.0では、イベントはすべてイベントリスナーを使って処理します。イベントリスナーというのは、イベントを扱うインスタンスに対して、イベントを指定したうえで、関数をリスナーとして登録することにより処理する仕組みです。そのインスタンスにイベントが発生すると、登録されたリスナー関数が呼出されます。
○2-2-2-1 イベントリスナーを使ったスクリプト
スクリプト2-2-001と同じ内容の処理を、ActionScript 3.0のイベントリスナーで行うと、以下のようなフレームアクションになります(スクリプト2-2-002)。
// MovieClip: アニメーションさせるインスタンス |
上記スクリプト2-2-002のポイントについて、順を追って説明します。
○2-2-2-2 DisplayObject.enterFrameイベント
ActionScript 3.0では、クラスの構成が整理され、階層化されました。[ヘルプ]の[ActionScript 3.0コンポーネントリファレンスガイド]で[MovieClipクラス]の項を見ると、「Sprite、DisplayObjectContainer、InteractiveObject、DisplayObject、EventDispatcherの各クラスを継承」していることがわかります(図2-2-002)。
▲図2-2-002■[ヘルプ]の[MovieClipクラス]の項
△MovieClipクラスは、Sprite、DisplayObjectContainer、InteractiveObject、DisplayObject、EventDispatcherの各クラスを継承している。
[MEMO]2-2-003 継承 |
InteractiveObjectクラスは、マウスやキーボード操作に関わるイベントを扱います。また、毎フレームの描画を更新するイベントは、DisplayObjectクラスにDisplayObject.enterFrameとして定義されています。このイベントは、DisplayObjectのサブクラスであるMovieClipインスタンスも当然受取ることができます。スクリプト2-2-002は、アニメーションさせるMovieClipインスタンスに対して、DisplayObject.enterFrameイベントを指定して、リスナー関数を登録しています。
もっとも、スクリプト中に、DisplayObject.enterFrameイベントの記述は直接は登場しません。つぎの2-2-2-3に説明する、イベントを登録するためのEventDispatcher.addEventListener()メソッドには、DisplayObject.enterFrameイベントの指定として定数Event.ENTER_FRAMEが渡されています。イベント定数については、後述2-2-2-4で解説します。
○2-2-2-3 EventDispatcher.addEventListener()メソッド
インスタンスに対してイベントを指定し、イベント発生時に呼出すリスナー関数を登録するのが、EventDispatcher.addEventListener()メソッドです。addEventListener()メソッドの構文は、つぎのとおりです。
ターゲットインスタンス.addEventListener(イベント, 呼出す関数)
第1引数がイベントを指定する文字列で、後述(2-2-2-4)のイベント定数を使うのが標準です。第2引数には、イベントが発生したときに呼出す関数を、リスナーとして指定します。ActionScript 2.0/1.0のイベントハンドラメソッドと異なり、関数名は任意で、またひとつのインスタンスの同じイベントに対してリスナーをいくつでも登録できます。
スクリプト2-2-002では、EventDispatcher.addEventListener()メソッドのターゲットインスタンスが指定されていませんので、スクリプトを記述しているMovieClipが参照されます。なお、登録したリスナー関数を削除するには、EventDispatcher.removeEventListener()メソッドを使います。その引数の指定は、EventDispatcher.addEventListener()メソッドと同じです。
[MEMO]2-2-004 イベントリスナーとリスナー関数 ActionScript 3.0では、リスナーのインスタンスは別途作成せず、実行すべきリスナー関数をそのままリスナーとしています。なお、ヘルプの[ActionScript 3.0のプログラミング]→[イベントの処理]→[イベントリスナー]を併せてご参照ください。 |
○2-2-2-4 Event.ENTER_FRAMEイベント定数
EventDispatcher.addEventListener()メソッドの第1引数は、前述(2-2-2-3)のとおり「文字列」(String)です。たとえば、今回のDisplayObject.enterFrameイベントを指定するには、そのイベント名を文字列"enterFrame"として渡します。ですから、前記スクリプト2-2-002でEventDispatcher.addEventListener()メソッドを呼出すステートメントは、つぎのように記述しても問題なく動作します。
addEventListener("enterFrame", xMoveX);
つまり、イベント定数Event.ENTER_FRAMEには、単に文字列"enterFrame"が収められているだけなのです(図2-2-003)。では、文字列と比べて記述も長くなるイベント定数を使う意義は何でしょう。
▲図2-2-003■[ヘルプ]の[Eventクラス]に掲載されている「パブリック定数」の一覧(抜粋)
△Event.ENTER_FRAMEには、String型の値"enterFrame"が設定されている。
[MEMO]2-2-005 定数 |
まず第1に、単純に文字列で指定すると、スペルの誤りがあってもチェックされません。たとえば、間違って頭を大文字にした"EnterFrame"と書いても、シンタックスカラーは文字列として表示されるので変わりませんし、[コンパイルエラー]も発生しません。
そして第2に、イベント定数を使えば、クラスを入力した後にドット(.)でコードヒントが表示されます(図2-2-004)。Event.ENTER_FRAMEの場合でしたら、"Event."の入力でEventクラスのコードヒントが現れ、イベント名の最初の文字"E"をタイプすると"ENTER_FRAME"がハイライトされますので、[Enter]([return])キーで確定できます。したがって、表記の文字数は多いものの、実際の入力は文字列よりもむしろ少なくて済むのです。
▲図2-2-004■イベント定数を記述すればコードヒントが表示される
△"Event."まで入力すると、Eventクラスのコードヒントが表示される。続けて"E"をタイプすれば"ENTER_FRAME"がハイライトするので、[Enter]([return])キーで確定する。
つまり、イベント定数を使えば、コードヒントが利用できるので、タイプミスが防げます。それでもスペルを誤った場合には、シンタックスカラーが適用されない(デフォルトでは黒の表示になります)ので、すぐに気づくことができます。さらに気づかずにコンパイルすれば、[コンパイルエラー]として表示されますから、確実なチェックが可能になるのです。
▲図2-2-005■イベント定数の記述を誤ると[コンパイルエラー]が表示される
△スペルを誤ったイベント定数はシンタックスカラーも適用されない。
○2-2-2-5 Eventオブジェクト
イベントが発生すると、リスナー関数にはオブジェクトがひとつ引数として渡されます。ActionScript 3.0では、2.0と異なり、渡された引数を関数の定義で受取らないと、ランタイムエラーが発生します(図2-2-006)。リスナー関数に渡されるのは、EventDispatcher.addEventListener()メソッドにイベント定数として指定したクラスのインスタンスで、「イベントオブジェクト」と呼ばれます。
▲図2-2-006■リスナー関数に引数が指定されていないとランタイムエラーになる
△ActionScript 3.0では、渡された引数は必ず受取らないといけない。
[CAUTION]2-2-001 引数不一致のエラー内容 エラーメッセージの英文は"Expected 0, got 1"ですので、関数の期待する引数の数が0で、受取ったのは1だというのが正しい意味です。したがって、「0を必要としますが、1が渡されました」とするか、「指定は0ですが、受取ったのは1です」とでも訳すべきだったでしょう。 |
[MEMO]2-2-006 voidデータ型 |
○2-2-2-6 DisplayObject.stageとStage.stageWidth/Stage.stageHeightプロパティ
ActionScript 3.0では、Stageクラスのプロパティやメソッドも扱いが変わりました。ActionScript 2.0とは異なり、Stageクラスに静的(static)に定義されているのではなく、Stageインスタンスのプロパティ・メソッドとして実装されています。
[MEMO]2-2-007 静的プロパティ・メソッド クラスが読込まれるとき、クラスに対してそれぞれひとつだけ初期化され、インスタンスを必要としません。よって、クラスを直接参照してアクセスします。 静的なプロパティ(定数)やメソッドを中心に構成されているクラスとして、Mathクラスが挙げられます。 [*筆者用参考] Java入門「メンバー変数(フィールド)」、「静的メンバ」。 |
Stageインスタンスは、Flash Playerが初期化されるときに、ひとつだけ生成されます。後からインスタンスを、追加して作成することはできません。
また、Stageインスタンスは、DisplayObject.stageプロパティから取得する必要があります。なお、後述(2-3-3-2「ActionScript 3.0はコンストラクタと表示リストでインスタンスを操作」)するとおり、DisplayObject(あるいはそのサブクラス)のインスタンスがStageを頂点とする「表示リスト」に属していないと、DisplayObject.stageプロパティはnullを返し、Stageインスタンスにアクセスできません。
[MEMO]2-2-008 データのデフォルト値 undefiendを返すのは、型指定されていないデータと、戻り値をvoidで指定された関数です。型指定されたデータにundefinedを代入すると、そのデータ型のデフォルト値に変換されます。なお、データ型とそのデフォルト値については、[ヘルプ]の[ActionScript 3.0のプログラミング]→[ActionScript言語とシンタックス]→[データ型]→[データ型の記述]をご参照ください。 |
Stage.stageWidthならびにStage.stageHeightプロパティは、それぞれステージの幅と高さを返します。
●2-2-3 イベントリスナーの特徴
ActionScript 3.0のイベントリスナーは、2.0のイベントハンドラメソッドと比べると、リスナー関数を登録するという処理が増えます。しかし、逆に任意の名前の関数を登録でき、しかもひとつのインスタンスの同じイベントに対して複数のリスナー関数を追加することも可能です。また、イベントの扱いも、2.0と比べてよりきめ細やかになりました。イベントについては、後述2-4「マウスイベントの扱い」で節を改めて解説します。本節では、リスナー関数が引数として受取るイベントオブジェクトについて、その内容をもう少し説明しておくことにしましょう。
○2-2-3-1 イベントオブジェクトを受取る
DisplayObject.addEventListener()メソッドの第2引数に指定したリスナー関数には、イベントが発生して呼出されるときに、イベントオブジェクトが渡されます。そのイベントオブジェクトは、DisplayObject.addEventListener()メソッドの第1引数にイベントとして指定した、イベント定数を備えるクラスのインスタンスです。
前記スクリプト2-2-002では、イベントを定数Event.ENTER_FRAMEで指定しましたので、リスナー関数xMoveX()に渡されるイベントオブジェクトはEventクラスのインスタンスになります。受取った引数は、trace()関数を使って[出力]パネルに表示することができます。たとえば、スクリプト2-2-002にtrace()関数を加えて、Eventオブジェクトを[出力]すれば、つぎのように表示されます(図2-2-007)。
[Event type="enterFrame" bubbles=false cancelable=false eventPhase=2]
▲図2-2-007■trace()関数を使って[出力]したEventインスタンス
△Event.typeプロパティは、イベント定数に指定された文字列を示す。
角括弧[]内には、「プロパティ=値」のかたちで、Eventオブジェクトの内容が示されます。Event.typeプロパティはリスナーの登録されたイベント名の文字列、つまりDisplayObject.addEventListener()メソッドの第1引数に指定されたイベント定数の値です。他のプロパティについては細かくなりますので、本稿では説明を割愛します。なお、イベントの扱いについては、2-4「マウスイベントの扱い」で後述します。
イベントオブジェクトは、Eventクラスまたはそのサブクラスのインスタンスです。たとえば、インスタンスのマウスクリックはInteractiveObject.clickイベントで受取ることができ、MouseEvent.CLICK定数により指定されます。MouseEventクラスはEventのサブクラスです。
MovieClipインスタンスへのマウスクリックを受取るには、MovieClipシンボルに以下のようなフレームアクションを記述します。インスタンスをクリックすると、trace()関数によりMouseEventオブジェクトが[出力]されます。
addEventListener(MouseEvent.CLICK, xShowEventObject);
function xShowEventObject(eventObject:MouseEvent):void {
trace(eventObject);
}
インスタンスをクリックしたときのMouseEventオブジェクト[出力]結果は、たとえばつぎのような内容です。
[MouseEvent type="click" bubbles=true cancelable=false eventPhase=2 localX=0 localY=0 stageX=120 stageY=90 relatedObject=null ctrlKey=false altKey=false shiftKey=false delta=0]
▲図2-2-008■インスタンスのクリックでMouseEventインスタンスを[出力]
△InteractiveObject.clickイベントは、MouseEvent.CLICK定数で指定。
まず、前述のとおりEvent.typeプロパティは、文字列のイベント名"click"で、イベント定数MouseEvent.CLICKの値です。つぎに、MouseEventクラスはEventのサブクラスですから、MouseEventインスタンスのプロパティにはEventインスタンスにないものがいくつか加わっています。その一部を下表2-2-001に掲げます。なお、マウスイベントについては、後述2-4「マウスイベントの扱い」でもう少し詳しく説明します。
▲表2-2-001■MouseEventインスタンスで追加されているおもなプロパティ式Aの値 | 式Bの値 |
localX、localY | インスタンスを基準としたマウスイベント発生位置の座標です。 |
stageX、stageY | ステージを基準としたマウスイベント発生位置の座標です。 |
ctrlKey、altKey、shiftKey | マウスイベント発生時に、[Ctrl]([command])や[Alt]([option])、[Shift]の各キーが押されていたか(true)、いないか(false)を示します。 |
ほかにも、Eventオブジェクトのサブクラスとして、InteractiveObjectクラスのキーイベントを指定するKeyboardEventクラスがあります。KeyboardEventインスタンスには、押したキーのキーコードなどがプロパティとして加わります。
○2-2-3-2 ひとつのイベントに複数のリスナーを登録できる
ActionScript 3.0のイベントリスナーの仕組みは、2.0のイベントハンドラメソッドと異なり、ひとつのインスタンスの同じイベントに複数のリスナー関数が登録できます。そこで実際に、前掲スクリプト2-2-002に手を加えて、複数のリスナー関数を同じDisplayObject.enterFrameイベントに追加してみましょう(スクリプト2-2-003)。
// MovieClip: アニメーションさせるインスタンス |
水平方向の揺らぎをcos値の変化により与える関数xMoveX()に加えて、垂直方向の揺らぎをsin値で加える関数xMoveY()が定義されています。さらに、楕円軌道上の垂直位置(sin値)に応じてインスタンスの水平スケールを変化させる関数xScale()も追加しました。そして、角度のラジアン値やその角度に対するcosおよびsin値を計算する関数は、xUpdate()として定義しています。これらの関数は、すべてインスタンスのDisplayObject.enterFrameイベントにリスナーとして追加します。
[ムービープレビュー]を見ると、インスタンスのアニメーションは楕円軌道を描きます。しかも、楕円軌道の端に行くほど水平スケールが縮まり、また上半分の軌道ではインスタンスの向きも反転します。そのため、3D風の回転のアニメーションとして表現されます。
▲図2-2-009■インスタンスの向きと水平スケールが3D風に変化する
△楕円軌道の端に行くほど水平スケールが縮まり、上方の軌道では向きが反転する。
関数xMoveY()だけの処理を見れば、インスタンスのy座標をsin値に比例させて垂直方向に揺り動かすだけです。しかし、x座標をcos値に比例させて水平に揺り動かす関数xMoveX()と組合わせると、角度の引数に同じ変数(nRadian)を使っているため、同期して楕円軌道を描きます。
[MEMO]2-2-009 三角関数を使った楕円の式 楕円軌道のx座標 = a + rx×cosθ なお、rx = ryのとき、軌道は正円を描きます。 |
関数xScale()が設定しているプロパティDisplayObject.scaleXは、水平方向のスケール(拡大・縮小率)を表します。ActionScript 2.0のMovieClip._xscaleプロパティの単位がパーセンテージだったのに対して、DisplayObject.scaleXは原寸を1とする小数値で示します(前掲表2-1-002「ActionScript 2.0で先頭に『_』のついたプロパティの3.0への変更例」参照)。また、負の値はインスタンスの向きを反転します。
一方、sin値は-1から1までの値を取り、楕円軌道の最下部が1、最上部が-1に対応します。そして、軌道の両端になる垂直方向の真ん中で値0を取ります。そのため、sin値を水平方向のスケールに設定すると、丁度楕円軌道の回転を3D風に表現できるのです。
3つの関数xMoveX()とxMoveY()、およびxScale()は、角度の値(nRadian)とその角度にもとづくcosならびにsin値で同期します。そこで、これらの基本となる値の更新と再計算を、関数xUpdate()として定義しました。
都合4つの関数は、それぞれ個別にインスタンスのDisplayObject.enterFrameイベントにリスナーとして追加されています。このうち関数xUpdate()は、他の関数の処理の基本となる値を計算していますので必須です。しかし、他の3つの関数はそれぞれ独立していますので、イベントリスナーに加えるかどうかは自由に選べます。
たとえば、関数xMoveY()は除いて、xMoveX()とxScale()だけでアニメーションを行えば、3D風の回転を真横から見たような表現ができます。あるいは、これらの関数を必要に応じて、リスナーに加えたりあるいは除いたりといった処理をスクリプトで動的に行うことも可能でしょう。
▲表2-2-002■リスナー関数の処理内容関数名 | 処理するプロパティ・変数 | 処理内容 |
xMoveX() | x | cos値に比例して揺り動かす |
xMoveY() | y | sin値に比例して揺り動かす |
xScale() | scaleX | sin値を設定 |
xUpdate() |
nRadian |
他の関数の処理の基本となる値を計算 |
今回の3D風の楕円軌道のアニメーションについては、すべての処理をひとつの関数にまとめても大した行数にはなりません。あるいは、リスナー関数はひとつだけ登録して、その関数内から他の4つの関数を呼出す方法も考えられます。しかし、イベントリスナーの仕組みを使うことで、関数の独立性を高め、必要に応じて組合わせるといった構成が可能になるのです。
作成者: 野中文雄
作成日: 2008年1月22日