■第1回 はじめに
■第2回 神の計画
■第3回 誰のどこに「いつ、だれの、何を、どうする」を書く
■第4回 変数を使いこなす
■第5回 Mathランダム徹底研究
■第6回 回転のメカニズム(番外編)
■第7回 正六面体の回転(番外編)

 第6回 回転のメカニズム(番外編) 2004.06.08 .
 自在に回転する球体を作る

601

前回からかなり日が経過してしまいました。インベーダゲームを仕上げたいのですが、ちょっと寄り道をして、FLASHにおける図形の回転について、いろいろ考えてみたいと思います。
今回の最終目標は、以下の”球体の回転”をアクションスクリプトで制作することです。



マウスの左右移動で回転速度及び回転方向が変更され、上下移動で回転面の角度が変更されます。
また回転速度に応じて遠心力やズーム機能も働くようになっています。
※演算負荷が大きいのでマシンによっては動きがギクシャクします。


 モーショントゥイーンによる基本の回転

602

FLASHを始められたころ、ステージに描いた文字や図形が簡単に回転することに驚きと感動を味わった方は、憲ちゃんも含めてたくさんおられると思います
だから最初のうちは嬉しくて何でもかんでも、ぐるぐる回したものです(笑)

603

図形を回転させる最も基本的な方法は、まず・・
@中心(基準)点をずらせた図形のグラフィックシンボルまたはムービークリップシンボルをステージ中央に図のように配置し、適当な間隔をおいて「キーフレームを挿入」します。
※このキーフレームの間隔が大きいほどゆっくり回転し、小さいほど速く回転します。
※シンボルの中心(基準)点のずらせ方は、Q&A過去記事の■アニメを往復運動させる方法(Response04)、■MX2004オブジェクト中心点・基準点についてを参照してください。



A開始キーフレームをクリック選択して、「フレーム」パネルで「モーション」を選択、回転を「時計回り(反時計回り)」に設定、回数をキー入力します。

