Adobe Flash CS3 Professional ActionScript 3.0 □04 MovieClipの座標の操作
○04-01 マウス座標を調べる ●継承されるプロパティとメソッド [MovieClipクラス]の[プロパティ]を見ても、デフォルトではxやyというプロパティが一覧表にありません(図04-001)。rotationプロパティも掲載されていませんし、妙に数が少ないようです。実は、表の上部に「継承されるブロパティを表示」というボタンがあります。これをクリックすると一覧表が拡張され、たくさんのプロパティが表示されるようになります。 図04-001■[ヘルプ]パネルに表示された[MovieClipクラス]の[プロパティ]一覧表
拡張された一覧表には、もちろんxやyプロパティが掲載されています(図04-002)。では、最初から一覧に含まれていたプロパティと、拡張表示されたプロパティは何が違うのでしょうか。それは一覧表の右の欄に示された「定義元」のクラスです。 図04-002■[MovieClipクラス]の[プロパティ]一覧表に拡張表示されたxとy
最初に表示されていたのは、「定義元」がMovieClipのものだけです(図04-001)。これはつまり、これらのプロパティがMovieClipクラスに定義されていることを意味します。ところが、xとyプロパティの「定義元」は、DisplayObjectと記載されています。ですから、これらのプロパティはMovieClipクラスでなく、DisplayObjectという別クラスで定義されているということになります。 しかし、すでに前の章でスクリプトを作成して試したとおり、xやyプロパティはMovieClipインスタンスで操作することができました。これを可能にする仕組みが、「継承」なのです。MovieClipクラスは、DisplayObjectクラスを「継承」しています。すると、DisplayObjectクラスのプロパティやメソッドが、MovieClipクラス自身に定義されたものと同じように、MovieClipインスタンスからアクセできるようになるのです。 自作PCを組立てようとするとき、すべての部品をひとつひとつ買い集めるのではなく、「べアボーン」と呼ばれるキットを利用することがあります。べアボーンキットには、どのようなPCにも最低限必要なマザーボードやケース、電源がワンセットになっています。そこに、自分の選んだCPUを挿し、必要な容量のメモリやディスクを積み、その他のボードやドライブを組込んで、オリジナルのPCをつくる訳です。 継承というのは、このべアボーンのように他のクラスをベースとして利用し、それに独自のプロパティやメソッドを追加定義することにより新しいクラスを作成する仕組みです。ベースとなるクラスのプロパティやメソッドは、当然新しいクラスで使用することができます。この基本となるクラスを「スーパークラス」、それをベースに追加・拡張したクラスは「サブクラス」と呼ばれます。
MovieClipは、DisplayObjectをスーパークラスとする、サブクラスなのです。もっとも、MovieClipクラスは、DisplayObjectクラスを直接継承している訳ではありません。ActiolnScript 3.0の継承は、何段階もの階層をもっています。ヘルプの[ActionScript 3.0コンポーネントリファレンスガイド]で[MovieClipクラス]の解説の最初に、MovieClipクラスまでの継承のつながりが記載されています(図04-003)。 図04-003■[MovieClipクラス]の解説冒頭に示された「継承」
DisplayObjectクラスを直接継承するのは、InteractiveObjectクラスです。つぎに、そのサブクラスとしてDisplayObjectContainerクラスがあり、それをさらにSpriteクラスが継承します。そして、SpriteクラスがMovieClipクラスの直接のスーパークラスになります。 DisplayObjectクラスにも、EventDispatcherというスーパークラスがあります。EventDispatcherはイベントを扱うクラスで、たとえばaddEventListener()メソッドは、このクラスに定義されています。そして、EventDispatcherクラスは、Objectクラスを継承します。このObjectは、ActionScript 3.0のすべてのクラスのスーパークラスになります。 MovieClipクラスは、これら6つのスーパークラスのすべてのプロパティやメソッドを利用することができます。なお今後は、プロパティやメソッドを引用して説明する際に、その定義されているクラスを示して、DisplayObject.xやDisplayObject.y、あるいはEventDispatcher.addEventListener()のように表記することがあります。 ●マウスポインタの座標を調べる
[read-only]といのは「読取り専用」、つまり調べることはできても設定はできないということです。Flashコンテンツでは、マウスポインタの位置を勝手に動かすことはできません。説明はきわめて簡素です。マウスポインタの座標を、ピクセル単位で返すということです。一見、疑問の余地はないように見えます。それでは、早速スクリプトを書いてみましょう。 当初考えた段取りの3点は、一応判明しました。(1)マウスポインタの座標は、DisplayObject.mouseXおよびDisplayObject.mouseYプロパティで調べられます。(2)MovieClipインスタンスの座標は、DisplayObject.xおよびDisplayObject.yプロパティで設定できることはすでにわかっていました。(3)毎フレームのアニメーションは、EventDispatcher.addEventListener()メソッドを使って、Event.ENTER_FRAMEイベントにリスナー関数を登録すれば処理できます。 いきなりxy座標両方の処理を書くのでなく、まずはx座標だけのスクリプトを作成してみましょう。つまり、マウスポインタの水平方向の動きに追随するアニメーションのスクリプトです。すると、スクリプトは以下のようなフレームアクションでよさそうに思えます。メインタイムラインにMovieClipインスタンスを置き、そのシンボル内の第1フレームアクションとして設定するものとします。 // MovieClip: マウスポインタに追随させるインスタンス図04-004■マウスポインタに追随させるMovieClipのフレームアクション
マウスポインタのx座標値をMovieClipインスタンスのx座標に設定している訳ですから、これでマウスポインタの水平方向の動きに追随しそうに思えます。しかし、[ムービープレビュー]でアニメーションを確認すると、点滅したような動きになるはずです。マウスを動かすと点滅する位置が変わるので、ポインタの動きに反応はしているようだということが推測できます。では、なぜMovieClipインスタンスの水平位置が、マウスポインタのx座標に一致しないのでしょうか。 ○04-02 プロパティの天動説と地動説 まず、DisplayObject.mouseXは、DisplayObjectクラスのプロパティであることをもう一度思い起こしましょう。DisplayObjectクラスのプロパティだということは、インスタンスによって値が異なることを意味します。つまり、ステージのような特定の座標空間を基準にするのでなく、各インスタンスから座標を測るということです。具体的には、インスタンスの基準点を原点とした座標を返します。 他方、DisplayObject.xプロパティについては、自分を中心にして自分の位置を決めることはできません。ですから、自分の配置された親のタイムラインを基準に、座標を測ることになります。つまり、親タイムラインの基準点を原点とした座標を返します。 いってみれば、DisplayObject.mouseXは自分中心の天動説プロパティで、DisplayObject.xは自分の属する座標空間を基準にした地動説プロパティだということになります。基準が異なりますので、一方のプロパティ値を他方のプロパティに設定すれば、ちぐはぐな結果を招きます。
たとえば、メインタイムラインに配置されたインスタンスのDisplayObject.xプロパティ値が100であれば、ステージ左端から100ピクセルの位置に存在します。そして、マウスポインタがステージ左端から50ピクセルの位置にあった場合、DisplayObject.mouseXプロパティはインスタンスから見た座標ですから、その値は-50だということになります(図04-005)。 図04-005■DisplayObject.xプロパティとDisplayObject.mouseXプロパティの値
前記フレームアクション(図04-004)で、Event.ENTER_FRAMEが発生し、DisplayObject.mouseXプロパティの値-50をインスタンスのDisplayObject.xプロパティに設定すると、ステージ左端から外に50ピクセルの位置に移動してしまいます。そして、マウスポインタが動かなかったとすると、インスタンスから見たDisplayObject.mouseXプロパティの値は、今度は100になります。したがって、つぎの描画更新時には、またステージ左端から100ピクセルの位置に戻ります。この繰返しが、点滅の動作になったのです。 ○04-03 親タイムラインから眺める − 地動説 前記スクリプト(図04-004)でDisplayObject.mouseXプロパティにターゲットを省略したので、スクリプトを記述したMovieClipインスタンスが参照されました。このターゲットを明記して、親タイムラインを指定すればよいでしょう。インスタンスは、メインタイムラインに配置されています。オーサリング時に配置したMovieClipインスタンスからメインタイムラインを参照するのは、DisplayObject.rootプロパティです。 DisplayObject.mouseXプロパティのターゲットをDisplayObject.rootプロパティで指定したのが、以下のフレームアクションです(スクリプト04-001)。[ムービープレビュー]で試すと、MovieClipインスタンスがマウスポインタの水平方向の動きに追随します(図03-006)。 スクリプト04-001■MovieClipインスタンスをマウスポインタの水平方向の動きに追随させるフレームアクション
図04-006■マウスポインタの水平方向の動きに追随するMovieClipインスタンス
DisplayObject.rootをターゲットとして参照するのは、汎用性の面では欠点があります。たとえば、マウスポインタに追随するMovieClipインスタンスを、もうひとつ別のMovieClipシンボルに入れ子にしてメインタイムラインに配置した場合です。 すると、DisplayObject.xプロパティの座標の基準は、入れ子の親のMovieClipインスタンスになります。ところが、DisplayObject.mouseXプロパティのターゲットにはDisplayObject.rootプロパティを指定している訳ですから、座標の基準はつねにメインタイムラインになります。基準の異なる座標を同一のものとして扱えば、またちぐはぐな動きになります。 ターゲットとしてつねに親タイムラインを参照できれば、この問題は解決します。親のインスタンスを参照するプロパティは、DisplayObject.parentです。前記フレームアクション(スクリプト04-001)のリスナー関数の処理内で、DisplayObject.mouseXプロパティのターゲットをDisplayObject.parentに替え、さらにy座標の処理について同様のステートメントを加えたのが、つぎのスクリプト04-002です。 スクリプト04-002■MovieClipインスタンスをマウスポインタの動きに追随させるフレームアクション − 地動説
[ムービープレビュー]を実行すると、マウスポインタの動きにMovieClipインスタンスが追随します(図04-007)。 図04-007■マウスポインタのxy座標の動きに追随するMovieClipインスタンス
○04-04 差を埋める − 天動説
まず第1は、関数xFollowMouse()本体の2行のステートメントに記述した、DisplayObject.mouseX/DisplayObject.mouseYプロパティのターゲットです。天動説を基準にするので、ターゲットを省略して、MovieClipインスタンス自身を参照するように変更しました。このままでは点滅したような動作になることは、04-01「マウス座標を調べる」で作成した、水平座標の処理を行うスクリプト(図04-004)により確かめました。 そこで第2に、通常の代入演算子=を、加算後代入演算子+=に修正しています。これで[ムービープレビュー]を確認すると、前記スクリプト04-002と同じように、マウスポインタの動きにMovieClipインスタンスが追随します。これは、なぜでしょう? マラソンで、先頭走者に追いつきたいと思ったとき、地動説の考え方はこうなります。まず、先頭走者が今この瞬間走っている、スタート地点からの位置を確かめます。それが何キロメートルの位置だと知ったら、瞬時にその同じ場所に移動できれば追いつきます。 しかし、通常ランナーは追いつきたい相手のスタート地点からの位置ではなく、自分のどれくらい前を走っているのかということを考えるでしょう。それが何百メートルあるいは何キロメートル先だとわかったら、その遅れを取戻そうとします。つまり、相手の位置までに足りない距離を、加えて埋め合わせてやればよいのです。これが天動説の考え方になります。 プロパティ += 自分から見た相手との差図04-009■自分から見た相手との差を埋め合わせれば追いつく
自分の方が相手より先にいる場合には、相手の位置が負の値になりますので、やはりその値を加えれば、相手の場所に追いつきます。追いついてしまうと、相手の位置との差がなくなりますので、0を足し込むことになり、そのままその場にとどまります。
○04-05 イーズアウトの公式
今回も、まずスクリプトを先にご紹介します。数値の変数nDeceleration("deceleration"は「減速」の意)を、新たに追加しています。 スクリプト04-004■MovieClipインスタンスをマウスポインタに向けてイーズインさせるフレームアクション − 天動説
変数nDecelerationは、減速率を決める小数値で、0から1の間の値を取ります。この変数値を、関数xFollowMouse()本体でインスタンスのxy座標を設定する代入式右辺の、DisplayObject.mouseXとDisplayObject.mouseYプロパティの値にそれぞれ乗じています。 変数nDecelerationの値が1であれば前記スクリプト04-003と同じ動作で、MovieClipインスタンスは遅れずにぴったりとマウスポインタに追随します。0なら動きません。値が0に近いほどゆっくりと後を追い、1に近ければ素早く追いつきます。 余談ですが、昔筆者の隣の家に、男の子の兄弟がいました。兄弟というと、大抵は下の子が要領はよいようです。あるとき、弟が茶の間のお菓子を見て、台所のお母さんに「お菓子食べてもいい?」と尋ねたそうです。ふたり兄弟ですから、「半分ずつよ」とお母さんは答えました。しかし、しばらくするとまた、「お菓子食べてもいい?」と聞くのです。「半分だからね」と念を押すと、またすぐ「お菓子食べてもいい?」と繰返します。 あまりしつこいので、お母さんが茶の間に行くと、お菓子はほぼなくなっていました。どうやら「半分よ」とお母さんの答えが返ってくるたびに、残ったお菓子を半分ずつ食べてしまっていたようです。 これは、減速率0.5(=1/2)の場合の処理と同じです。毎回、つまり毎フレーム、半分(1/2)だけ目的地に近づきます。目的地に近づくにしたがって、移動する1/2の距離は小さくなります。すなわち、減速していくということです。そして、速度を落としながらも、目的地にかぎりなく近づいていきます。 このイーズアウトの処理を公式風に表現すると、つぎのとおりです。
前記スクリプト04-004は、地動説の基準でも記述することができます。地動説の場合、マウスポインタの座標DisplayObject.mouseX/DisplayObject.mouseYプロパティは、親のインスタンスに対するDisplayObject.parentプロパティの参照をターゲットにすることになります。そのうえでマウスポインタの座標を自分から見た値とするには、自分のインスタンスの座標を差引けばよいでしょう。 先頭走者のスタート地点からの位置がわかったら、同じように自分のスタート地点からの位置を調べてその差を取れば、自分から見た相手までの距離になるからです。したがって、フレームアクションに定義した関数xFollowMouse()を、つぎのスクリプト04-005のように書替えても動作は変わりません。 スクリプト04-005■MovieClipインスタンスをマウスポインタに向けてイーズインさせるフレームアクション − 地動説
よって、イーズアウトの公式は、つぎのように書替えることもできます。
もっとも、今回の処理では天動説プロパティDisplayObject.mouseX/DisplayObject.mouseYを使って、自分から見た相手の値との差が直接取得できるので、あえて地動説の処理にする必要はありません。わざわざ差を計算しなければならないのは、目的の値が予め決まっているような場合です。たとえば、ステージ中央にイーズアウトのアニメーションをさせるといった例が考えられます。 ステージ中央の座標は、直接取得できません。Stageクラスで幅と高さのStage.stageWidthとStage.stageHeightのプロパティ値を調べて、中心の座標を計算する必要があります。ただし、Stageクラスに直接アクセスして、インスタンスを作成することはできません。 new Stage(); new演算子でコンストラクタを呼出してStageインスタンスを作成しようとすれば、つぎのようなエラーが[出力]されます。 ArgumentError: Error #2012: Stage クラスをインスタンス化することはできません。
DisplayObjectおよびそのサブクラス(たとえばMovieClip)のインスタンスは、DisplayObject.stageプロパティをもちます。このDisplayObject.stageプロパティがStageインスタンスを参照しますので、そこからStageクラスのプロパティを取得します。 そうすると、ステージ中央の座標を計算したうえで、MovieClipインスタンスをその位置に向けてイーズアウトさせるフレームアクションは、以下のようになります(スクリプト04-006)。MovieClipインスタンスは、メインタイムラインの端(中央以外)に配置し、スクリプトはこのMovieClipシンボルのフレームアクションとして設定します。 スクリプト04-006■MovieClipインスタンスをステージ中央に向けてイーズインさせるフレームアクション
まず、スクリプトの初期設定で、Stage.stageWidthとStage.stageHeightプロパティを使って、それぞれの半分の長さつまりステージ中央の座標を計算しています。MovieClipインスタンスはメインタイムラインに配置されていますので、インスタンスのDisplayObject.x/DisplayObject.yプロパティにこれらの座標値を設定すれば、ステージ中央に配置できます。 つぎに、Event.ENTER_FRAMEイベントに設定したリスナー関数xFollowMouse()内の処理で、上記のイーズアウトの公式にしたがい、目指すステージ中央の座標から自分の座標値を引いた値に減速率を乗じて、DisplayObject.x/DisplayObject.yプロパティに設定しています。 [ムービープレビュー]で確かめると、MovieClipインスタンスはイーズアウトしながら、ステージ中央に移動します。
イーズアウトの処理について、ふたつの計算方法をご紹介しました(表04-001)。ただいずれも、相手と自分の差を取り、減速率を掛合わせて、プロパティの現在値に加算するという意味では同じ内容といえます。ひとことでいうなら、「差を割引いて足し込む」ということになるでしょう。 表04-001■イーズアウトの公式
○04-06 プロパティを設定する関数
●インスタンスの変数値をどのように設定するか そうすると、MovieClipインスタンスを配置した親のタイムラインにフレームアクションを記述して、各インスタンスの変数値を個別に変えればよいでしょう。ただし、今回は変数に値を直接代入する方法は取りません。理由はふたつあります。 第1に、フレームアクションの実行される順序が問題です。フレームアクションは、親から子の順で処理されます。すると、親タイムラインのフレームアクションで変数値を設定しても、子のフレームアクションでvar宣言時に初期値を与えていますので、その値で上書きされてしまいます。 たとえば、以下のスクリプトは、メインタイムラインの第1フレームアクションから、MovieClipインスタンスmy_mcの(Number型)変数testに数値1を設定します。しかし、そのMoiveClipシンボル内の第1フレームアクションで、Number型変数testをvar宣言するとともに、初期値0を代入しています。すると、フレームアクションは、MovieClipインスタンスのスクリプトがメインタイムラインよりも後に処理されますので、変数testの値は1から0に変更されてしまうことになります。 // メインタイムライン // MovieClip: インスタンスmy_mc // [出力]パネルの表示 [出力]結果を見ると、メインタイムラインで設定した変数値1がまずtrace()関数で表示され、つぎにMoiveClipインスタンスmy_mcで代入した初期値0に書替えられたことがわかります。 変数に値を直接代入しない理由の第2は、修正や拡張に対する柔軟性が損なわれるためです。 たとえば、各MovieClipインスタンスの減速率の値だけでなく、アルファ値も変えることになるかもしれません。その場合、減速率やアルファの値は、試行錯誤しながら調整する可能性が高いですし、後から変更することも十分あり得ます。それらの調整や変更をMovieClipインスタンスの外部から行うのは、煩雑なだけでなく、MovieClipシンボル内にフレームアクションを記述してパーツ化した意味が失われます。 さらに、MovieClipシンボルに設定したスクリプトを拡張する際には、変数に設定しうる値をチェックしたり、設定のタイミングを管理するなどして、誤動作(バグ)が生じないような工夫を加えることが少なくありません。インスタンスの外部から直接変数の値を変更すると、このような処理が無視される結果となります。 以上の理由から、パーツ化を推し進め、スクリプトの信頼性と整合性を高めるためには、変数をインスタンスの外部から直接変更するのは望ましくありません。そこで、MovieClipシンボル内のフレームアクションに、変数設定用の関数を定義した方がよいということになるのです。 ●変数値を設定する関数の定義 関数の定義に慣れるコツは、まず呼出し方から考えることでした。タイムラインにマウスポインタを追いかけるMovieClipインスタンスmy_mcを置いたとすると、タイムラインのフレームアクションからインスタンスmy_mcに対して、減速率の値たとえば0.2をつぎのように設定することにします。 my_mc.xInitialize(0.2); 今のところは、関数xInitialize()の処理は、変数nDecelerationに引数の値を代入する1ステートメントだけの内容です。練習問題としても、ごく簡単でしょう。MovieClipシンボルの第1フレームアクション(スクリプト04-004)に追加する関数xInitialize()は、つぎのように定義されます。 function xInitialize(nValue:Number):void { しかし、ほかにもうひとつ、フレームアクション中に修正しなければならない点があります。それは、var宣言した変数nDecelerationの初期値設定です。もとのスクリプト04-004では、var宣言と同時に、変数nDecelerationには0.2が代入されています。 var nDeceleration:Number = 0.2; 変数値を関数xInitialize()の呼出しにより設定したとしても、フレームアクションの処理は親から子の順番であることは変わりありません。すると、メインタイムラインからの関数xInitialize()を呼出して変数値を設定しても、つぎにMovieClipインスタンスのフレームアクションが実行されれば初期値0.2で上書きされてしまいます。 それを避けるには、フレームアクションでは変数nDecelerationのvar宣言と型指定のみにとどめる必要があります。その修正も加えたのが、つぎのスクリプト04-007です。 スクリプト04-007■減速率設定の関数を追加したMovieClipのフレームアクション
たとえば、メインタイムラインにマウスポインタに追随するMovieClipインスタンスを3つ配置し、それぞれにmy0_mc、my1_mc、my2_mcというインスタンス名をつけたとします。すると、メインタイムラインのフレームアクションに以下のステートメントを記述すれば、3つのMovieClipインスタンスが異なった減速率により、少しずつ追いかけるスピードを変えてマウスポインタに連なります。[ムービープレビュー]で、確認してみましょう。 my0_mc.xInitialize(0.6);
●アニメーションを開始する関数の定義 これは、Number型の変数nDecelerationに初期値が設定されていないため、デフォルト値としてNaN(Word 02-003「NaN」)が与えられてしまうからです。NaNにどのような数値演算を施しても、結果はNaNになります。そして、NaNを数値のプロパティに設定すると、予想外の値、DisplayObject.xとDisplayObject.yではステージ外の座標値として扱われてしまうのです(02-03「関数(function)を定義する」「秒針が最初に一瞬ずれる訳 − NaN(非数) −」参照)。
つまり、新たな仕様というのは、MovieClipインスタンスに対して必ず関数xInitialize()を呼出し、減速率の初期値を設定しなければならないということです。しかし、逆に考えると、MovieClipをタイムラインに置いただけで直ちにアニメーションが始まるのでなく、好きなときにスタートできるという意味でもあります。 ただ、いきなりステージ上から消えてしまうのは問題です。それだけでなく、Event.ENTER_FRAMEイベントにリスナー関数が登録されていますので、アニメーションを始める必要がなくても、関数が繰返し呼出されてしまいます。 そこで、アニメーションを開始する関数xStart()を定義し、そこでxInitialize()を呼出して変数の初期値を設定するとともに、イベントリスナーの登録を行うこととします。そうすると、MovieClipインスタンスには、初期状態ではEvent.ENTER_FRAMEイベントへのリスナー関数は登録されておらず、関数xStart()の呼出しを待ってアニメーションが始まることになります。 関数xStart()の処理は、前述のとおり、ふたつのステートメントから構成されます。第1は、EventDispatcher.addEventListener()メソッドを呼出して、Event.ENTER_FRAMEイベントにリスナー関数xFollowMouse()を登録します。第2は、xInitialize()を呼出して減速率の変数nDecelerationに初期値を与えます。この関数xStart()を定義して加えたのが、つぎのスクリプト04-008です。 スクリプト04-008■アニメーション開始の関数を追加したMovieClipのフレームアクション
このマウスポインタに追随するMovieClipインスタンスを配置したタイムラインのフレームアクションからは、前のスクリプト04-007のときの関数xInitialize()に替えて、xStart()を呼出すことになります。ですから、減速率の初期値は、関数xStart()の引数として渡します。xStart()の関数本体{}内では、その値をそのまま引数にして関数xInitialize()を呼出しています。 メインタイムラインにMovieClipインスタンスmy0_mc、my1_mc、my2_mcを配置したすると、メインタイムラインのフレームアクションにたとえば以下のようなステートメントを記述することになります。 my0_mc.xStart(0.6); MovieClipインスタンスに対して関数xStart()を呼出さなければ、そのインスタンスのEvent.ENTER_FRAMEイベントにはリスナー関数が登録されません。前述スクリプト04-008の関数xFollowMouse()内には、それを確認するためのtrace()関数をステートメントとして加えています。 ただし、trace()ステートメントはコメントアウトしてありますので、確認するにはコメント行区切り記号//を削除します。[ムービープレビュー]を行うと、関数xStart()を呼出したMovieClipインスタンスの情報が、Event.ENTER_FRAMEイベントの発生するたびに、trace()関数により[出力]パネルに表示されます(図04-011)。関数xFollowMouse()内のステートメントで、trace()関数に指定した引数は3つあります。 図04-011■trace()ステートメントを有効にして[ムービープレビュー]で確認する
第1は、関数xFollowMouse()が呼出されているターゲットのインスタンスを参照するthisです。タイムラインに配置した3つのMovieClipインスタンスが、それぞれのxStart()関数を呼出されてアニメーション開始しますので、[出力]パネルに情報を表示されるインスタンスも3つあります。しかし、[出力]パネルにおける表記は、どれも同じ[[object Pen_1]となっています。 "object"の後に示される識別子は、シンボル名をもとに自動的に生成されるようです。したがって、インスタンスが異なっても、シンボルが共通なら同じ表記になってしまいます。
そこで、trace()関数の第2の引数として、DisplayObject.nameプロパティを指定しました。このプロパティは、MovieClipインスタンス名を文字列で返します。各インスタンスには異なった識別子を設定してあますので、この値はMovieClipインスタンスごとに違います。これで、3つのMovieClipインスタンスの[出力]を、区別することができます。 trace()関数に指定した第3の引数は、変数nDecelerationです。この変数には、MovieClipインスタンスごとに、異なった減速率が値として設定されているはずです。値が意図どおりに正しく設定されているかを、[出力]された結果により確認することができます。前掲図04-011の[出力]パネルの表示は、意図どおりの結果を示しています。 つぎに、メインタイムラインに記述した、各MovieClipインスタンスのxStart()関数を呼出すフレームアクションを削除(もしくはコメントアウト)すると、[出力]はまったくされなくなります。よって、各MovieClipインスタンスのEvent.ENTER_FRAMEイベントにリスナー関数は登録されておらず、アニメーションを処理する関数xFollowMouse()も呼出されていないことが確認できます。 ○04-07 パラメータを加える ●パラメータとして固定値を設定 [プロパティ]インスペクタでインスタンスの[カラー]を設定する場合(図04-013)と異なり、アルファ値はパーセンテージ(%)でなく、小数値で指定することに注意しましょう。 図04-013■[プロパティ]インスペクタの[カラー]設定
スクリプト04-008が設定されたMovieClipインスタンスのアニメーションは、低い減速率が設定されたインスタンスほど、マウスポインタに遅れて追随することになります。したがって、減速率の変数nDecelerationの値が小さいインスタンスほど低いDisplayObject.alphaプロパティ値を設定すれば、意図どおりに遅いMovieClipインスタンスほどアルファ値が下がることになります。 先ほどマウスポインタに追随するMovieClipインスタンスを3つ置いて試したときは、減速率としてそれぞれ0.6と0.4、0.2を設定しました。スクリプト04-008に追加する処理では、ポインタに一番速く近づく減速率0.6のインスタンスを、アルファ値が1になるようにしたいと思います。そこで、減速率に0.4を加えて、MovieClipインスタンスのDisplayObject.alphaプロパティを設定することにします(スクリプト04-009)。 スクリプト04-009■アルファ値の変更を追加したMovieClipのフレームアクション
メインタイムラインのフレームアクションに前回と同じく以下のステートメントを記述して、[ムービープレビュー]を確認すると、マウスポインタに遅れて追随するMovieClipインスタンスほど低いアルファが設定されます(図04-014)。 my0_mc.xStart(0.6);図04-014■アルファ値の設定を加えたマウスポインタに追随するMovieClipインスタンス
このアルファの処理(スクリプト04-009)に特に問題はないものの、少し不満が残ります。ひとつは、MovieClipインスタンスに設定する減速率を、限定していることです。マウスポインタに一番速く追いつくインスタンスの減速率を0.6として、アルファ値を調整する値の0.4を決めました。つまり、この減速率が0.8になったり、0.5に変わったりすれば、0.4という調整値をいちいち書替えなければなりません。 もうひとつは、MovieClipインスタンスごとに、アルファ値が固定していることです。これは好みの問題ではあります。しかし、せっかくのインタラクティブなアニメーションですから、マウスポインタの動きに応じて、インスタンスごとのアルファも変化させてみたいと思います。 ●変化するパラメータ値を設定 もっとも、インスタンスの(座標や角度などの)動きと比べると、アルファは細かな変化までわかりません。そこで、パラメータとしてはMovieClipインスタンスとマウスポインタとの距離を測り、アルファはその値に反比例させてみます。 まず、2点間の距離は「三平方の定理」(後述数学編Math 02「三平方(ピタゴラス)の定理」参照)によって求めることができます。2点をそれぞれ(x1, y1)と(x2, y2)とすれば、その間の距離lは、つぎの式で表されます。 【2点(x1, y1)と(x2, y2)との距離l】
もっとも、今回計算するのは、MovieClipインスタンス(の基準点)とマウスポインタの座標との距離です。MovieClipインスタンスをターゲットとすれば、基準点は座標(0, 0)です。したがって、インスタンス(の基準点)の座標(0, 0)からマウスポインタの座標までの距離nDistanceは、DisplayObject.mouseXとDisplayObject.mouseYプロパティの値をそのまま用いて、つぎの式で求められます。 var nDistance:Number = Math.sqrt(Math.pow(mouseX, 2)+Math.pow(mouseY, 2)); Mathクラスの新しいメソッドが、ふたつ使われています。第1のMath.sqrt()メソッドは、引数に渡した数値の平方根(square root)を返します。なお、引数は0以上の数値でなければならず、負の数を渡すとNaNが返ります。
第2のメソッドは、累乗あるいはべき乗(power)を計算するMath.pow()です。引数はふたつあり、aのn乗、anを得るには、つぎのように指定します。 var nAnswer:Number = Math.pow(a, n);
マウスポインタまでの距離を計算して、距離に反比例した値をアルファに与える処理は、毎フレームのアニメーションとして行います。そこで、前掲スクリプト04-009の関数xFollowMouse()に、つぎのような修正を加えました(スクリプト04-010)。 スクリプト04-010■アルファ値をマウスポインタからの距離に反比例させたフレームアクション
第1に、インスタンス(の基準点)からマウスポインタまでの距離を求める式については、すでに説明しました。ただし、マウスポインタの座標値であるDisplayObject.mouseXとDisplayObject.mouseYプロパティの値は、予めローカル変数(nXとnY)に格納しています。これは、ローカル変数の値を取得する方が、インスタンスのプロパティにアクセスするより、スピードが速いからです。 もちろん、インスタンスのプロパティへのアクセスが1度きりであれば、ローカル変数に代入する意味はありません。しかし、その値を同じ関数内の複数のステートメントで使う必要がある場合は、ローカル変数に代入すると、アクセススピードを稼ぐことができます。また、ターゲットのインスタンスをドット(.)で指定しているときには、ローカル変数を使うことで記述を短くすることもできます。 上記スクリプト04-010では、マウスポインタの座標値は、インスタンスの座標の移動と今回追加したマウスポインタまでの距離の計算の2箇所で使うことになったため、ローカル変数に代入することにしました。
第2に、マウスポインタまでの距離に反比例してアルファを設定するステートメントに、説明していない項がふたつ加えられています。アルファを単にマウスポインタまでの距離に反比例させるなら、一見つぎのステートメントでよさそうに思われます。 alpha = 1/nDistance; しかし、まず代入式右辺の分子を1にすると、変数nDistanceの値つまりマウスポインタまでの距離が1ピクセルまで近づかないと、インスタンスが完全な不透明となる1(100%)になりません。10ピクセル離れてしまえば、アルファ値は0.1(10%)になってしまいます。 そこで分子に設定した変数nRangeは、var宣言で初期値として40が与えられています。したがって、マウスポインタまで40ピクセルの距離まで近づけば、インスタンスは完全に不透明なアルファ値1になります。 つぎに、代入式右辺の分母である変数nDistanceに加えた数値1が問題となります。これは、ともかくもこの+1を取除いてみると、必要性は直ちにわかります。+1を除き分母を変数nDistanceのみにして[ムービープレビュー]を試すと、マウスを止めてMovieClipインスタンスがポインタの位置に達したときに消えてしまいます(図04-015)。 図04-015■DisplayObject.alphaに値を設定する右辺の分母に1加算しない場合
問題は、MovieClipインスタンスがマウス座標に追いついて距離が0になったときです。0による割り算は数学では定義されていませんし、ActionScriptでも正の無限大を意味するInfinityという特殊な値になります。この値は、算術演算の対象としたり、数値として扱うのに適さない場合が少なくありません。実際、DisplayObject.alphaプロパティに代入すると、0が設定されてしまいます。 変数nDistanceは、マウスポインタまでの距離を表しますので、必ず0以上の数値になります。したがって、0による割り算を避けるには、正の値を何か加えればよいでしょう。そこで、スクリプト04-010のDisplayObject.alphaプロパティへの代入式右辺は、分母に1を足してある訳です。
[ムービープレビュー]で試すと、マウスポインタからの距離が離れるほどアルファが下がり、近づくにつれて不透明になります(図04-017)。 図04-017■マウスポインタからの距離にアルファが反比例する
○Column 04 変数宣言と関数定義の初期化時期 ●スクリプトペイン内の場合
最初のステートメントで、後にfunction定義した関数xTest()を呼出しています。そして、その関数内では、さらに後のステートメントでvar宣言されている変数iの値を、trace()関数により[出力]しています。その結果、[出力]パネルには0が表示されました(図04-018)。 図04-018■後に定義した関数を呼出してさらにその後で宣言した変数値を[出力]
第1に、[出力]パネルに値が表示されたのは、関数xTest()が呼出されたことを示します。第2に、出力された値0は、int型で宣言された変数の初期値です。したがって、変数宣言もすでに有効であることがわかります。
●同じタイムラインの別フレームの場合
[出力]パネルには、やはり0が表示されます。したがって、後のフレームで定義されている関数を呼出し、さらにその後のフレームに宣言されている変数にアクセスできることがわかります。 ●入れ子になったタイムラインの場合 テストとしてスクリプト04-011と同一の処理内容を、今度は階層化されたインスタンスに分けて設定してみました(スクリプト04-013)。まず、メインタイムラインから、そこに配置した子のMovieClipインスタンスparent_mcに定義された関数xTest()を呼出します。そして、関数xTest()で[出力]する変数iは、さらに入れ子にした孫のインスタンスchild_mcのフレームアクションで宣言しています(図04-021)。 スクリプト04-013■メインタイムラインから子の関数を呼出し孫に変数宣言
図04-021■階層化された子に関数定義しさらに孫で変数宣言
[ムービープレビュー]を試すと、やはりint型変数のデフォルト値0が[出力]パネルに表示されます。 ●変数宣言と関数定義はどこに記述すべきか しかしもちろん、どこにでも書けるということと、そうすることがよいかどうかは別の問題です。スクリプトは可能なかぎりまとめた方が、わかりやすいですし、管理もしやすいでしょう。したがって、本書では基本的に、タイムラインの第1フレームアクションにスクリプトをまとめることにします。また変数宣言は冒頭に、関数定義もフレームアクションの前の部分に記述します。 作成者: 野中文雄 Copyright © 2001-2007 Fumio Nonaka. All rights reserved. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||