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

 第7回 正六面体の回転(番外編) 2004.06.23 .
 正六面体の回転を作る

701

前回の「回転のメカニズム」のスクリプトと「MX」以降の描画機能を応用して以下の「正六面体の回転」を制作します。



マウスの動きで前後左右の回転角度が変化します。
※演算負荷が大きいのでマシンによっては動きがギクシャクします。

今回も内容を理解していただくために、少々遠回りになりますがステップごとに解説しながら進めていきます。
作成の過程を一緒に体験していただく中で、アクションスクリプトの考え方を学んでいただければ、ご自分なりにアレンジすることもさらに発展させることもできるようになります。


 部品の準備

702

まず、ムービー(ドキュメント)の大きさを「幅400×高さ300」、背景色「黒」、フレームレート「30」に設定します。

サンプルで使用する部品は、「点」という名前を付けた「空のムービークリップ」とそれを包含した「回転エリア」ムービークリップ」だけでできています。

「空のムービークリップシンボル」は、通常、メニューバー「挿入」→「新規シンボル」で中に何も入れない状態で作成しますが、ここでは編集途中の作業をやりやすくするため、以下のようにひとまずダミーとして塗り図形を「シンボルに変換」し、作業が終わったあと、「シンボルの編集」でその図形を削除するという方法で「空」にすることにします。

703

シーン編集画面のステージにダミーとしての正円(10px程度)の塗り図形を描きます。
それが選択された状態で、メニューバー「挿入」→「シンボルに変換」をクリック、「シンボルに変換パネル」で、名前を「点」、タイプを「ムービークリップ」、基準点を中央にして「OK」をクリック。
これで 「点」ムービークリップシンボルが「ライブラリ」に登録され、ステージにはそのインスタンス(分身)が配置された状態になります。
この「点」が正六面体の8つの角のひとつとなるので、残り7つを「ライブラリ」からステージにドラッグして配置します。
これらは再生の際アクションスクリプトで位置が決定されますので、ステージのどこに配置しても構いませんが、一応編集の目安として、下図のような感じに並べてください。
上4つを上面の4つの角、下4つを下面の4つの角とみなし、「インスタンス名」として上面を左から順に「a1,a2,a3,a4」、下面を「b1,b2,b3,b4」と付けてください。



※図は「回転エリア」ムービークリップの編集画面です。

シーン編集画面の8つの「点」インスタンスすべてが選択された状態で、メニューバー「挿入」→「シンボルに変換」をクリック、「シンボルに変換パネル」で、名前を「回転エリア」、タイプを「ムービークリップ」として「OK」をクリックすると、これら8つの「点」を包含した「回転エリア」ムービークリップシンボルが「ライブラリ」に登録され、シーン編集画面にはそのインスタンスが配置された状態になります。
この「回転エリア」インスタンスの中心(基準)点が、中にある各「点」の座標の原点になりますので、これをムービーの中央に配置してください。

以上で部品の準備はすべて完了です。
そしてこれをダブルクリックすると、図のような「回転エリア」ムービークリップの編集画面になります。



 回転角度・回転速度・扁平率を変数にした前回スクリプトを移植!

704

上面4つの点、下面4つの点が一定の間隔を保ちながら、上面、下面それぞれで楕円軌道上を同じ速度で回転すれば立方体が横回転しているように見えるはずです。
そこで前回の「回転のメカニズム」”618”でご説明した「球体」ムービークリップのクリップアクション・・・

   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
     kakudo += _parent.speed;
   }


「球体」の楕円軌道のスクリプトを「点」ムービークリップに応用します。
しかしこのままでは、すべての「点」が「回転エリア」ムービークリップの中心(基準)点を中心に回転しますので、上面は上面の4つの点で楕円軌道を描き、下面は下面の4つの点で楕円軌道を描くように修正する必要があります。

705

そこで図のように「回転エリア」の中心(基準)点(=原点)を中心として、上下等間隔の位置にそれぞれ上面・下面の楕円の中心を移動させることにします。

円の中心の座標を指定する方法は、前回「回転のメカニズム」”607”でご説明した最初の方法・・・

  指定したい円の中心点の座標を、(xCenter,yCenter) としたとき、

     x = cosθ * 半径R + xCenter
     y = sinθ * 半径R + yCenter; 

これを使うことができます!


各楕円の中心のX座標値は原点と同じですからそのままで、中心のY座標値を変数Centerとして、上記”704”の「楕円軌道のスクリプト」に当てはめると・・・
上面の楕円は、原点より上で、座標ではマイナス方向ですから・・・

   this._x = Math.cos(theRadian) * _parent.R;
   this._y = Math.sin(theRadian) * _parent.R * _parent.henpei - Center

下面の楕円は、原点より下で、座標ではプラス方向なので・・・

   this._x = Math.cos(theRadian) * _parent.R;
   this._y = Math.sin(theRadian) * _parent.R * _parent.henpei Center

このようになります。

706

ではこの変数Centerの具体的な数値とは、どのようなものでしょうか?
「正六面体」のすべての辺の長さは同じですから、視点の角度を無視して考えれば、例えば上図で上下に「点a1」と「点b1」を結んだ線は「点a1〜点a2」「点a2〜点a3」「点a3〜点a4」「点a4〜点a1」と同じ一辺の長さです。
「点a1」と「点b1」を結んだ線は、上下楕円の中心点の距離「Y1〜Y2」と同じですから、変数Centerは一辺の長さの2分の1ということになります。
一辺の長さは、次のようにして計算します。

左図は「正六面体」を真上から見た図ですが、4つの辺の長さが等しいということは、4つの角の角度はすべて90度(直角)ということです。
したがって、半径Rの正円上にある各「点」の配置角度は、「点a1=0度」とすれば以後90度間隔に「点a2=90度」「点a3=180度」「点a4=270度」となります。
この状態で、一辺の長さを求めるなら・・・
例えば図の 「a2→原点→a3」が直角で、直角の両側の辺の長さは共に半径Rですから、一辺の長さ「a2〜a3」は、 「ピタゴラスの定理」で・・・

  (一辺の長さ)の2乗 = (半径R)の2乗+(半径R)の2乗

  ∴一辺の長さ = 平方根(2×半径R×半径R)

で求められます。

一辺の長さを変数Ippen として、これをスクリプトで表現すると・・・

   Ippen = Math.sqrt( 2 * R * R )

「Math.sqrt( 数値 )」が平行根を求めるメソッドです。
したがって、変数Center の理想値(真横からみたときの値)は・・・

   Center = Ippen / 2;

ということになります。

707

楕円の中心のY座標Centerは、各「点」にまたがる共通事項ですから、個々の「点」のクリップアクションで定義するより、ひとつ上の階層のムービークリップである「回転エリア」の変数として一括管理するほうが合理的なので・・・
上記”704”のスクリプトを修正し、各「点」ムービークリップのクリップアクションとしてとりあえず以下のようにします。

   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 - _parent.Center
     kakudo += _parent.speed;              ※上面のa点は「-」、下面のb点は「+
   }

さらにこれを、マシンの負担軽減のため「function定義(前回”626”参照)」を使って書き換えると・・・

   onClipEvent (load) {  自分が読み込まれた最初に以下を定義
     kakudo = 0;  ←※「点」インスタンスごとに上記”703”の図を参考にそれぞれ角度を修正
     function kaiten() {
       theRadian = kakudo * Math.PI / 180;
       this._x = Math.cos(theRadian) * _parent.R;
       this._y = Math.sin(theRadian) * _parent.R * _parent.henpei - _parent.Center;
       kakudo += _parent.speed;
     }
   }
   onClipEvent (enterFrame) { フレームレートごとに以下を繰り返す
     kaiten()
   }

