ActionScript 3.0で始めるオブジェクト指向スクリプティング

第17回3D風に回転するアニメーション

前回の第16回三角関数を使った楕円軌道のアニメーションでは、インスタンスが楕円軌道でアニメーションするフレームアクションを作成した。今回はそのスクリプトに手を加えて、遠近感のある3D風の回転を表現してみたい。

簡易なパースペクティブを与える

前回作成したスクリプト16-4は、第16回の2ページ目からダウンロードできる。ここまでの段階では、インスタンスがただ楕円軌道を描いて回転するだけだ。なお、フレームアクションに確認用に挿入したtrace()関数のステートメントは、コメントアウト(コメント区切り記号で無効化)した。また、この後のアニメーション表現の都合で、インスタンスのイラストは左向きとする図1⁠。

図1 インスタンスが楕円軌道を描いて回転する
図1 インスタンスが楕円軌道を描いて回転する

「パースペクティブ」⁠perspective)とは遠近法を意味する(⁠⁠パース」と略されることもある⁠⁠。もっとも、今回はごく簡易な表現で済ませる。修正点はふたつだ。第1に、インスタンスは平面をイメージして、楕円軌道の両端に近づくほど幅を狭くする。第2は、楕円軌道の上を3次元の奥とみなし、上にいくほどインスタンスのサイズを小さくする図2⁠。

図2 端にいくほど幅は狭く上にいくほどサイズを小さく
図2 端にいくほど幅は狭く上にいくほどサイズを小さく

インスタンスの幅やサイズを変えるには、DisplayObject.scaleXとDisplayObject.scaleYプロパティを使う。これらのプロパティは、[変形]パネルの幅と高さのスケールに当たる図3⁠。ただし、単位はパーセンテージ(%)ではなく、実寸を1.0とする小数値であることに注意しよう[1]⁠。

図3 [変形]パネルの幅と高さのスケール
図3 [変形]パネルの幅と高さのスケール

遠近感を与えるためのスケールの変更は、新たな関数xScale()を定義して処理することにする。もちろん、この関数もインスタンスのDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)にリスナーとして加える。まず、第1の幅を変化させる処理から書いていこうスクリプト1⁠。

スクリプト1 楕円軌道の端にいくほどインスタンスの幅を狭める
// MovieClip: 回転するインスタンス
var nDegree:Number = 0;
var nRadian:Number = 0;
var nSpeed:Number = 5;
var nDegreeToRadian:Number = Math.PI/180;
var nCenterX:Number = stage.stageWidth/2;
var nCenterY:Number = stage.stageHeight/2;
var nRadiusX:Number = 100;
var nRadiusY:Number = 50;
var nCos:Number = Math.cos(nRadian);
var nSin:Number = Math.sin(nRadian);
addEventListener(Event.ENTER_FRAME, xMoveX);
addEventListener(Event.ENTER_FRAME, xMoveY);
addEventListener(Event.ENTER_FRAME, xScale);   // 追加
addEventListener(Event.ENTER_FRAME, xUpdate);
function xMoveX(eventObject:Event):void {
  x = nCenterX+nCos*nRadiusX;
}
function xMoveY(eventObject:Event):void {
  y = nCenterY+nSin*nRadiusY;
}
function xScale(eventObject:Event):void {   // 追加
  scaleX = nSin;
}
function xUpdate(eventObject:Event):void {
  nDegree += nSpeed;
  nDegree = (nDegree%360+360)%360;
  nRadian = nDegree*nDegreeToRadian;
  nCos = Math.cos(nRadian);
  nSin = Math.sin(nRadian);
}

[ムービープレビュー]を見ると、インスタンスは楕円軌道の端にいくほど幅が狭くなる図4⁠。新たに定義した関数xScale()の処理はというと、DisplayObject.scaleXプロパティに単にsin値(nSin)を設定しているだけだスクリプト1⁠。

図4 楕円軌道の端にいくほどインスタンスの幅が狭まる
図4 楕円軌道の端にいくほどインスタンスの幅が狭まる

sinの値の変化は、インスタンスの垂直方向の座標に比例係数として与えた。そして、垂直方向の最下部で最大値1、最上部で-1の値を取った。さらに、楕円軌道の両端になる、垂直方向の中央で値は0になる。忘れてしまった読者は、前回の解説をもう1度読んでほしい。また、DisplayObject.scaleX/DisplayObject.scaleYプロパティの特色として、マイナスの値を与えるとインスタンスが反転する。だから、sin値をそのままプロパティに設定するだけでよいという訳だ。

つぎに、第2のインスタンスのサイズを変える処理である。DisplayObject.scaleXとDisplayObject.scaleYの両プロパティの値を変える。インスタンスの大きさも、やはり垂直方向の座標に比例すべきなので、sin値が使えそうだ。仮想の3次元でもっとも手前となる楕円軌道の最下部が1、一番奥の最上部のとき0.8にサイズを設定することにしよう。