インスタンス(シンボルをステージ上に配置したもの)の中心(基準)点を中心にコンパスのように回転しますので、描く軌道は正円になります。
※サンプルは、ここをクリック。 (ball_kaiten0.fla

604

正円ではなく楕円軌道上を回転させる場合には・・
@「ガイドレイヤー」を使って、そこに「楕円ツール」で、ガイドとなる楕円の線を描きます。
 楕円を描いたあと、必ずどこか1箇所線を切断しておきます。
A回転させる図形は、上記同様グラフィックシンボルでもムービークリップシンボルでも構いませんが、シンボルの中心(基準)点は、図形の中心とします。



B適当な間隔をおいて「キーフレームを挿入」、切断した楕円の最初と最後の位置に、それぞれのインスタンスの中心(基準)点を重ねるように配置します。
C開始フレームをクリック選択して、「フレーム」パネルで「モーション」を選択、回転を「自動」に設定します。

※サンプルは、ここをクリック。 (ball_kaiten1.fla

ご参考までに過去記事の■ガイドレイヤーとモーションの関係も参照してください。


605

これらモーショントゥイーンで作成した図形の回転は、タイムライン上でループするだけのアニメーションですから、ボタンやマウスなど外部からの操作によって制御する場合、基本的にタイムラインの停止・進行ぐらいしかできません。
任意に回転速度や軌道を修正しようとすれば、同じ動きをアクションスクリプトを使って”数値”で動かし、その数値をボタンやマウスの動作と連動させて変更するのが一般的な考え方です。
ということで、これらと同じような動きをアクションスクリプトを使って以下に実現してみることにします。


 モーショントゥイーンの回転をアクションスクリプトで再現!

606

モーショントゥイーンの場合、対象となる図形はシンボルであれば、グラフィックでもボタンでもムービークリップでも良かったのですが、アクションスクリプトを使って直接図形を制御するためには、その図形は「ムービークリップ」でなければなりません。(ただし、MX以降は例外あり)

そのことをおさえたうえでまず、上記603の正円の回転ですが、
@ 同様に中心(基準)点がずれた「ムービークリップシンボル」をステージの中央に配置。
ステージに配置されると、その時点でシンボルは、シンボルの分身であるインスタンスに変身し、インスタンス固有の「プロパティ」をもつことはこれまでの説明のとおりです。

この場合に必要なプロパティは”回転角度”で「_rotation」であらわされます。
最初ステージに配置された状態が、回転角度0度。

   this._rotation = 0;

の状態で、自らの中心(基準)点を中心に時計回りに値が増えていきます。
アクションスクリプトで、

  this._rotation = 30;

としてやると、元の状態から時計回りに30度回転して傾きます。
したがって、フレームレート(通常1/12秒)ごとにこの値を少しずつ加えていけば、時計回りにぐるぐる回転し、逆に少しずつ減じていけば、反時計回りにぐるぐる回転することになります。

Aステージのムービークリップをクリック選択して、「アクションパネル」で以下のようなクリップアクションを記載します。

   onClipEvent (enterFrame) {  フレームレートごとに以下のことを繰り返す
     this._rotation += 10;; ←自分の回転角度に「10」を加える
   }

12分の1秒ごとに毎回、自分の回転角度「_rotation」の値が10ずつ大きくなるので、時計回りに回転します。
この値が大きいほど回転は速く、小さいほど遅く回転します。
※サンプルは、ここをクリック。 (ball_kaiten00.fla

この方法を応用したものとして、Q&A過去記事サンプルでは・・・■観覧車のようなボタン
■大円の中を小円が滑らず転がるScript・・・  ■内接する回転円の定点軌跡
などがありますので参考にしてください。

607

上記604の楕円の場合は、回転角度だけでなく回転半径も刻々変化するので、上記の方法では少しむつかしそうです。

そこで、座標の原点を中心とする円周上のムービークリップの座標を、その回転角度と半径から求める式を使います。昔習ったことのあるような三角関数ですが、理屈はどうでも・・・。
要するに円周上の1点の座標値は、「回転角度θ」と「回転半径R」から、

 x = cosθ×R
 y = sinθ×R

このような式で求められます。
ただし、この場合の「回転角度θ」はラジアン角といって、普通の360度の角度とは別の単位です。 普通の角度をラジアン角に変換する式は、

  ラジアン角θ = 普通の角度×π/180

重要なのはこの式の応用ですから、式自体についてあまり深く追求しないで結果だけを丸覚えしてください(笑)
そして、普通の角度(「_rotation」の値に相当)を変数kakudo、ラジアン角を変数theRadian、半径を変数Rとしたとき、これをスクリプトで表現すると、

   theRadian = kakudo * Math.PI / 180; ※先に普通の角度をラジアン角に変換して
   this._x = Math.cos( theRadian ) * R;    それをそれぞれの式に当てはめます。
   this._y = Math.sin( theRadian ) * R

このようになります。

具体的には・・・
@まず、中心(基準)点が中央にある「球体」ムービークリップをステージの任意の場所に配置します。
AFLASHでは、シーン編集画面のステージにあるムービーの左上が座標の原点「x=0,y=0」ですから、そのままでは再生したときムービーの左上を中心に回転してしまいます。
ムービーの任意の位置(例えば中央)を中心に回転させるには、次の2つの方法があります。
ひとつは、上記の式で円の中心点の座標を指定する方法。
指定したい円の中心点の座標を、(xCenter,yCenter) としたとき、

   this._x = Math.cos( theRadian ) * R + xCenter
   this._y = Math.sin( theRadian ) * R + yCenter; 

このように記載します。
ムービーの大きさが200×200なら、その中央の座標は「x=100,y=100」ですから、それぞれに100の値を加えます。

   this._x = Math.cos( theRadian ) * R + 100
   this._y = Math.sin( theRadian ) * R + 100

これで、ムービーの中央を中心に回転します。
もうひとつは、回転の対象である「球体」ムービークリップをさらに別のムービークリップで包み込む方法。
こうすることで回転図形の座標の原点は、それを包み込んだムービークリップの中心(基準)点に変わります。
そして、包み込んだムービークリップを、シーン編集画面でムービーの中央に配置すれば、そこが円の中心点になります。
以下のサンプルは、後者の方法で行っています。

@の「球体」ムービークリップをステージでクリック選択。
メニューバー「挿入」→「シンボルに変換」で、名前を仮に「回転エリア」、タイプを「ムービークリップ」、基準点を中央にして「OK」をクリック。
これで左図のように「回転エリア」ムービークリップの中に「球体」ムービークリップが包含されます。

「球体」が描く円の中心(座標の原点)は、それを包含する「回転エリア」ムービークリップの中心(基準)点ですから、「回転エリア」ムービークリップがシーン編集画面でムービーの中央にあれば、そこを基準に回転することになります。

※円運動は「回転エリア」の中心(基準)点を中心にアクションスクリプトで動作しますから、編集時に「球体」を「回転エリア」内のどの位置に配置しても構いません。

Bこの「回転エリア」編集画面上で、「球体」ムービークリップをクリック選択し、「アクションパネル」で以下のようなクリップアクションを記載します。

   onClipEvent (load) {  自分が読み込まれた最初に(初期値として以下を定義)
     R = 100;    ←変数R(半径)に100を代入
     kakudo = 0;  ←変数kakudo(普通の角度)に 0 を代入
   }
    onClipEvent (enterFrame) {  フレームレートごとに以下を繰り返す
     theRadian = kakudo * Math.PI / 180;   ←普通の角度をラジアン角に変換して変数theRadianに代入
     this._x = Math.cos( theRadian ) * R;    ←角度と半径からX座標値を計算して、そこに自分を配置
     this._y = Math.sin( theRadian ) * R * 0.5; ←角度と半径からY座標値を計算して、そこに自分を配置
     kakudo += 10;  ←普通の回転角度に10を加える
   }

Y座標計算で、回転半径Rに「×0.5」とすることは、Y座標の半径を2分の1に縮小することになるので、その分上下に扁平した楕円軌道になるわけです。
したがってこの「0.5」は楕円の「扁平率」ということになり、値を小さくすればするほど楕円軌道が扁平し、「0」で水平軌道、省略すると(「扁平率」 =1)正円軌道になります。
※最後の行で、kakudo += 10;として、毎回、普通の角度を10ずつ大きくしているので、やはり時計回りに回転します。

※サンプルは、ここをクリック。 (ball_kaiten11.fla

608

これから制作しようとする冒頭サンプルの”球体の回転”は楕円軌道であり、これをムービー上のマウスの位置によって、回転速度や楕円の扁平率などを変化させますので、最後の607の手法を応用して作成していくことになります。


 作成手順1(部品の準備)

609

まず、ドキュメント(ムービー)のサイズを「500×400」に設定、背景色やバックグラウンドの図形等はお任せしますが、フレームレートを今回は「30」に設定してください。
一般的にフレームレートは初期値の「12」で十分なのですが、今回なぜ「30(1秒間に30フレーム再生)」かといいますと、速度変化による微妙な動きをビジターにできるだけスムーズに見ていただくためです。
ただし、フレームレートをあげると最新のパソコンでは動きがスムーズになる反面、パソコンの演算処理能力によっては、ぎこちない動きになるリスクがあることも承知しておいてください。


610

シーン編集画面のステージ上の適当な位置に、「楕円ツール」を使って、Shiftキーを押しながら、直径40pxの正円塗り図形を描きます。
描いた図形が選択された状態で「カラーミキサー」を使って、球体に見えるようグラデーションを設定してください。
球体図形が選択された状態で、メニューバー「挿入」→「シンボルに変換」で「シンボルに変換パネル」をだし、名前を「球体」、タイプを「ムービークリップ」、(さらにMXの場合は基準点を中央にして)「OK」をクリックします。
これで回転の対象である「球体」ムービークリップが、シンボルとして「ライブラリ」に登録され、ステージにある元の球体図形はこの時点で「球体」シンボルの分身である「球体」インスタンスに変身します。

611

さらにステージの「球体」ムービークリップが選択された状態で、メニューバー「挿入」→「シンボルに変換」で「シンボルに変換パネル」をだし、名前を「回転エリア」、タイプを「ムービークリップ」にして、「OK」をクリックします。
「球体」ムービークリップが「回転エリア」ムービークリップの中に包含され、シーン編集画面では「回転エリア」ムービークリップが配置された状態に変わります。
シーン編集画面での「回転エリア」ムービークリップの中心(基準)点が、その中にある「球体」ムービークリップが描く円周の中心になりますので、「回転エリア」ムービークリップをムービーの中央に配置してください。

612

ステージで「回転エリア」ムービークリップをダブルクリックして、その編集画面にします。
※ステージを見ているだけでは、シーン編集画面と見分けがつきませんが、上記607下図の赤○部分を確認して、現在の編集画面が何かを判断してください。
ステージの「球体」ムービークリップをクリック選択、「アクションパネル」でまずは、上記607Bのクリップアクションをそのまま記載、メニューバー「制御」→「ムービープレビュー」で動作確認してください。
当然のことながら、上記607のサンプルと同じ回転軌道ですが、フレームレートをあげた分回転速度は速くなります。

以下、このスクリプトをベースに「回転速度」、「楕円の扁平率」、「遠心力」、「球体の遠近表現」、「ズーム」の変化について、ひとつずつスクリプトを追加修正し、動きを検証しながら作業を進めていきます。


 作成手順2(回転速度の変化)

613

回転速度は、スクリプトの最後の行「kaiten += 10;」でした。つまりこの場合、フレームレート(30分の1秒)ごとに普通の角度で10度回転しますから、1秒間に10度×30=300度、秒速300度で回転しているということです。
この数値「10」に代えて変数「speed」を当て、任意に変数の値を変更できるようにすれば回転速度を変化させることができます。

 

ではこの変数speedをどのムービークリップに定義するか(どのムービークリップの所有する変数にするか)という問題ですが…
勿論、この「球体」ムービークリップの変数にしても構いませんが、最終目標として「球体」は複数配置し、どの「球体」も同じ速度に設定したいので、どこかで一括管理した方が合理的です。
そこで、「球体」ムービークリップを包含するひとつ上の階層のムービークリップ、すなわち「回転エリア」ムービークリップで一括管理することにします。
シーン編集画面に戻って、ステージの「回転エリア」ムービークリップをクリック選択、「アクションパネル」で以下のクリップアクションを記載します。

   onClipEvent (load) { 自分(「回転エリア」)が読み込まれた最初に(初期値として以下を定義)
     speed = 10;  ←変数speedに10を代入
   }

とりあえず初期値として「speed = 10;」としておきます。
これで変数speedは「回転エリア」ムービークリップの変数として定義されます。


614

次にこの変数speedの値が、マウスの横方向の動きに連動して値が変化するような仕掛けが必要です。
マウスのムービークリップ上の座標値を調べるプロパティとして「_xmouse」「_ymouse」があります。
「_xmouse」がマウスのX座標値、「_ymouse」がY座標値です。
書式は・・・

  変数A = MCインスタンス名._xmouse;
  変数B = MCインスタンス名._ymouse;

として、指定したムービークリップの中心(基準)点を原点「x=0,y=0」とするマウスの位置座標を教えてくれます。

指定するムービークリップを「回転エリア」自身(this)として、「mouseMove」イベントの中に、

   onClipEvent (mouseMove) { ムービー上でマウスが動いたら
     speed = this._xmouse;  ←マウスのX座標値を変数speedに代入
   }

と記載すると、マウスがステージ上で移動するたびに「回転エリア」ムービークリップの中心(基準)点を座標の原点「0」とする現在のマウスのX座標値を調べ、マウスが原点(=ムービーの中央)より右にあるときは「プラス値」、左にあるときは「マイナス値」が変数speedに代入されます。
これを「球体」ムービークリップの回転に応用した際には、「プラス値」のときは時計回り、「マイナス値」のときは反時計回りに回転するということです。
「値の大きさ」はというと、ムービーの大きさが「幅500×高さ400」ですから、中央から左右に分けると「−250〜+250」の値になります。
しかしこれでは、最大・最小値のときの回転速度speedは、250度×フレームレート30=7500度となって、とても回転とはいえない値になってしまいます。
そこで、最大速度を仮に1秒間に1.5回転と定め、360度×1.5回転=秒速540度として、
変数speedの最大値を逆算すると、540度÷フレームレート30=18度 が得られます。
マウスのX座標の最大値250のとき、変数speedが18(度)になるようにするには、座標値に「18÷250」を掛ければよいわけですから、「回転エリア」ムービークリップのクリップアクションで…

   onClipEvent (load) {
     speed = 10;
   }
   onClipEvent (mouseMove) {
     speed = this._xmouse * 18 / 250
   }

このように記載します。

615

そして「回転エリア」の中の「球体」ムービークリップでは、マウスのX座標で変化する変数speedの値を活用して…。

   onClipEvent (load) {
     R = 100;
     kakudo = 0;
   }
   onClipEvent (enterFrame) {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos(theRadian) * R;
     this._y = Math.sin(theRadian) * R * 0.5;
     kakudo += _parent.speed; ←自分(「球体」)のひとつ上の階層にある変数speedの値を加算
   }

「回転エリア」ムービークリップで定義した変数speedは、「球体」ムービークリップからみて「ひとつ上の階層にあるムービークリップ」の変数なので、「ひとつ上の階層の・・・」という意味の相対パス「_parent」を変数speedの前に「.(ドット)」を介して指定します。

※ここまでのサンプルは、ここをクリック。 (action_ball1.fla
ムービーの上でマウスを左右に移動し、回転速度の変化を確認してみてください。


 作成手順3(扁平率の変化)

616

楕円の扁平率は、上記スクリプトの下から2行目「this._y = Math.sin(theRadian) * R * 0.5;」でした。
回転速度で変数を当てたように、ここでも「0.5」という数値の代わりに変数「henpei」を当て、この変数も同様にひとつ上の階層にある「回転エリア」ムービークリップの変数として定義することとして、「球体」ムービークリップのクリップアクションで…

   onClipEvent (load) {
     R = 100;
     kakudo = 0;
   }
   onClipEvent (enterFrame) {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos(theRadian) * R;
     this._y = Math.sin(theRadian) * R * _parent.henpei
     kakudo += _parent.speed;
   }

としておきます。

617

ひとつ上の階層の「回転エリア」ムービークリップでは、まず変数henpeiの初期値を「0.5」として定義。

   onClipEvent (load) {
     speed = 10;
     henpei = 0.5; ←変数henpeiに0.5を代入
   }

そして今度はこれが、自分の中心(基準)点を座標の原点「0」とするマウスのY座標値に連動するようにします。
ムービークリップの縦中央を原点「0」として、下方向が「プラス値」、上方向が「マイナス値」で、ムービーの高さ400の2分の1から、マウスのY座標値として「−200〜+200」の値が得られます。
「扁平率」は、値が「0」のとき水平軌道、「−1又は1」のとき正円軌道ですから、マウスの座標が「−200又は+200」とき、変数henpeiの値を「−1又は+1」とするためには、座標値に1÷200 をに掛ける、つまり200で割ればよいということです。
したがって、扁平率を加味した「回転エリア」ムービークリップのクリップアクションは…

   onClipEvent (load) {
     speed = 10;
     henpei = 0.5;
   }
   onClipEvent (mouseMove) {
     speed = this._xmouse * 18 / 250;
     henpei = this._ymouse / 200
   }

このようになります。

※ここまでのサンプルは、ここをクリック。 (action_ball2.fla
ムービーの上でマウスを上下左右に移動し、動作を確認してみてください。


 作成手順4(遠心力)

618

遠心力は、ここでは回転速度が大きくなるとそれに比例して回転半径が大きくなるものとします。(実際の遠心力の法則とは異なると思います。)
回転半径は「球体」ムービークリップで「R = 100;」と定義していますが、回転速度と同様これもそれを包含する「回転エリア」ムービークリップで一括管理する方が合理的です。
そこで、「球体」ムービークリップのクリップアクションでは…

   onClipEvent (load) {
                   ←R = 100;を削除して「回転エリア」で定義
     kakudo = 0;
   }
   onClipEvent (enterFrame) {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos(theRadian) * _parent.R
     this._y = Math.sin(theRadian) * _parent.R * _parent.henpei;
     kakudo += _parent.speed;
   }

当初、「load」イベント内で定義していた「R = 100;」を削除して、ひとつ上の階層の「回転エリア」ムービークリップで再定義し、座標計算に用いる変数Rに相対パス指定します。

619

回転半径は、最小速度である「speed = 0」のとき「R = 80」、最大速度である「speed = 18」のとき、ムービーの高さの2分の1と球体自身の大きさも考慮して「R = 170」になるように設定するものとすれば…
変数speedの値が「0→18」のとき、変数Rの値が「80→170」となるような関係式は、

   R = 80 + 90 * ( speed / 18 );

このようになります。(変数speedに具体の数値を入れて確認すればわかります。)
しかし変数speedは実際には「-18〜+18」の値であり(上記614参照)、そのままではマイナス値の半径「R」も存在してしまうので、変数speedの値を絶対値にする必要があります。
絶対値を求めるMathオブジェクトのメソッドは「Math.abs( 数値 )」ですから・・・

   R = 80 + 90 * ( Math.abs( speed ) / 18 );

とします。
この式を「回転エリア」ムービークリップのクリップアクションに追加して…

   onClipEvent (load) {
     speed = 10;
     henpei = 0.5;
     R = 80 + 90 * ( Math.abs( speed ) / 18 ); ※初期値として定義
   }
   onClipEvent (mouseMove) {
     speed = this._xmouse * 18 / 250;
     henpei = this._ymouse / 200;
     R = 80 + 90 * ( Math.abs( speed ) / 18 ); ※各値が変更された後の再計算
   }

このように「load」イベントと「mouseMove」イベントの両方に記載しておきます。
なぜかといいますと・・・
ムービーが再生され「回転エリア」ムービークリップが読み込まれても、マウスが最初にムービー上に乗るまで「mouseMove」イベントは発生しません。
回転半径の計算式を「mouseMove」イベント内に記載しておくだけでは、イベントが発生するまで「球体」は半径を持たないので回転しないことになります。
したがって、同じ式を「load」イベントにも記載します。

※ここまでのサンプルは、ここをクリック。 (action_ball3.fla
ムービーの上でマウスを上下左右に移動し、動作を確認してみてください。

 作成手順5(球体の遠近表現)

620

球体が二次元平面の楕円軌道を回転するのであれば、軌道上のどの位置にあっても大きさは同じに見えます。
しかし三次元空間の中で正円軌道で回転しており、その回転平面が角度を変えることで楕円軌道に見えていると想定したとき、遠くにあるときは小さく、近くにあるときは大きく見えます。
これを平面のムービー座標で、例えば円周上のY座標値に注目して、Y座標がマイナス値(中央より上)のとき小さくなり、プラス値(中央より下)のとき大きくなるようにすれば、立体的に見せることができます。
ムービークリップの大きさを比率(%)で拡大縮小するプロパティが「_xscale」「_yscale」(100が原寸)ですから、
単純に考えれば・・・

  this._xscale = this._y * 調整値A + 調整値B;←「自分のY座標値」と連動して自分の横方向の大きさを変化
  this._yscale = this._xscale;            ←縦方向の大きさを横方向と同じ大きさにする

※調整値は、そのままでは大きすぎたり小さすぎたりするかもしれないので、あとで適当な値を入れて補正します。
これでいきますと、自分(球体)が原点より上にきたときはその値に応じて横幅が小さくなり、下のときはその値に応じて大きくなります。
球体ですから縦横同じなので、同じだけ縦幅も拡大縮小します。
楕円軌道が固定(「扁平率」が固定)されている場合はこれで遠近感を表現できます。

しかしこのサンプルは、マウスの上下移動に応じて、楕円軌道が正円から水平まで任意に変化します。
正円軌道の場合とは、回転面を真上から見ている状態なので、円周上のすべての位置で同じ大きさに見えなくてはいけません。
水平軌道の場合とは、回転面を真横から見ている状態なので、左右往復運動の中で手前に来たとき最大、奥にいったとき最小に見えなくてはいけません。
このいずれの場合も、上記の式では成立しないことは容易にご理解いただけると思います。

621

では、どのような式にすればいいのでしょうか?
”式”を考えるにあたって、各要素と球体の大きさの関係を整理しますと・・・

@「球体の大きさ」は、「球体のY座標」と何らかの連動はするはず。
 ただし「球体の大きさ」は・・・
A「扁平率」が最大の「1」(軌道が正円)のとき「球体のY座標」に影響されず、
 以後「扁平率」の値が減少する程度に応じて影響を増し、
 「扁平率」が「0」(軌道が水平)のとき、最大の影響を受ける。
B「扁平率」が最小の「−1」 (逆回転の正円軌道)のとき「球体のY座標」に影響されず、
 以後「扁平率」の値が増加する程度に応じて影響を増し、
 「扁平率」が「0」(軌道が水平)のとき、最大の影響を受ける。

以下の文章を読む前に、ここであなたもじっくり考えてみてください(^^g/~~

622

憲ちゃんも堂々巡りしながら結構複雑な式も考えましたが、何とか・・・

   球体の大きさ =100+球体のY座標×{1−絶対値(扁平率)}×調整値

ここまで、こぎつけました?(※?の意味はのちほど・・・)

※{1−絶対値(扁平率)}は、「扁平率」の値が「球体のY座標」に与える影響を正反対にするためのもので、「扁平率」の値が”−1→0→+1”と変化したとき、この式で”0→1→0”と変化させます。
※「球体のY座標」は、楕円軌道上をプラス・マイナスの値をとりながら変化しますので、式の青字部分は「反扁平率」の影響を受けながら、「マイナスの一定の値→0→プラスの一定の値」に変化します。
※元の球体の大きさを「100(%)」として、それにプラスマイナスそれぞれ変化する一定の値を加えますから、
「球体の大きさ」は、「100」を中心として例えば「70→100→130」というように変化します。
※「70→100→130」にするのか「10→100→190」にするのかは「調整値」で、作品を動かしながら決めていきます。

この式を「球体」ムービークリップのクリップアクションに書き加えると・・・

   onClipEvent (load) {
     kakudo = 0;
   }
   onClipEvent (enterFrame) {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos(theRadian) * _parent.R;
     this._y = Math.sin(theRadian) * _parent.R * _parent.henpei;
     this._xscale = 100 + this._y * ( 1 - Math.abs( _parent.henpei ) ) * 0.8
     this._yscale = this._xscale
     kakudo += _parent.speed;
   }

※ここまでのサンプルは、ここをクリック。 (action_ball4.fla
ムービーの上でマウスを上下左右に移動し、その動作を確認してみてください。

623

うまくいったと思ったのですが・・・
特に水平軌道付近のときの球体の大きさに注意していただくと、左右の中央付近に来ても大きさが変化せず、元の球体の大きさのまま往復運動しているようです(;^_^Aアセアセ・・・
どうも失敗のようですね!!
もっとよく動きを観察すると、水平軌道と正円軌道のちょうど中間あたり(「扁平率」が±0.5)で大小の比率が一番顕著になっています。
確かに水平軌道のとき、「反扁平率」の影響は最大の「1」になりますが、改めて冷静に考えてみると・・・

   球体の大きさ=100+球体のY座標×{1−絶対値(扁平率(H))}×調整値

このとき「球体」は「Y=0」の線上を横に往復しているだけなので、「球体のY座標」は常に「0」であり、式に当てはめると「球体の大きさ」は永久に「100」のままです!
軌道が水平から上下に離れるほど、「反扁平率」の影響が少なくなりますが、一方「球体のY座標」の上下の振幅の値が大きくなり、「扁平率」が±0.5あたりで交差し大小の比率が最大になったのでした。
むつかしいものですね(笑) 。

ということで、折角の考えもここには適用できず別の方法を考えなければならないことになりました!
このようにアクションスクリプトで動かすときには、実際に動かしてみて初めて気が付くことがよくあるのです(^^g/~~

624

そこで、またいろいろ考えあぐんだ末にひとつのアイデアが浮かびました!
球体が実際に移動する楕円軌道とは別に、「扁平率」を持たない(つまり「扁平率」 =1の)仮想の正円軌道を重ね、それぞれの軌道上における同じ回転角度の「Y座標」の差を求め、それと「球体の大きさ」と関連付けられないか?ということです。

球体が移動する楕円軌道のY座標を(B)、同じ回転角度(θ)をもつ正円軌道上のY座標を(A)、その差「A-B」を(C)として、図で示しますと・・・ 。

※この図では、座標の原点から下がY座標のプラス方向で、上がマイナス方向です。

図からわかるように、正円上のY座標(A)と楕円上のY座標(B)の差(C=A-B)は、真下のときを最大値として、時計方向に回りながらその差を減少させ、左端にきたとき「0」になります。(C1→C2→C3)
さらに回転を続けると、Y座標の値はそれぞれマイナスになるので、その差(C)もマイナスに転じます。
そして真上にきたときの差(C)が最も大きいマイナス値になり
、ここが最小値。
以降マイナスの値を減少させながら、右端にきたとき「0」になります。(C4→C5→C6)

つまり、球体の回転によって(C)の値は、
  最大値(+)最小値(−)
を限りなく繰り返すことになります。

懸案だった水平軌道上を往復運動する場合(扁平率が「0」)を考えてみると、正円上のA点と水平線上のB点の差(C)は、増減パターンは上記と同じでも、値の増減幅が最も大きくなります。

扁平率が最大の「1」のとき、楕円は仮想の正円軌道と同じ軌道となり、常にA=Bの状態で回転しますので、差(C)は「0」で一定します。

この差(C)の値を軌道上の「球体の大きさ」に利用すれば、上記621の条件を満たせそうです。

625

回転角度θのとき、上記607の式から・・
仮想の正円軌道上のY座標(A)は、「A = sinθ×半径」、球体の楕円軌道上のY座標(B)は、「B = sinθ×半径×扁平率」でしたから、差(C=A-B)は、「C = (sinθ×半径)−(sinθ×半径×扁平率)」となります。式を整理すると・・・

   C = sinθ×半径×(1−扁平率)

ここで、扁平率は「−1〜0〜+1」の値ですから、マイナス値を避けるため絶対値にして、

   C = sinθ×半径×{1−絶対値(扁平率)} としておきます。

Cの値は、回転角度によって「 最大値(+)最小値(−)」と変化しますから、「球体の大きさ」に適用するときは、元の大きさ「100」に、その増減分として加算します。(上記622参照)

   球体の大きさ = 100+ sinθ×半径×{1−絶対値(扁平率)}×調整値

これを上記622のスクリプトと入れ替えて・・・

   onClipEvent (load) {
     kakudo = 0;
   }
   onClipEvent (enterFrame) {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos(theRadian) * _parent.R;
     this._y = Math.sin(theRadian) * _parent.R * _parent.henpei;
     this._xscale = 100 + Math.sin(theRadian) * _parent.R * ( 1 - Math.abs( _parent.henpei ) ) * 0.3
     this._yscale = this._xscale
     this._alpha = this._xscale * 0.8; ←遠くに行ったとき背景に溶け込むようアルファ値を設定
     kakudo += _parent.speed;
   }

※式の末尾の太赤字は調整値、大きくすれば大小比率が大きくなり、小さくすれば小さくなります。
※「this._xscale」の値は100を中心に前後プラス・マイナスの値ですから、これをアルファ値に適用すれば、球体が小さくなる程度に応じて透明化します。(*0.8は調整値)

※ここまでのサンプルは、ここをクリック。 (action_ball5.fla
ムービーの上でマウスを上下左右に移動し、動作を確認してみてください。

 作成手順6(球体スクリプトの効率化)

626

そして、球体の動作としては同じですが、スクリプトを”効率化するため”に「function」を使って書き換えると・・・

   onClipEvent (load) {  (球体である)自分が読み込まれた最初に
     kakudo = 0;
     function kaiten() {  「kaiten」という名前のメソッドを以下に定義
       theRadian = kakudo * Math.PI / 180;
       this._x = Math.cos(theRadian) * _parent.R;
       this._y = Math.sin(theRadian) * _parent.R * _parent.henpei;
       this._xscale = 100 - Math.sin(theRadian) * _parent.R * ( 1 - Math.abs(_parent.henpei) ) * 0.3;
       this._yscale = this._xscale;
       this._alpha = this._xscale * 0.8;
       kakudo += _parent.speed;
     }
   }
   onClipEvent (enterFrame) {  フレームレート(1/30秒)ごとに以下を繰り返す
     kaiten();    ←「kaiten」メソッドを実行
   }

上記625の「enterFrame」イベント内のスクリプトを、そっくりそのまま「load」イベントの中に「kaiten」という名前のメソッドとしてfanction定義(その処理内容を記載)します。

627

「メソッド」というのは、第3回”327”でもご説明していますが、特定の機能や処理をあらかじめ定め、ひとつの単語(メソッド名)として定義し(単語名に処理を封じ込め)、その単語をスクリプトとして呼び出せば定めたとおり自動的に実行するようにしたものです。
「play()」や「gotoAndStop(引数)」「Math.sin(引数)」などなどは、マクロメディア社がFlashソフトの中にあらかじめ用意したメソッドです。
「function定義」というのは、このメソッドをユーザーが独自で定義(作成)するものです。

書式は・・・

   function 任意のメソッド名 引数名
      処理内容・・・・・・・・
      ・・・変数名・・・・・・・・・    ※( )内の「引数(ひきすう)名」と処理内容の中の「変数名」が対応します。
      ・・・・・・・・・・・・・・・・
   

※引数は、複数設定することも、また省略することもできます。ここでは省略しています。
※functionで「メソッド」を定義するのは1回でよいので、通常、クリップアクションの「load」イベント内かフレームアクションの1フレームに記載します。

一度定義すると、それが記載されたムービークリップが存在する限りその仕組みはマシンの中に保持されます。
そして、定義した処理内容を実行するときは、通常のメソッドの実行と同様、実行させたい位置に引数とセットで「メソッド名」を記載します。

   任意のメソッド名 引数名

これが実行されると、自動的にfunction定義の{ }内の処理が実行されます。

628

しかし、このようにfunction定義として書き換えることでいったい”何が効率化されるのか?”と、きっと今疑問をいだいておられると思います。
スクリプトを”記載する作業”としては、前のスクリプトよりもむしろ手間ではではないか?

変数を上手く活用し組み立てを工夫することで、アクションスクリプトの記述をスッキリさせたり、スクリプトの文字数を少なくして記載する手間を少なくすることも確かに”効率化”ですが、ここでいうのは”マシン(パソコン)にとっての効率化”のことです。

図形をディスプレイに表示したり、それを動かしたりできるのはすべてマシンが高速で演算処理をしているからです。そのためマシンの処理能力を超える作業を命じると、時間内(フレームレート内)に処理が追いつかず、動きがギクシャクすることがあります。
今回はフレームレートを通常の12分の1から30分の1にあげているので、これまで12分の1秒内に処理を完了すればよかったものを30分の1秒以内に完了しなければなりません。これだけでもマシンへの負担が大きくなっています。
そして、このサンプル作成の今の段階は、球体はひとつですが最終的にはあと4つ追加します。
すると、このスクリプトの処理は5つの球体でそれぞれ演算処理することになります。
多分、この程度なら最新のパソコンなら能力範囲とは思いますが、Webにアップするとそうとは限らない場合がありますので、効率化できるものは少しでも行っておこうということです。

ではなぜこの場合”fanction定義すればマシンにとって効率的になるのか?”ということですが・・・
スクリプトは、人間が理解できるように作られた単語の羅列なので、これで直ちにマシンが自分の行うべき処理を理解しているわけではありません。
マシンが処理を実行するためには、スクリプトの文章をマシンが実行できる言葉にまず自分で翻訳して、その翻訳に基づいて実行します。
この翻訳がマシンにとって結構大変な作業なのです。
回転計算を行うスクリプトは、書き換え前は「enterFrame」イベントの中に記載しましたので、フレームレート(1/30秒)ごとに全文を翻訳して実行します。
書き換え後は、function定義として「load」イベントの中に記載しました。
「load」イベントというのは、ムービークリップが読み込まれた最初の1回しか行われないので、回転計算スクリプトの翻訳は1回きりです。
翻訳されて「メソッド」として定義されると、それはもう一般の「play()」などと同様、マシンの中にマシンの言葉で組み込まれてしまいます。
あとはフレームレートごとに「メソッドの実行命令」だけを翻訳して実行することになります。
つまり、最終的な処理内容は同じですが、マシンに対してスクリプトの翻訳作業を大幅に軽減してあげることなのです。

※今回の場合は以上の理由でfunction定義を行っていますが、スクリプトを組み立てる際に人間が理解しやすいために活用する場合もあります。その場合も結果的にマシンの負担の軽減になります。


 作成手順7(球体を増やす)

629

「球体」ムービークリップのクリップアクションが完成したところで、次にこの「球体」を「回転エリア」編集画面のステージ上で右クリック、右クリックメニューの「コピー」をクリック。
ステージの何もないところで再び右クリック、右クリックメニューの「ペースト」をクリックすると、「球体」がコピーされてステージに配置されます。この方法で「球体」を5つステージに並べます。



クリップアクションが記載されたムービークリップインスタンスを、このようにステージ上で「コピー&ペースト」すると、アクションスクリプトも含めてコピーされます。
この状態で5つの「球体」ムービークリップには同じクリップアクションが記載されていることになります。
それぞれの球体の初期値を30度ずつ回転させて配置するとすれば、ペーストした順(重なりの下順)に、クリップアクションの「load」イベントの中で定義してる変数kakudoの値を、「30」「60」「90」「120」と書き換えます。

   onClipEvent (load) {
     kakudo = 0; ←この値だけを、それぞれ「30」「60」「90」「120」に変更
     function kaiten(kakudo) {
      ・・・・・・・・・・・・・・・
     }
    }

※ここまでのサンプルは、ここをクリック。 (action_ball6.fla
ムービーの上でマウスを上下左右に移動し、各球体の動作を確認してみてください。


 作成手順8(加速度)

630

マウスの移動により、5つの連なったボールが回転速度や楕円軌道を変化させながら回転します。
しかし、その変化は直線的でマウスの移動に俊敏に反応します。これはこれでよいのですが、もしこの反応速度をにぶらせ一定の時間をかけて目的の速度まで上昇・下降させることができれば、動きの変化が加速度的になり球体に慣性の法則が適用されたかのような質量感も生まれます。

回転速度は、「回転エリア」ムービークリップの中のクリップアクション(上記614)で、

   onClipEvent (mouseMove) {
     speed = this._xmouse * 18 / 250;
   }

としています。
マウスが移動するごとに、マウスのX座標に連動して直接、球体の回転速度speedに値を代入しますので、即その値が各「球体」に伝えられ反応しました。
そこで、変数speedに変えて、目標速度としてspeedMという変数を間に介し・・・

     speedM = this._xmouse * 18 / 250;

として、マウスからの情報を一旦変数speedM に格納し、その値に向かって現在の回転速度speedの値を暫時近づけるというような仕掛けを作ればうまくいきそうです。
そこで次の式・・・

   onClipEvent (enterFrame) {  フレームレートごとに毎回
     speed = speed + ( speedM - speed ) / 調整値
   }

式の意味は・・・
「調整値」を仮に「2」とすると、
目標速度と現在の速度の差(距離)を「2」で割った値を現在の速度に加算する。
つまり、 差の2分の1を現在の速度に加えることです。
現在の速度speedを「5」、目標速度speedMを「15」とした場合・・・
   +(15)/10  元の速度speedが5→10に変化し、それが各「球体」に反映されます。
この式を「enterFrame」イベント内に記載していますから、フレームレートごとに何回も繰り返されます。
2回目、変数speedは「10」になっていますから・・・
   10+(1510)/12.5
3回目・・・
   12.5+(1512.5)/13.75
・・・・・・・・
このあと次々と計算を繰り返すたびに減速しながら限りなく目標速度15に接近していくのがお分かりと思います。
この式を第5回Mathランダム徹底研究の502A代入演算子「+=」を使って簡略化すると・・・

     speed += ( speedM - speed ) / 調整値

このように記載することができます。これが「目標値接近の方程式」です。
これを上記目標速度のスクリプトと組み合わせて・・・

   onClipEvent (mouseMove) {  ムービー上でマウスが動いたら
     speedM = this._xmouse * 18 / 250; ←マウスのX座標に連動して値を目標速度speedMに代入
   }
   onClipEvent (enterFrame) {  フレームレート(1/30秒)ごとに毎回
     speed += ( speedM - speed ) / 30; ←現在の速度speedを目標速度に近づける
   }

※調整値を「30」としているので、30分の1秒のフレームレートでは1秒間かけて目標速度に達します。
 この値を大きくすれば反応がより遅く、小さくすればより速くなります。
※「enterFrame」イベント内で時々刻々変化する変数speedの値は、その都度各「球体」に反映され、小刻みに反応することになります。

631

回転速度だけでなく、扁平率の変化についても上記「目標値接近の方程式」を適用することとして、元の「回転エリア」ムービークリップのクリップアクション(上記619)・・・

   onClipEvent (load) {
     speed = 10;
     henpei = 0.5;
     R = 80 + 90 * ( Math.abs( speed ) / 180 );※修正後は「enterFrame」で計算するので削除
   }
   onClipEvent (mouseMove) {
     speed = this._xmouse * 18 / 250;
     henpei = this._ymouse / 200;
     R = 80 + 90 * ( Math.abs( speed ) / 180 );※修正後は「enterFrame」で計算するので削除
   }

これを以下のように修正します。

   onClipEvent (load) {
     speed = speedM = 10;   ※新たに変数speedと同じ値の目標速度speedMを定義
     henpei = henpeiM = 0.5; ※新たに変数henpeiと同じ値の目標扁平率henpeiMを定義
   }
   onClipEvent (mouseMove) { マウスが移動したときだけ
     speedM = this._xmouse * 18 / 250;   各々目標値を設定
     henpeiM = this._ymouse / 200;
   }
   onClipEvent (enterFrame) { フレームレートごとに毎回
     speed += ( speedM - speed ) / 30;   各々目標値に向かって接近
     henpei += ( henpeiM - henpei ) / 30
     R = 80 + 90 * ( Math.abs(speed) / 18 );※目標値に向かって接近する回転速度を使って
   }                                             半径Rはここで毎回計算する

目標速度speedMと目標扁平率henpeiMの値は、「mouseMove」イベントの中でマウスの座標値によって決定されるので、マウスが最初にムービー上に乗るまで値が決定されません。
一方「enterFrame」イベントは「回転エリア」ムービークリップが読み込まれると直ちに発生しますので、そのままでは目標値がないまま「目標値接近の方程式」を計算することになります。(とんでもないところに勝手に動く)
そこで最初に「load」イベントの中で、それぞれ変数speed、変数henpeiと同じ値を初期値として定義しておくと、マウスが乗るまでそれぞれの初期値で固定されます。
※「speed = speedM = 10;」という記述は・・・
   speed = 10;
   speedM = 10;
 と記載するのと同じことです。

半径Rは、スクリプト修正前は「mouseMove」イベントの中で決定される変数speedの値に基づいて計算しましたから、マウスが最初にムービー上に乗るまでのつなぎとして、同時に「load」イベント内にも計算式を記載しました。
修正後、変数speedが「enterFrame」イベント内に移動したことに伴い、半径Rの計算式も「enterFrame」内に記載します。 「enterFrame」イベントは、「回転エリア」ムービークリップが読み込まれると直ちに発生しますから、「load」イベント、「mouseMove」イベント内ともに半径Rの計算式を記載する必要がなくなります。

※ここまでのサンプルは、ここをクリック。 (action_ball7.fla
ムービーの上でマウスを上下左右に移動し、動作を確認し「目標値接近」を適用しない場合と見比べてみてください。
動きが連続的でよりスムーズになっているはずです。


 作成手順9(ズーム処理)

632

回転速度が一定の速度を下回ったとき、全体にズームして球体に接近した感じを演出します。
※グラデーション図形を動かすだけでもマシンには負担ですが、これをさらに拡大縮小するのでこの処理はかなりの負担になります。

方法は回転速度が一定の速度を下回ったら、下回った分だけ今度は「回転エリア」ムービークリップ自身を拡大します。
元の「回転エリア」を拡大することで、その中に包含される各「球体」が全体的に拡大されます。
上記「enterFrame」内のスクリプトに続けて・・・

   onClipEvent (enterFrame) {
     speed += ( speedM - speed ) / 30;
     henpei += ( henpeiM - henpei ) / 30;
     R = 100 + 60 * ( Math.abs( speed ) / 18 );
     if ( Math.abs( speed ) < 10 ) {   変数speedの絶対値が「10」を下回ったら
       this._xscale = 100 + ( 10 - Math.abs( speed ) ) * 4; ←下回った分の4倍の値を100に加算
       this._yscale = this._xscale; ←自分(「回転エリア」)の縦横の大きさを同じにする
     } else {   そうでないなら
       this._xscale = this._yscale = 100; ←自分の縦横の大きさを原型に戻す
     }
   }

式の内容はこれまでご説明してきた内容の応用です。
これは回転速度が「10」を下回ったとき全体を拡大します。
速度speedが「0(回転停止)」のときが最大拡大倍率で「(10−0)×4=40」を100に加算しますから、「140(%)」に拡大されます。

633

そして、「回転エリア」ムービークリップのクリップアクションもfunction定義を使って・・・

   onClipEvent (load) {
     speed = speedM = 10;
     henpei = henpeiM = 0.5;
     function target() {     「target」という名前のメソッドを以下に定義
       speedM = this._xmouse * 18 / 250;
       henpeiM = this._ymouse / 200;
     }
     function move() {     「move」という名前のメソッドを以下に定義
       speed += ( speedM - speed ) / 30;
       henpei += ( henpeiM - henpei ) / 30;
       R = 80 + 90 * ( Math.abs( speed ) / 18 );
       if ( Math.abs( speed ) < 10) {
         this._xscale = 100 + ( 10 - Math.abs( speed ) ) * 4;
         this._yscale = this._xscale;
       } else {
         this._xscale = this._yscale = 100;
       }
     }
   }
   onClipEvent (mouseMove) {  マウスが動いたら
     target();   ←「target」メソッドを実行
   }
   onClipEvent (enterFrame) {  フレームレートごとに毎回
     move();    ←「move」メソッドを実行
   }

マシンのために効率化を図ります。
少しは楽になったかな(笑)

※冒頭のサンプルは、ここまでの処理を行ったもので、そのflaファイルです。→ action_ball.fla


 おわりに

634

以上、サンプルの”球体の回転”の制作についてご説明してきましたが、これらアクションスクリプトを使った回転のメカニズムは単に球体を回転させることにとどまらず、アイデア次第でいろいろな作品に応用できます。
「MX」のライン描画機能と組み合わせれば、キューブの回転などもつくれるはずです。
みなさまのご奮闘をお祈りします(*^.^*)


第6回 回転のメカニズム(番外編) 戻る このページの先頭へ 次へ

質問掲示板 過去記事 サンプル作品集 FLASH5講座 ACTION SCRIPT講座 リンク 憲ちゃんのHP FLASH制作代行 トップページ

トップページ