これを「回転エリア」ムービークリップ編集画面で、まず最初「a1」をクリック選択して、「アクションパネル」で記載します。
記載が終われば、同じパネルの「スクリプトエリア」でこのスクリプトすべてを選択(反転)し右クリック、右クリックメニューの「コピー」をクリック。
次に「a2」を編集画面でクリック選択し、白紙の「アクションパネル」の「スクリプトエリア」上で右クリック、右クリックメニューの「ペースト」をクリックすると、同じスクリプトが「a2」にも複写されます。
そして、「load」イベントの中の変数kakudoの初期値を「90」に変更します。
以降、同様に「a3」「a4」と複写し、変数kakudoの初期値だけを上記”703”の図の初期角度を参考に修正してください。
「b1」〜「b2」は、変数kakudoのほか、変数Centerの前の符号もそれぞれ「+」に変更します。

708

そして、各「点」ムービークリップのスクリプトで変数名の前に相対パス「_parent」をつけた半径R、扁平率henpei、楕円の中心のY座標Center及び回転速度speedについては、「回転エリア」ムービークリップでそれぞれ定義することになりますが、このクリップアクションも前回”631”の修正後のスクリプトをとりあえず移植して必要な修正を行うことにします。
そのスクリプトは・・・

   onClipEvent (load) { 「回転エリア」が読み込まれた最初に
     speed = speedM = 10;
     henpei = henpeiM = 0.5;
   }
   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計算
   }

このようなものでした。(詳しくは前回の該当箇所を参照)

ここで半径Rは、前回は「遠心力」を表現したいため変数speedの値によって変化させましたが、今回は変化させません。
そして半径Rは他の関係式で使われる(上記”705”)ので、「load」イベントの中で最初に定義します。
サンプルでは各楕円の半径を「50」、回転速度とその目標値の初期値をもっと遅く「5」とし、扁平率はとりあえず固定の「0.3」ということにして、まずは基本動作を確認してみます。
そこで、以下のようにスクリプトを修正して・・・

   onClipEvent (load) { 最初に以下を定義
     R = 50;
     speed = speedM = 5;
     henpei = 0.3;
     Ippen = Math.sqrt( 2 * R * R )
     Center = Ippen / 2; ※1
   }
   onClipEvent (mouseMove) { マウスが移動したときだけ
     speedM = this._xmouse * 50 / 200; ※2
   }
   onClipEvent (enterFrame) {
     speed += ( speedM - speed ) / 10; ※3
   } 

※1 変数Centerの値は、とりあえず一辺の長さの2分の1と仮定して「Ippen / 2」としています。(詳細は後述)
※2 「mouseMove」イベント内の「this._xmouse」は、ムービーの幅が400ですから「−200〜+200」の値を取得します。ここでは最大速度を「-50〜50」と設定したいので、調整値として「50÷200」を掛けています。
※3 「enterFrame」イベント内では、回転速度speedを「目標値接近の方程式(前回”630”参照)」を使って徐々に目標値speedMに向かって変化させています。フレームレートが1/30秒で調整値が「10」ですから3分の1秒で目標値に到達する設定です。