そこで、sin値に比例した一定の範囲の値を返す関数があると便利だ。関数名をxGetIndexZ()とし、必要な垂直方向最上部(sinが-1のとき)の値と最下部(sinが1のとき)の値を、ふたつの引数として渡す。すると、関数xScale()はつぎのように記述すればよいことになる。

    function xScale(eventObject:Event):void {
      scaleX = scaleY = xGetIndexZ(0.8, 1);
      scaleX *= xGetIndexZ(-1, 1);
    }

関数xScale()本体の第1ステートメントでは、xGetIndexZ()関数により、垂直位置に応じてインスタンスを最上部0.8から最下部1.0のスケールに設定する。関数の戻り値をふたつのプロパティDisplayObject.scaleXとDisplayObject.scaleYに代入する式は1行で記述した。各プロパティには、最右辺の関数xGetIndexZ()から返される値が設定される。

第2ステートメントも、右辺はxGetIndexZ()関数を呼出すことにした。引数が-1と1なので、nSinと値は同じになる。ただし、プロパティへの値の設定に、乗算後代入演算子*=を使っていることに注意してほしい。第1ステートメントのサイズをもとに、さらに水平方向スケールを変える必要があるからだ。

関数xGetIndexZ()の定義は、以下のようになる。sinは±1の範囲の値をとるので、1を足して2で割れば0から1までの値になる。それに、引数で渡された最小値から最大値までの値の幅を乗じ、最小値を加えることで、求める数値が得られる。

    function xGetIndexZ(nMin:Number, nMax:Number):Number {
      var nIndexZ:Number = (nMax-nMin)*(nSin+1)/2+nMin;
      return nIndexZ;
    }

以上の修正を加えて、楕円軌道の上にいくほどインスタンスのサイズを小さくするようにしたのが、つぎのスクリプト2だ。

スクリプト2 楕円軌道の上にいくほどインスタンスのサイズを小さくする
// MovieClip: 回転するインスタンス
var nDegree:Number = 0;
var nRadian:Number = 0;
var nSpeed:Number = 5;
var nDegreeToRadian:Number = Math.PI/180;
var nCenterX:Number = stage.stageWidth/2;
var nCenterY:Number = stage.stageHeight/2;
var nRadiusX:Number = 100;
var nRadiusY:Number = 50;
var nCos:Number = Math.cos(nRadian);
var nSin:Number = Math.sin(nRadian);
addEventListener(Event.ENTER_FRAME, xMoveX);
addEventListener(Event.ENTER_FRAME, xMoveY);
addEventListener(Event.ENTER_FRAME, xScale);
addEventListener(Event.ENTER_FRAME, xUpdate);
function xMoveX(eventObject:Event):void {
  x = nCenterX+nCos*nRadiusX;
}
function xMoveY(eventObject:Event):void {
  y = nCenterY+nSin*nRadiusY;
}
function xScale(eventObject:Event):void {
  scaleX = scaleY=xGetIndexZ(0.8, 1);
  scaleX *= xGetIndexZ();
}
function xUpdate(eventObject:Event):void {
  nDegree += nSpeed;
  nDegree = (nDegree%360+360)%360;
  nRadian = nDegree*nDegreeToRadian;
  nCos = Math.cos(nRadian);
  nSin = Math.sin(nRadian);
}
function xGetIndexZ(nMin:Number=-1, nMax:Number=1):Number {
  var nIndexZ:Number = (nMax-nMin)*(nSin+1)/2+nMin;
  return nIndexZ;
}

このスクリプト2には、補足がひとつある。関数xGetIndexZ()の引数に、代入演算子で値-1と1が指定されていることだ。この引数に用いられた代入演算子は、デフォルト値の設定になる。つまり、引数の数が足りなければ、そのデフォルト値が引き数として補われる。そして、関数xScale()本体の第2ステートメントでは、引数なしで xGetIndexZ()が呼出されている。すると、ふたつの引数のデフォルト値-1と1が渡されたものとして処理が行われるのだ。

[ムービープレビュー]を確かめると、楕円軌道の端にいくほどインスタンスの幅は狭く上にいくほどサイズが小さくなって回転するアニメーションが実行される図5⁠。

図5 楕円軌道の端にいくほどインスタンスの幅は狭く上にいくほどサイズが小さくなる
図5 楕円軌道の端にいくほどインスタンスの幅は狭く上にいくほどサイズが小さくなる

ダイナミックにぼかしを加える

Flash CS3でムービー作成(「オーサリング」ともいう)のときに、[プロパティ]インスペクタの[フィルタ]タブを用いて、インスタンスにフィルタを適用することができる図6⁠。ActionScriptを使うと、適用するフィルタの種類やパラメータをダイナミックに設定・変更することが可能だ。楕円軌道のアニメーションで仮想3Dの奥、つまり軌道上部にいくほどぼかしを加えれば、遠近感がより増してくる。