※ここまでのサンプルは、ここをクリック。 (cube1.fla
ムービーの上でマウスを左右に移動し、動作を確認してください。
何となく立体に見えたでしょうか(*^.^*)


 縦回転と扁平率

709

以上はマウスの横移動による正六面体?の横回転の仕組みですが、次にこれをマウスの縦移動に連動して縦回転させます。
正六面体が縦回転する様子を想像していただくと、上下の楕円の扁平率の変化と連動することはだいたい理解できると思います。
回転している正六面体の各「点」を真正面から見ると上面・下面とも水平軌道に見えるので楕円の扁平率は「0」、真上から見ると扁平率「1」の正円軌道になります。

しかし前回の球体の単独回転と異なり、単に扁平率を変化させるだけでなく、縦の辺の長さもそれに応じて変化させないと立体が縦回転したように見えません。
真正面から見た「扁平率 = 0」のとき、正方形が横回転するので縦の辺の長さは、理想値の一辺の長さ(= Ippen)になるはずで、真上から見た「扁平率 = 1」のとき、上面と下面が完全に重なるので縦の辺の長さは「0」になります。
縦の辺の長さとは、上記”705”の図で上下楕円の中心点の距離のことでしたので、
これを単純に、扁平率と関連付けて・・・

   上下楕円の中心点の距離 = 理想値の1辺の長さ×{1−絶対値(扁平率)}×調整値

そして楕円の中心点のY座標値Center は、上下楕円の中心点の距離の2分の1でしたから、

   Center = Ippen * ( 1 - Math.abs( henpei ) ) * 調整値 / 2;

この関係式と変数henpei の値をマウスのY座標と連動させ、上記”708”の「回転エリア」のクリップアクションに、

   onClipEvent (load) {
     R = 50;
     speed = speedM = 5;
     henpei = henpeiM = 0.3;
     Ippen = Math.sqrt( 2 * R * R );
     Center = Ippen * ( 1 - Math.abs( henpei ) ) * 1.2 / 2; ※初期値としてCenterを定義
   }
   onClipEvent (mouseMove) { マウスが移動したときだけ
     speedM = this._xmouse * 50 / 200;
     henpeiM = this._ymouse /150
   }
   onClipEvent (enterFrame) {
     speed += ( speedM - speed ) / 10;
     henpei += ( henpeiM - henpei ) / 10;
     Center = Ippen * ( 1 - Math.abs( henpei ) ) * 1.2 / 2; ※フレームレートごとに再計算
   } 

このように追加記載したサンプルが、ここです。
クリックして、動作を確認してみてください。(flaファイルは添付していません)

710

確かに縦の辺は真正面から見たとき最大、真上から見たとき重なって最小になりますが、途中の長さが不自然で立方体が上下に伸縮しているようにしか見えません。
ということはこれではダメということで、実際に正六面体が縦回転しているとき、ディスプレイ平面上では縦の辺がどんな長さに見えるか、もう少し詳しく分析する必要がありそうです!
正六面体の横回転を停止し、ディスプレイ正面からみて正方形に見えるとき、パソコンの中に入ってそれを左真横から眺めてみると、



真横から見ても図の白線のように水平に置かれた正方形であり、上下回転面の中心点を結んだ垂直線(図の縦赤線A線)が横回転の回転軸ということになります。
正六面体がディスプレイ正面から見て手前に縦回転するとは、この回転軸が中央O点(=原点)を中心として時計回りに回転、上下回転面の中心点はそれぞれ図赤丸の正円軌道上を時計回りに回転することです。

711

回転軸がA線のように垂直のとき、ディスプレイに投影された上面回転の中心点は、中央の原点から「一辺の長さ」の2分の1だけ上(マイナス)の位置に見え、下面回転の中心点はその180度反対の位置にあるので、中央の原点から同じ距離だけ下(プラス)の位置に見えます。
回転軸が時計回りに60度回転したB線のとき、各中心点のディスプレイ上の見かけの位置は、それぞれ原点に向かって一定の距離同じだけ接近し、90度回転したC線では、原点からの距離がそれぞれ「0」となって原点と同じに位置に見えます。
ディスプレイ平面に投影されたこの回転面の中心点と原点との距離こそが、各「点」ムービークリップが回転する「楕円の中心点のY座標」のことで、変数Center として「回転エリア」ムービークリップで値を決定したかったものです。

図から明らかなように、変数Center の値は、回転軸の回転角度に応じて半径「Ry」の正円上を移動する「楕円の中心点のY座標」のことで、半径「Ry」は一辺の長さの2分の1ですから、回転軸の回転角度を「kakudoY」としたとき、前回”607”の式から・・・

   ラジアン角 = kakudoY×π/180
   Center = sin(ラジアン角)×一辺の長さ/2

となり、これをスクリプトでまとめて記載すると・・・

   Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;

このようになります。

712

一方扁平率は・・・
上の回転面の中心点がA点のとき、A点を中心として回転する各「点」(図のA1・A2)のY座標値は同じで、ディスプレイ平面に投影すると水平軌道(扁平率 = 0)を往復運動します。
回転軸が時計回りに60度回転してB点にくると、各「点」はB1・B2の位置に移動して、ディスプレイ平面ではその分楕円の振幅幅が大きくなります。
90度回転したC点では、楕円の振幅幅が「一辺の長さ」と同じになり、ディスプレイ平面で各「点」は正円軌道(扁平率 = 1)を描くようになります。
したがって、扁平率とは、回転軸の回転角度「kakudoY」に応じて半径「R」の正円上を移動する図の2つの「点」のY座標値の差を「一辺の長さ」で割った比率ということがいえます。

2つの「点」のY座標値を求めるためには、各「点」自身の回転角度がわかる必要がありますが、各「点」の回転角度は、これも図から明らかなように回転軸の回転角度「kakudoY」の前後45度の関係にあります。

   点1(図のA1、B1、C1)の角度は、kaitenY+45度。
   点2(図のA2、B2、C2)の角度は、kaitenY−45度。

それぞれラジアン角に変換して・・

   点1のラジアン角 = ( kakudoY+45 )×π/180
   点2のラジアン角 = ( kakudoY−45 )×π/180

そしてこの場合の各「点」の回転半径「R」は、元の「半径R」と同じなのでそれぞれのY座標値は、

   点1のY座標値 = sin(点1のラジアン角)×半径R
   点2のY座標値 = sin(点2のラジアン角)×半径R

各点のY座標値の差を求めると・・

   各「点」のY座標値の差 = 点1のY座標値 − 点2のY座標値
             = { sin(点1のラジアン角)×半径R }−{ sin(点2のラジアン角)×半径R }
             = { sin(点1のラジアン角)−sin(点2のラジアン角) }×半径R
したがって・・・

   扁平率 = {sin(点1のラジアン角)−sin(点2のラジアン角)}×半径R/一辺の長さ

各「点」のラジアン角をそれぞれ変数Radian1、変数Radian2 としてスクリプトで記載すると・・・

   Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
   Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
   henpei = ( Math.sin( Radian1 )-Math.sin( Radian2 ) )*R / Ippen

となります。

713

そして回転軸の回転角度「kakudoY」の値をムービー上のマウスのY座標値に連動させれば、正六面体を縦回転させることができます。

   kakudoY = this._ymouse * 調整値A/調整値B;

ムービーの中でマウスのY座標が取得する値の幅は、ムービーの高さ300を中央から上下に分割して「−150〜+150」 ですから、分母の調整値Bは「150」とします。
それに連動して、kakudoYに代入したい値の幅を分子の調整値Aに設定します。
kakudoYは360度で1回転しますから、「360」とすると「−360〜+360」の値となって、マウスをムービーの上の端から下の端に移動すると2回転することになります。
ここでは1.5回転させたいので、360度×1.5回転÷2=270度として・・・

   kakudoY = this._ymouse * 270 / 150;

マウスに連動して滑らかに回転させるには、kakudoYの代わりに一旦目標値kakudoYMに置き換え・・・
「mouseMove」イベントの中で・・・

   kakudoYM = this._ymouse * 270 / 150;

「enterFrame」イベントの中で「目標値接近の方程式」を使って、

   kakudoY += ( kakudoYM - kakudoY ) / 10;

とします。
調整値を横回転と同じ「10」として、3分の1秒かけて目標値に到達するようにしました。

714

以上のことを「回転エリア」ムービークリップのクリップアクションとして整理すると以下のようになります。

   onClipEvent (load) {
     R = 50;
     speed = speedM = 5;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
   }
   onClipEvent (mouseMove) {
     speedM = this._xmouse * 50 / 200;
     kakudoYM = this._ymouse * 270 / 150;
   }
   onClipEvent (enterFrame) {
     speed += ( speedM - speed ) / 10;
     kakudoY += ( kakudoYM - kakudoY ) / 10;
     Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
     Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
     Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
     henpei = ( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
   }

さらにこれを「function定義」を使って整理すると・・・

   onClipEvent (load) {
     R = 50;
     speed = speedM = 5;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
   function target() {
     speedM = this._xmouse * 50 / 200;
     kakudoYM = this._ymouse * 270 / 150;
   }
   function move() {
     speed += ( speedM - speed ) / 10;
     kakudoY += ( kakudoYM - kakudoY ) / 10;
     Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
     Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
     Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
     henpei = ( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
   }
   onClipEvent (mouseMove) {
     target()
   }
   onClipEvent (enterFrame) {
     move()
   }   
         

「load」イベントの中で、回転軸の回転角度「kakudoY」とその目標値「kakudoYM」の初期値を「60」としている意味は、Flashの場合、回転角度は原点(上図「0」点)からX軸に沿って右(+)方向を0度として、時計回りに回転しながら角度が増していきます。
上図のAの状態は回転角度が90度(又は270度)で、正六面体の上下面が見えず側面だけが水平に回転、Cの状態が回転角度0度(又は180度)で、側面が見えず上面又は下面の一方が正面に見えて正円で回転している状態です。
60度(又は240度)はその中間よりやや下方向ですから、斜めに傾いて扁平した上面又は下面と側面が見えた状態で回転するということです。

※ここまでのサンプルは、ここをクリック。 (cube3.fla
ムービーの中央付近でマウスを上下移動して、正六面体の縦の回転角度の変化を確認してみてください。
少しは正六面体が回転しているイメージが表現できたと思います(*^.^*)


 各「点」の間にスクリプトで線を描画

715

ここから先は「MX」から新たに追加されたアクションスクリプトの機能を使って、各「点」を結ぶ線を描画します。
※残念ですが「FLASH5」では実現できません!

線をA点から始めて、B点、C点、D点と連続して描画していくときの記載順序と書式は・・・

@まず、これから引く線のスタイルを決めることから始めます。

   MCインスタンス名.lineStyle(線の太さ,色);

線の太さの単位はピクセル、色は16進数カラーコードの場合「0x(数字のゼロと小文字のエックス)」に続けて6桁カラーコードを記載します。例、「0x0000FF」

A次に、始点であるA点の座標を指定、

   MCインスタンス名.moveTo(xA,yA);

Bそこから引く次の点Bを指定、

   MCインスタンス名.lineTo(xB,yB);

C以後、この「lineTo」メソッドだけで順にC点、D点を指定していきます。

   MCインスタンス名.lineTo(xC,yC);
   MCインスタンス名.lineTo(xD,yD);

一度、筆をあげて別の地点から描き始めるときは、
もう一度「moveTo()」から同様に行います。

※各点の座標の原点は、描画メソッドの前に指定したMCインスタンスの中心(基準)点になります。

716

各「点」の座標は「回転エリア」ムービークリップの中心(基準)点なので、描画スクリプトの記載場所も「回転エリア」ムービークリップのクリップアクションに追加するかたちで記載し、指定するMCインスタンス名は自分自身である「this」とします。
線の太さは「3」ピクセル、色は「#EEEEEE」とします。
線を結んでいく順序について特に注意することはありませんが、線の引き忘れのないようにしなければなりません。
ここでは、とりあえず、上面の「a1〜a4」の各点と下面の「b1〜b4」の各点を結んたあと、それぞれ上下対応する線を結んでいくものとします。

   this.lineStyle( 3,0xEEEEEE ); ←線の太さと色を指定して
   this.MoveTo( a1._x,a1._y );   ←点「a1」を始点として
   this.lineTo( a2._x,a2._y );    ←点「a2」に線を引き
   this.lineTo( a3._x,a3._y );    ←そこから続いて点「a3」に線を引き
   this.lineTo( a4._x,a4._y );    ←そこから続いて点「a4」に線を引き
   this.lineTo( a1._x,a1._y );    ←そこから続いて点「a1」に線を引く

最後に元の「a1」に戻しているので、これで上面の矩形部分が完成。
続けて・・・下面の矩形部分として

   this.MoveTo( b1._x,b1._y );   ←点「b1」を始点として
   this.lineTo( b2._x,b2._y );    ←点「b2」に線を引き
   this.lineTo( b3._x,b3._y );    ←そこから続いて点「b3」に線を引き
   this.lineTo( b4._x,b4._y );    ←そこから続いて点「b4」に線を引き
   this.lineTo( b1._x,b1._y );    ←そこから続いて点「b1」に線を引く

さらに上から下にそれぞれ

   this.MoveTo( a1._x,a1._y );   ←点「a1」を始点として
   this.lineTo( b1._x,b1._y );    ←点「b1」に線を引く       
   this.MoveTo( a2._x,a2._y );   ←点「a2」を始点として
   this.lineTo( b2._x,b2._y );    ←点「b2」に線を引く
   this.MoveTo( a3._x,a3._y );   ←点「a3」を始点として
   this.lineTo( b3._x,b3._y );    ←点「b3」に線を引く
   this.MoveTo( a4._x,a4._y );   ←点「a4」を始点として
   this.lineTo( b4._x,b4._y );    ←点「b4」に線を引く

これが実行されると正六面体の線図形が表示されますが、一度スクリプトで描かれた線や塗りはそのままでは画面から消えないので、「enterFrame」イベントごとに描画していけば常に上書きされ、移動の軌跡として塗りつぶされてしまいます。
そこで、このスクリプトの先頭に、画面からすべての描画を消し去るメソッド「MCインスタンス名.clear()」を記載して、消しては描き、消しては描きを繰り返すようにします。

   this.clear(); スクリプトで描画したすべてのものを消去
   this.lineStyle( 3,0xEEEEEE );  以下上記のとおり
   this.MoveTo( a1._x,a1._y );
   this.lineTo( a2._x,a2._y );
   ・・・・・・
   ・・・・・・
※ただしこの「clear()」メソッドは、スクリプトで描画したものだけに適用され、編集時にステージに描いた線や図形等(サンプルではロゴや光など)には影響しません。

717

以下、機能とは関係ありませんが、各メソッドの前に指定するMCインスタンス名はすべて「this」なので、いちいちこれを記載するのが面倒と思われる場合は、次のように、「with」アクションを用いて束ねることもできます。

   with( this ){
     clear();
     lineStyle( 3,0xEEEEEE ); 以下メソッドの前のインスタンス名を省略
     MoveTo( a1._x,a1._y );
     lineTo( a2._x,a2._y );
     lineTo( a3._x,a3._y );
     ・・・・・・・
   }

そして、いずれかの方法で記述した描画スクリプトを、任意のメソッド名を「line」として「function定義」して、

   function line() {
     with( this ){
       clear();
       lineStyle( 3,0xEEEEEE );
       MoveTo( a1._x,a1._y );
       lineTo( a2._x,a2._y );
       lineTo( a3._x,a3._y );
       ・・・・・・・
     }

「enterFrame」イベントの中で・・・

   onClipEvent(enterFrame){
     move();
     line()
   }

というように「move()」メソッドに続けて記載します。
あるいは、「move()」メソッドの「function定義」の最後の行にこの「line()」メソッドを記載して・・・

   function move() {
     speed += ( speedM - speed ) / 10;
     kakudoY += ( kakudoYM - kakudoY ) / 10;
     Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
     Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
     Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
     henpei = ( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
     line()
   }

「enterFrame」メソッド内はこれまでどおり「move()」メソッドのみとすることもできます。

718

サンプルは後者の方法を用いて、以下のクリップアクションを「回転エリア」ムービークリップに記載しています。

   onClipEvent (load) {
     R = 50;
     speed = speedM = 3;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
     function target() {
       speedM = this._xmouse * 50 / 200;
       kakudoYM = this._ymouse * 270 / 150;
     }
     function move() {
       speed += ( speedM - speed ) / 10;
       kakudoY += ( kakudoYM - kakudoY ) / 10;
       Center = Math.sin( kakudoY * Math.PI / 180) * Ippen / 2;
       Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
       Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
       henpei =( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
       line()
;     }
     function line() {
       with ( this ) {
         clear()
         lineStyle( 3, 0xEEEEEE )
         moveTo( a1._x, a1._y )
         lineTo( a2._x, a2._y );
         lineTo( a3._x, a3._y );
         lineTo( a4._x, a4._y );
         lineTo( a1._x, a1._y );
         moveTo( b1._x, b1._y )
         lineTo( b2._x, b2._y );
         lineTo( b3._x, b3._y );
         lineTo( b4._x, b4._y );
         lineTo( b1._x, b1._y );
         moveTo( a1._x, a1._y )
         lineTo( b1._x, b1._y );
         moveTo( a2._x, a2._y )
         lineTo( b2._x, b2._y );
         moveTo( a3._x, a3._y )
         lineTo( b3._x, b3._y );
         moveTo( a4._x, a4._y )
         lineTo( b4._x, b4._y );
       }
     }
   onClipEvent (mouseMove) {
     target();
   }
   onClipEvent (enterFrame) {
     move()
   }

※ここまでのサンプルは、ここをクリック。 (cube4.fla
ここまでくればもう立派な正六面体の回転ですね(^^g/~~


 横回転の動きに変化をつける

719

正六面体のかたちが明確になると、その動き方もよく見えてきて多少変化もつけたくなります。
横回転は、マウスのX座標値に応じてその回転速度と回転方向を変化させていますが、回転速度を一定にして、縦回転で行ったと同じように、横の回転角度を変更するようにしてみます。

横の回転速度を一定にするということは、各「点」ムービークリップのクリップアクションの中の「kaiten()」定義で・・・

   function kaiten() {
     theRadian = kakudo * Math.PI / 180;
     this._x = Math.cos( theRadian ) * _parent.R;
     this._y = Math.sin( theRadian ) * _parent.R * _parent.henpei - _parent.Center;
     kakudo += _parent.speed
   }

ここの変数speedの値を一定にすることです。現在マウスのX座標値と連動して変化するようになっていますから、マウスとの関係を断ち切ることで一定になります。
その速度は、「回転エリア」ムービークリップの中で初期値として定義している・・

     speed = speedM = 5;

となり、マウスとの関係を断ち切りますから、目標値である変数speedM は必要なくなるので、これを・・・

     speed = 5;

としておきます。
その結果、ここの変数kakudoは、何もしなければ各「点」は回転角度「5」の速度で等速回転運動をすることになります。
そして代わりにマウスのX座標と連動させる回転角度を「_parent.kakudoX」として、このkakudoの値に加えた値を、「点」の実際の回転角度としてやれば、マウスが動いたとき、等速回転運動が乱されその瞬間、横回転の回転角度が変化します。
「点」の実際の回転角度を変数「theKakudo」として、「kaiten()」定義の中で・・・

   function kaiten() {
     theKakudo = kakudo + _parent.kakudoX; ←この式を追加
     theRadian = theKakudo * Math.PI / 180;  ←実際の回転角度をkakudoからtheKakudoに変更
     this._x = Math.cos( theRadian ) * _parent.R;
     this._y = Math.sin( theRadian ) * _parent.R * _parent.henpei - _parent.Center;
     kakudo += _parent.speed;
   } 

このように書き加えます。
この追加修正をすべての「点」ムービークリップに対して行います。

720

次に「回転エリア」ムービークリップのクリップアクションで、この変数kakudoX を定義し、マウスのX座標と連動させます。
kakudoXについても目標値kakudoXMを設け、目標値とマウスのX座標との関連付けを行います。

   kakudoXM = this._xmouse * 調整値A/調整値B;

ムービーの中でマウスのX座標が取得する値の幅は、ムービーの幅400を中央から左右に分割して「−200〜+200」 ですから、分母の調整値Bは「200」。
それに連動して、目標値の幅を分子の調整値Aに設定します。
マウスで変更する横方向の回転角度も縦同様1.5回転として、360度×1.5回転÷2=270度ですから
「mouseMove」イベントの中で・・・

   kakudoXM = this._xmouse * 270 / 200;

「enterFrame」イベントの中で「目標値接近の方程式」を使って、

   kakudoX += ( kakudoXM - kakudoX ) / 10;

とします。

721

結果「回転エリア」ムービークリップのクリップアクションは以下のとおりになります。

   onClipEvent (load) {
     R = 50;
     speed = 5
     kakudoX = kakudoXM = 0;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
     function target() {
       kakudoXM = this._xmouse * 270 / 200;
       kakudoYM = this._ymouse * 270 / 150;
     }
     function move() {
       kakudoX += ( kakudoXM - kakudoX ) / 10;
       kakudoY += ( kakudoYM - kakudoY ) / 10;
       Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
       Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
       Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
       henpei =( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
       line();
     }
     function line() {
       with ( this ) {
         clear();
         lineStyle( 3, 0xEEEEEE );
         moveTo( a1._x, a1._y );
         lineTo( a2._x, a2._y );
         lineTo( a3._x, a3._y );
         ・・・・一連の描画スクリプト・・・・
       }
     }
   }
   onClipEvent (mouseMove) {
     target();
   }
   onClipEvent (enterFrame) {
     move();
   }

722

一応サンプルにおけるアクションスクリプトは、最後の「面の塗り」を残してほぼ出来上がりです。
線を描くまでの動作の目安として、またクリップアクションを記載する際のムービークリップ選択の”的”として、「点」ムービークリップにダミーとして入れていた「正円塗り図形」をこの辺で取り外すことにします。
「ライブラリ」にある「点」ムービークリップのシンボル名の部分をダブルクリックして、シンボル編集画面にし、ダミー図形を削除してください。
これで「点」ムービークリップは「空のムービークリップ」となり、それを包含する「回転エリア」ムービークリップからも一切の図形が消えます。
ムービークリップの階層構造とアクションスクリプトだけが存在する状態です。

※ここまでのサンプルは、ここをクリック。 (cube5.fla
角の●が取れてスッキリした正六面体になりました。動きも冒頭のサンプルと同じです。


 面を塗る

723

最後の仕上げに正六面体の面を塗りますが、スクリプトでの面の塗りつぶしは、上記”715”で行った線の描画とセットで、線で囲まれた範囲を塗りつぶすことになります。
A点、B点、C点、D点で囲まれた範囲を塗りつぶす場合の記載順序と書式は・・・

@まず、塗りの色を指定することから始めます。

   MCインスタンス名.beginFill(塗りの色);

以下、上記”715”の線の描画と同じ要領で塗りに必要な線を描画します。
Aこれから引く線のスタイルを決めます。

   MCインスタンス名.lineStyle(線の太さ,色); ※線の太さと色を省略すると、線なし塗りのみの図形になります。    
B次に、始点であるA点の座標を指定、

   MCインスタンス名.moveTo(xA,yA);

Cそこから引く次の点Bを指定、

   MCインスタンス名.lineTo(xB,yB);

D以後、この「lineTo」メソッドだけで順にC点、D点を指定していきます。

   MCインスタンス名.lineTo(xC,yC);
   MCインスタンス名.lineTo(xD,yD);
   MCインスタンス名.lineTo(xA,yA);  ※始点のA点を指定して閉じます。

E最後に塗りの修了を告げます。

   MCインスタンス名.endFill();


※各点の座標の原点は、描画メソッドの前に指定したMCインスタンスの中心(基準)点になります。
※@とAは順序が入れ替わっても機能します。

724

試しに、正六面体の上面と下面を上記の方法を使って塗りつぶしてみます。
※「線なし」の設定のときは「lineStyle()」の「()」内に何も記載しません。
「回転エリア」ムービークリップの「line()」メソッドの「function定義」の中で・・・

   function line() {
     with (this) {
       clear();           ←描画を消去
       lineStyle();         ←「線なし」設定
       beginFill( 0xFFFF00 );  ←「黄」で塗りつぶす
       moveTo( a1._x, a1._y );   以下上面の線描画
       lineTo( a2._x, a2._y );
       lineTo( a3._x, a3._y );
       lineTo( a4._x, a4._y );
       lineTo( a1._x, a1._y );
       endFill();           ←塗り終了
       beginFill( 0x0000FF );  ←「青」で塗りつぶす
       moveTo( b1._x, b1._y );   以下下面の線描画
       lineTo( b2._x, b2._y );
       lineTo( b3._x, b3._y );
       lineTo( b4._x, b4._y );
       lineTo( b1._x, b1._y );
       endFill();           ←塗り終了
     }
   }

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

725

塗り機能の実験としては成功ですが、正六面体の上面・下面ということでは”上手く”ありません。
マウスを上下に移動して面の重なり具合をよく観察すると、重なったときは常に「下面(青)」が前面にでてしまいます。
それは、スクリプトは記載された順に上から下に実行されるからです。
上記スクリプトでは「下面」があとで実行されますので、常に「上面」の上に上書きされます。

上面と下面が完全に重なった状態のときとは、扁平率が「1」又は「−1」のときです。
扁平率が「プラス」のとき上面を手前に、「マイナス」のとき下面を手前に表示すると考えれば・・・

   function line() {
     with (this) {
       clear();
       lineStyle();
       if( henpei >= 0 ) {     扁平率がプラスなら
         beginFill( 0x0000FF );  先に下面を「青」で塗りつぶして
         moveTo( b1._x, b1._y );
         lineTo( b2._x, b2._y );
         lineTo( b3._x, b3._y );
         lineTo( b4._x, b4._y );
         lineTo( b1._x, b1._y );
         endFill();
         beginFill( 0xFFFF00 );  次に上面を「黄」で塗りつぶす(上書き)
         moveTo( a1._x, a1._y );
         lineTo( a2._x, a2._y );
         lineTo( a3._x, a3._y );
         lineTo( a4._x, a4._y );
         lineTo( a1._x, a1._y );
         endFill();
        } else {      そうでない(扁平率がマイナス)なら
         beginFill( 0xFFFF00 );  先に上面を「黄」で塗りつぶして
         moveTo( a1._x, a1._y );
         lineTo( a2._x, a2._y );
         lineTo( a3._x, a3._y );
         lineTo( a4._x, a4._y );
         lineTo( a1._x, a1._y );
         endFill();
         beginFill( 0x0000FF );  次に下面を「青」で塗りつぶす(上書き)
         moveTo( b1._x, b1._y );
         lineTo( b2._x, b2._y );
         lineTo( b3._x, b3._y );
         lineTo( b4._x, b4._y );
         lineTo( b1._x, b1._y );
       }
     }
   }

このように、画面の手前に表示させたい部分を下の行に記載します。

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

726

上下面の重なりについては、「扁平率」に着目することで解決しました(*^.^*)
冒頭サンプルは、側面の面についても「塗り」を適用しています。 この場合は、かなり複雑です。
上下面も含めて画面からみて手前に「見える面」だけに塗りを適用するように考えなければなりません。



左図でいえば、上面「a1・a2・a3・a4・a1」と2つの側面「a1・b1・b2・a2・a1」「a4・b4・b1・a1・a4」の3面が塗りの対象です。

まず上下面では、同時に2面見えることはなく、扁平率の正負によって上面か下面のいずれか一方しか見えませんので、上記スクリプトを、次のように変更します。

   function line() {
     with (this) {
       clear();
       lineStyle();
       if( henpei >= 0 ) {     扁平率がプラスなら
         beginFill( 0xFFFF00 );  上面を「黄」で塗りつぶす
         moveTo( a1._x, a1._y );
         lineTo( a2._x, a2._y );
         lineTo( a3._x, a3._y );
         lineTo( a4._x, a4._y );
         lineTo( a1._x, a1._y );
         endFill();
        } else {      そうでない(扁平率がマイナス)なら
         beginFill( 0x0000FF );  下面を「青」で塗りつぶす
         moveTo( b1._x, b1._y );
         lineTo( b2._x, b2._y );
         lineTo( b3._x, b3._y );
         lineTo( b4._x, b4._y );
         lineTo( b1._x, b1._y );
       }
     }
   }

側面の場合・・・
左図の8つの「点」のうち最も手前に見える「点」は、回転角度70度の「a1」「b1」です。(回転角度90度が最前面)
正六面体では側面が同時に3面見えることはないので、最も手前にある「点」の左右両面を塗りの対象にすればよいことがわかります。
問題は、回転している各「点」のうち、どの「点」が最も手前にあるか?という判断をどうするかということですが・・・
上面の各4点、下面の各4点はそれぞれ90度の間隔を保ちながら同じ回転をしていますので、例えば「a1」が最前面の回転角度90度の位置にきたとき、左の「a2」は180度、右の「a4」は0度の位置にあります。
そこからさらに時計回りに回転して「a1」が135度の位置にきたとき、右の「a4」点は45度にあって、真横に並びます(図右)
そしてこれ以上回転すると、「a4」が最も手前の「点」になります。
つまり、各「点」の現在の回転角度を調べて「45度以上135度未満」の範囲にあるとき、その「点」が最も手前にある「点」と判断して、その左右の面を描画するようにすればよいということです。

なお回転角度は、上下対応する「点」同士(例えば「a1」と「b1」)は同じですから、調べるのは一方の「a各点」だけで十分です。
各「点」の固有の回転角度は、それぞれのクリップアクションの中で定義されている・・・

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

ここの「theKakudo」のことですから、各々インスタンス名を付けて、以下のように「if文」で調べます。

   if( a1.theKakudo >= 45 && a1.theKakudo < 135 ) { 「a1」のtheKakudoが45以上135未満なら
     beginFill( 0xFF0000 );  その左の面を「赤」で塗りつぶして
     moveTo( a1._x, a1._y );
     lineTo( b1._x, b1._y );
     lineTo( b2._x, b2._y );
     lineTo( a2._x, a2._y );
     lineTo( a1._x, a1._y );
     endFill();
     beginFill( 0x00FF00 );  その右の面を「緑」で塗りつぶす
     moveTo( a4._x, a4._y );
     lineTo( b4._x, b4._y );
     lineTo( b1._x, b1._y );
     lineTo( a1._x, a1._y );
     lineTo( a4._x, a4._y );
     endFill();
   }
   if( a2.theKakudo >= 45 && a2.theKakudo < 135 ) { 「a2」のtheKakudoが45以上135未満なら
     beginFill( 0x0000FF );   以下同様に、図の立体図をみて個々に指定
     moveTo( a2._x, a2._y );
     lineTo( b2._x, b2._y );
     lineTo( b3._x, b3._y );
     ・・・・・・・・
     endFill();
   }
   if( a3.theKakudo >= 45 && a3.theKakudo < 135 ) { 「a3」のtheKakudoが45以上135未満なら
     ・・・・・・・・
   }
   if( a4.theKakudo >= 45 && a4.theKakudo < 135 ) { 「a4」のtheKakudoが45以上135未満なら
     ・・・・・・・・
   }

727

ところが、実際のtheKakudoの値が「45度以上135度未満」という条件を満たすのは最初の1周だけで、値は一方通行で増えていきます。
なぜなら、上記”726”の「点」のクリップアクションの「kakudo += 5;」を受けて、theKakudoの値がフレームレートごとに永久に「5」ずつ加算されているからです。
例えば、調べた時点の回転角度が「1563度」というようなことなので、これを「360度換算」してから比較する必要があります。
方法は、調べた値を360度で割り算し、その余りが換算された値になります。

 1563度÷360度=4 余り 123度

スクリプトで記載すると、「第5回 Mathランダム徹底研究」の”502”@、余りの算術演算子を使って・・・

   変数 = a1.theKakudo % 360; ←「a1」のtheKakudoの値を360で割った余りを変数に代入

したがって、上記「if文」で各「点」ごとにすべて以下のように修正します。

   if( a1.theKakudo % 360 >= 45 && a1.theKakudo % 360 < 135 ) {
     ・・・・・・・・
   }

これは、変数theKakudo が360度を超えた場合の対応ですが、theKakudo の値を左右するもうひとつの要因・・・

    theKakudo = kakudo + _parent.kakudoX

「回転エリア」ムービークリップで定義している「kakudoX」は、マウスのX座標値と連動して「−270〜+270」の値を取得します。(上記”720”)
ムービーを再生した当初、まだ十分に変数kakudoの値が大きくないとき、「kakudoX」がマイナスの値を取得すると、このtheKakudo の値がマイナスになる可能性があります。
すると、いずれの「点」も条件式に当てはまらない状況が生じて、どの側面も塗られないことになります。
このような状況になることを避けるためには、「kakudoX」がマイナスにならずかつ回転角度を狂わせないようにする工夫が必要です。
そこで・・・
マウスから目標値に値を代入する際、

   kakudoXM = this._xmouse * 270 / 200 + 360

調整値のあとに「360度」を加えることで対処します。

※ここまでのサンプルは、ここをクリック。 (cube8.fla
どうでしょう?上手く成功したでしょうか(^^;

728

マウスを上下に動かして、中央から下3分の1ぐらいの間は上出来ですが、それ以外は何か変です(^^)
もう一度上記”710”の図を見ていただくと・・・
縦回転することによって、当初前面に見えていた各「点」が後ろに回り込み、後ろにあった各「点」が前面にでてくるという逆のケースもあったのですね!
しかし後ろに回りこんだとしても、それは「回転エリア」ムービークリップに記載のスクリプトで各「点」の横回転軌道を縦回転させているだけであって、各「点」ムービークリップ自体は予定通りの回転角度で回転しています。
したがって「うしろに回り込んだ場合」は、最も後面にある「点」(ディスプレイ平面からみると前面)の左右両面を塗りの対象にします。

「うしろに回り込んだかどうか」はどうしてわかるかということですが・・・
上記”710”の図で縦の回転軸が「A」→「B」→「C」と移動していくとき、上面と下面の回転の中心点の距離が「一辺の長さ」→「0」に変化します。
さらに時計回りに回転すると、距離はマイナスの値になって「−0」→「−一辺の長さ」へと変化していきます。
この中心点の距離の2分の1の値が変数Center ですから、変数Center の値の正負符号を調べれば、うしろに回り込んだかどうかがわかります。
そして「うしろに回り込んだ場合」は、上記”726”の図から、「45度以上135度未満」の裏返しである「225度以上315度未満」に「点」があるとき、その左右両面を塗りつぶすことにします。

   if( Center >= 0 ) {  正六面体が普通に正面を向いているとき
     if( a1.theKakudo % 360 >= 45 && a1.theKakudo % 360 < 135 ) {
        ・・・・・・・・
     }
     if( a2.theKakudo % 360 >= 45 && a2.theKakudo % 360 < 135 ) {
        ・・・・・・・・
     }
     if( a3.theKakudo % 360 >= 45 && a3.theKakudo % 360 < 135 ) {
        ・・・・・・・・
     }
     if( a4.theKakudo % 360 >= 45 && a4.theKakudo % 360 = 135 ) {
        ・・・・・・・・
     }
   } else {       逆さまになってるとき
     if( a1.theKakudo % 360 >= 225 && a1.theKakudo % 360 < 315 ) {
        ・・・・・・・・
     }
     if( a2.theKakudo % 360 >= 225 && a2.theKakudo % 360 < 315 ) {
        ・・・・・・・・
     }
     if( a3.theKakudo % 360 >= 225 && a3.theKakudo % 360 < 315 ) {
        ・・・・・・・・
     }
     if( a4.theKakudo % 360 >= 225 && a4.theKakudo % 360 < 315 ) {
        ・・・・・・・・
     }
   }

条件が異なるだけで、各「点」の両面を塗りつぶすことには変わりないので、同じスクリプトを2度記載するのが面倒なときは・・・
比較すべき条件の最小回転角度を「minKakudo」、最大回転角度を「maxKakudo」として、

   if( Center >= 0 ) {
     minKakudo = 45
     maxKakudo = 135
   } else {
     minKakudo = 225
     maxKakudo = 315
   }
   if( a1.theKakudo % 360 >= minKakudo && a1.theKakudo % 360 < maxKakudo ) {
      ・・・・・・・・
   }
   if( a2.theKakudo % 360 >= minKakudo && a2.theKakudo % 360 < maxKakudo ) {
      ・・・・・・・・
   }
   if( a3.theKakudo % 360 >= minKakudo && a3.theKakudo % 360 < maxKakudo ) {
      ・・・・・・・・
   }
   if( a4.theKakudo % 360 >= minKakudo && a4.theKakudo % 360 < maxKakudo ) {
      ・・・・・・・・
   }

このように一度で済ますこともできます。

※ここまでのサンプルは、ここをクリック。 (cube9.fla

 今後の作品に応用できるようスクリプトを編集する

729

どうやら問題ないようですね(*^.^*)
これで、正六面体の回転は作品としては完成です。
しかしここは「ACTIONSCRIPT講座」ですので、もう少しスクリプトにこだわって検証してみたいと思います。

ここまでの「回転エリア」ムービークリップのクリップアクション全文を改めて眺めると・・・

   onClipEvent (load) {
     R = 50;
     speed = 5;
     kakudoX = kakudoXM = 0;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
     function target() {
       kakudoXM = this._xmouse * 270 / 200;
       kakudoYM = this._ymouse * 270 / 150 + 360;
     }
     function move() {
       kakudoX += ( kakudoXM - kakudoX ) / 10;
       kakudoY += ( kakudoYM - kakudoY ) / 10;
       Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
       Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
       Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
       henpei =( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
       line();
     }
     function line() {
       with ( this ) {
         clear();
         lineStyle();
         if ( henpei >= 0 ) {
           beginFill( 0xFFFF00 );
           moveTo( a1._x,a1._y );
           lineTo( a2._x, a2._y );
           lineTo( a3._x, a3._y );
           lineTo( a4._x, a4._y );
           lineTo( a1._x, a1._y );
           endFill();
         } else {
           beginFill( 0x0000FF );
           moveTo( b1._x, b1._y );
           lineTo( b2._x, b2._y );
           lineTo( b3._x, b3._y );
           lineTo( b4._x, b4._y );
           lineTo( b1._x, b1._y );
           endFill();
         }
         if ( Center >= 0 ) {
           minKakudo = 45;
           maxKakudo = 135;
         } else {
           minKakudo = 225;
           maxKakudo = 315;
         }
         if ( a1.theKakudo % 360 >= minKakudo && a1.theKakudo % 360 < maxKakudo ) {
           beginFill( 0xFF0000 );
           moveTo( a1._x, a1._y );
           lineTo( b1._x, b1._y );
           lineTo( b2._x, b2._y );
           lineTo( a2._x, a2._y );
           lineTo( a1._x, a1._y );    
※以下同じ面の塗りが2つずつ存在する
           endFill();

           beginFill( 0x00FF00 );
           moveTo( a4._x, a4._y );
           lineTo( b4._x, b4._y );
           lineTo( b1._x, b1._y );
           lineTo( a1._x, a1._y );
           lineTo( a4._x, a4._y );
           endFill();

         }
         if ( a2.theKakudo % 360 >= minKakudo && a2.theKakudo % 360 < maxKakudo ) {
           beginFill( 0x00FF00 );
           moveTo( a2._x,a2._y );
           lineTo( b2._x, b2._y );
           lineTo( b3._x, b3._y );
           lineTo( a3._x, a3._y );
           lineTo( a2._x, a2._y );
           endFill();

           beginFill( 0xFF0000 );
           moveTo( a1._x, a1._y );
           lineTo( b1._x, b1._y ); 
           lineTo( b2._x, b2._y );
           lineTo( a2._x, a2._y );
           lineTo( a1._x, a1._y );
           endFill();

         }
         if ( a3.theKakudo % 360 >= minKakudo && a3.theKakudo % 360 < maxKakudo ) {
           beginFill( 0xFF0000 );
           moveTo( a3._x, a3._y );
           lineTo( b3._x, b3._y );
           lineTo( b4._x, b4._y );
           lineTo( a4._x, a4._y );
           lineTo( a3._x, a3._y );
           endFill();

           beginFill( 0x00FF00 );
           moveTo( a2._x, a2._y );
           lineTo( b2._x, b2._y );
           lineTo( b3._x, b3._y );
           lineTo( a3._x, a3._y );
           lineTo( a2._x, a2._y );
           endFill();

         }
         if ( a4.theKakudo % 360 >= minKakudo && a4.theKakudo % 360 < maxKakudo ) {
           beginFill( 0x00FF00 );
           moveTo( a4._x, a4._y );
           lineTo( b4._x, b4._y );
           lineTo( b1._x, b1._y );
           lineTo( a1._x, a1._y );
           lineTo( a4._x, a4._y );
           endFill();

           beginFill( 0xFF0000 );
           moveTo( a3._x, a3._y );
           lineTo( b3._x, b3._y );
           lineTo( b4._x, b4._y );
           lineTo( a4._x, a4._y );
           lineTo( a3._x, a3._y );
           endFill();

         }
       }
     }
   }
   onClipEvent (mouseMove) {
     target();
   }
   onClipEvent (enterFrame) {
     move();
   }

非常に長いスクリプトになりましたが、スクリプトの長短が問題なのではありません。
スクリプトは、作成した当初はその内容が理解できていますが、時間が経過すると自分で作成したものでも何が書いてあったのかわからなくなります。
ですからスクリプトは、あとで自分が見ても理解できるものであることと、次の作品に簡単に応用できるものであることが重要です。
次の作品で半径や回転速度、各面の色などを変更しようとするとき、変更箇所が多かったり場所が散乱しているとそれだけ作業量が増えますし、スクリプトの内容が思い出せないような場合は、どこを修正してよいのかさえ迷うことになります。
側面の塗り部分も、機能的には問題がないものの同じ面の記載が2つずつ重複しています。そのままだと修正の際、それぞれに手を加えなければならないことになります。

730

これをどのように再編集するかは、それこそ自分があとで見て理解できるようにすることですから人によって様々ですが、一例として・・・

各面ごとに任意のメソッドで「function定義」して、塗る必要があるときにその面のメソッドを呼び出すようにします。

   function paintUe() {    ※上面の塗りを「paintUe」というメソッド名で以下に定義
     beginFill( colorUe );
     moveTo( a1._x,a1._y );
     lineTo( a2._x, a2._y );
     lineTo( a3._x, a3._y );
     lineTo( a4._x, a4._y );
     lineTo( a1._x, a1._y );
     endFill();
   }
   function paintSita() {    ※下面の塗りを「paintSita」というメソッド名で以下に定義     
     beginFill( colorSita );
     moveTo( b1._x, b1._y );
     lineTo( b2._x, b2._y );
     lineTo( b3._x, b3._y );
     lineTo( b4._x, b4._y );
     lineTo( b1._x, b1._y );
     endFill();
   }
   function paintYoko1() {    ※側面1の塗りを「paintYoko1」というメソッド名で以下に定義     
     beginFill( colorYoko1 );
     ・・・・・・
   }
   function paintYoko2() {    ※側面2の塗りを「paintYoko2」というメソッド名で以下に定義     
     beginFill( colorYoko2 );
     ・・・・・・
   }
   function paintYoko3() {    ※側面3の塗りを「paintYoko3」というメソッド名で以下に定義     
     beginFill( colorYoko3 );
     ・・・・・・
   }
   function paintYoko4() {    ※側面4の塗りを「paintYoko4」というメソッド名で以下に定義     
     beginFill( colorYoko4 );
     ・・・・・・
   }

そして、

   if( henpei >= 0 ) {   扁平率がプラスのとき
     paintUe();       ←「paintUe()」メソッドを実行して上面を塗る
   } else {          そうでないとき
     paintSita();      ←「paintSita()」メソッドを実行して下面を塗る
   }
   ・・・・・・・
   if ( a1.theKakudo % 360 >= minKakudo && a1.theKakudo % 360 < maxKakudo ) {  「a1」が前面にあるとき
     Yoko1();         ←「Yoko1()」メソッドと「Yoko4()」メソッドを実行して側面1と側面4を塗る
     Yoko4()
   }
   ・・・・・   
    
というようにします。

また、「function定義」の中のそれぞれの面の色も、一旦「color****」という変数に置き換えて、「load」イベントのはじめに・・・

   onClipEvent (load) {
     R = 50;
     speed = 5;
     colorUe = 0xFFFF00;
     colorSita = 0x0000FF;
     colorYoko1 = 0xFF0000;
     colorYoko2 = 0x00FF00;
     colorYoko3 = 0xFF0000;
     colorYoko4 = 0x00FF00;
     kakudoX = kakudoXM = 0;
     kakudoY = kakudoYM = 60;
     ・・・・・・・・・・・

このように定義しておけば、ここでカラーコードを入れ替えるだけで簡単に色を変更できるようになります。

さらに、各面の「function定義」の中の・・・

     beginFill( 変数 );
     moveTo( xP1, yP1 );
     lineTo( xP2, yP2 );
     lineTo( xP3, yP3 );
     lineTo( xP4, yP4 );
     lineTo( xP1, yP1 );
     endFill();

このパターンに着目して、これ自体を別途「function定義」して記述の簡略化を図ることもできますが、あまりやりすぎると返ってわからなくなることもありますので、ここではこの程度で止めておきます(*^.^*)

731

以下、冒頭サンプルのアクションスクリプトとflaファイルです。

■「回転エリア」ムービークリップのクリップアクション。

   onClipEvent (load) {
     R = 50;
     speed = 5;
     colorUe = 0x888888;
     colorSita = 0x888888;
     colorYoko1 = 0xAAAAAA;
     colorYoko2 = 0xCCCCCC;
     colorYoko3 = 0xAAAAAA;
     colorYoko4 = 0xCCCCCC;
     kakudoX = kakudoXM = 0;
     kakudoY = kakudoYM = 60;
     Ippen = Math.sqrt( 2 * R * R );
     function target() {
       kakudoXM = this._xmouse * 270 / 200;
       kakudoYM = this._ymouse * 270 / 150 + 360;
     }
     function move() {
       kakudoX += ( kakudoXM - kakudoX ) / 10;
       kakudoY += ( kakudoYM - kakudoY ) / 10;
       Center = Math.sin( kakudoY * Math.PI / 180 ) * Ippen / 2;
       Radian1 = ( kakudoY + 45 ) * Math.PI / 180;
       Radian2 = ( kakudoY - 45 ) * Math.PI / 180;
       henpei =( Math.sin( Radian1 ) - Math.sin( Radian2 ) ) * R / Ippen;
       line();
     }
     function line() {
       with ( this ) {
         clear();
         lineStyle();
         if ( henpei >= 0 ) {
           paintUe();
         } else {
           paintSita();
         }
         if ( Center >= 0 ) {
           minKakudo = 45;
           maxKakudo = 135;
         } else {
           minKakudo = 225;
           maxKakudo = 315;
         }
         if ( a1.theKakudo % 360 >= minKakudo && a1.theKakudo % 360 < maxKakudo ) {
           paintYoko1();
           paintYoko4();
         }
         if ( a2.theKakudo % 360 >= minKakudo && a2.theKakudo % 360 < maxKakudo ) {
           paintYoko2();
           paintYoko1();
         }
         if ( a3.theKakudo % 360 >= minKakudo && a3.theKakudo % 360 < maxKakudo ) {
           paintYoko3();
           paintYoko2();
         }
         if ( a4.theKakudo % 360 >= minKakudo && a4.theKakudo % 360 < maxKakudo ) {
           paintYoko4();
           paintYoko3();
         }
       }
     }
     function paintUe() {
       beginFill( colorUe );
       moveTo( a1._x, a1._y );
       lineTo( a2._x, a2._y );
       lineTo( a3._x, a3._y );
       lineTo( a4._x, a4._y );
       lineTo( a1._x, a1._y );
       endFill();
     }
     function paintSita() {
       beginFill( colorSita );
       moveTo( b1._x, b1._y );
       lineTo( b2._x, b2._y );
       lineTo( b3._x, b3._y );
       lineTo( b4._x, b4._y );
       lineTo( b1._x, b1._y );
       endFill();
     }
     function paintYoko1() {
       beginFill( colorYoko1 );
       moveTo( a1._x, a1._y );
       lineTo( b1._x, b1._y );
       lineTo( b2._x, b2._y );
       lineTo( a2._x, a2._y );
       lineTo( a1._x, a1._y );
       endFill();
     }
     function paintYoko2() {
       beginFill( colorYoko2 );
       moveTo( a2._x, a2._y );
       lineTo( b2._x, b2._y );
       lineTo( b3._x, b3._y );
       lineTo( a3._x, a3._y );
       lineTo( a2._x, a2._y );
       endFill();
     }
     function paintYoko3() {
       beginFill( colorYoko3 );
       moveTo( a3._x,a3._y );
       lineTo( b3._x, b3._y );
       lineTo( b4._x, b4._y );
       lineTo( a4._x, a4._y );
       lineTo( a3._x, a3._y );
       endFill();
     }
     function paintYoko4() {
       beginFill( colorYoko4 );
       moveTo( a4._x, a4._y );
       lineTo( b4._x, b4._y );
       lineTo( b1._x, b1._y );
       lineTo( a1._x, a1._y );
       lineTo( a4._x, a4._y );
       endFill();
     }
   }
   onClipEvent (mouseMove) {
     target();
   }
   onClipEvent (enterFrame) {
     move();
   }

■「回転エリア」内の各「点」ムービークリップのクリップアクション。

   onClipEvent (load) { 
     kakudo = 0;      ※各々「0、90、180、270」に設定
     function kaiten() {
       theKakudo = kakudo + _parent.kakudoX;
       theRadian = theKakudo * Math.PI / 180;
       this._x = Math.cos( theRadian ) * _parent.R;
       this._y = Math.sin( theRadian ) * _parent.R * _parent.henpei - _parent.Center;
       kakudo += _parent.speed;                     ※「a」は「-」、「b」は「+」
     }
   }
   onClipEvent (enterFrame) {
     kaiten();
   }

cube.fla



第7回 正六面体の回転(番外編) 戻る このページの先頭へ  

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

トップページ