図6 [プロパティ]インスペクタの[フィルタ]タブでぼかしフィルタを適用したインスタンス
図6 [プロパティ]インスペクタの[フィルタ]タブでぼかしフィルタを適用したインスタンス

スクリプトでぼかしフィルタを適用するには、BlurFilterクラスを用いる。ぼかしフィルタ(BlurFilter)にかぎらず、スクリプトでフィルタを適用するには、基本的につぎの4つの手順が必要だ。

フィルタの適用手順
  1. 配列(Array)インスタンスを作成する。
  2. フィルタインスタンスを作成する。
  3. フィルタインスタンスを配列インスタンスに格納する。
  4. インスタンスのDisplayObject.filtersプロパティに配列インスタンスを代入する[2]⁠。

たとえば、タイムラインにMovieClipインスタンスmy_mcが配置されているとき、そのインスタンスにぼかしフィルタをかけるには、つぎのようなフレームアクションを記述する。

    var filters_array:Array = new Array();
    var myBlur:BlurFilter = new BlurFilter();
    filters_array.push(myBlur);
    my_mc.filters = filters_array;

BlurFilterクラスのコンストラクタには、引数が指定できる(オプション⁠⁠。第1引数が水平方向、第2引数は垂直方向のぼかし量だ[3]⁠。省略すると、ともにデフォルト値の4が適用される。

前掲フレームアクション(スクリプト2)に新たな関数xBlur()を定義して、インスタンスに対してその垂直位置で変化するぼかしを与えてみよう。この関数も、インスタンスのDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナーに加えるスクリプト3⁠。

スクリプト3 垂直位置でぼかしを変化させるフレームアクション
// MovieClip: 回転するインスタンス
var nDegree:Number = 0;
var nRadian:Number = 0;
var nSpeed:Number = 5;
var nDegreeToRadian:Number = Math.PI/180;
var nCenterX:Number = stage.stageWidth/2;
var nCenterY:Number = stage.stageHeight/2;
var nRadiusX:Number = 100;
var nRadiusY:Number = 50;
var nCos:Number = Math.cos(nRadian);
var nSin:Number = Math.sin(nRadian);
addEventListener(Event.ENTER_FRAME, xMoveX);
addEventListener(Event.ENTER_FRAME, xMoveY);
addEventListener(Event.ENTER_FRAME, xScale);
addEventListener(Event.ENTER_FRAME, xBlur);   // 追加
addEventListener(Event.ENTER_FRAME, xUpdate);
function xMoveX(eventObject:Event):void {
  x = nCenterX+nCos*nRadiusX;
}
function xMoveY(eventObject:Event):void {
  y = nCenterY+nSin*nRadiusY;
}
function xScale(eventObject:Event):void {
  scaleX = scaleY=xGetIndexZ(0.8, 1);
  scaleX *= xGetIndexZ();
}
function xBlur(eventObject:Event):void {   // 追加
  var nBlur:Number = xGetIndexZ(4, 0);
  var myBlur:BlurFilter = new BlurFilter(nBlur, nBlur/2);
  filters = [myBlur];
}
function xUpdate(eventObject:Event):void {
  nDegree += nSpeed;
  nDegree = (nDegree%360+360)%360;
  nRadian = nDegree*nDegreeToRadian;
  nCos = Math.cos(nRadian);
  nSin = Math.sin(nRadian);
}
function xGetIndexZ(nMin:Number=-1, nMax:Number=1):Number {
  var nIndexZ:Number = (nMax-nMin)*(nSin+1)/2+nMin;
  return nIndexZ;
}

ぼけ幅は回転のアニメーションの最前面(軌道最下部)は0、最背面(軌道最上部)を4として、インスタンスの垂直位置に応じて変える。そこで、関数xBlur()本体の第1ステートメントでは、4と0を引数としてxGetIndexZ()関数を呼出し、ぼけ幅の値を得ている。

第2ステートメントは、そのぼけ幅の値をBlurFilter()コンストラクタの引数に渡して、ぼかしフィルタのインスタンスを作成する。ただし、水平に回転する想定のアニメーションなので、垂直方向のぼけ幅(第2引数)は値を半分にした。

第3ステートメントは、BlurFilterインスタンスを納めた配列のDisplayObject.filtersプロパティへの設定だ。前述のフィルタの適用手順では4つに分けたステップのうち、配列の作成とフィルタインスタンスの格納、およびそのDisplayObject.filtersプロパティへの設定の3つを、1つのステートメントで済ませている[4]⁠。

[ムービープレビュー]でアニメーションを実行すると、楕円軌道の上部にいくほどぼかしがかかり、焦点がずれたような遠近感の効果を与える図7⁠。

図7 BlurFilterクラスによるぼかしの効果で遠近感を出す
図7 BlurFilterクラスによるぼかしの効果で遠近感を出す

次回からは、クラスを自分で定義する「カスタムクラス」について解説する。いよいよ本格的なプログラミングの考え方を学習することになる。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