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

第51回 ベクトルの内積で面の向きを調べる

この記事を読むのに必要な時間:およそ 5.5 分

ベクトルの内積から面の向きがわかる

ベクトルは方向をもった大きさで,矢印により表現されることが多い図3⁠。矢印の長さが大きさ,矢じりは方向を示す。そして,内積はふたつのベクトルによる計算だ。計算結果は数値(実数)で,その値はふたつのベクトル同士の向きや角度に関わる。取りあえず,これくらいの情報を頭に入れておけばよい。

図3 ふたつのベクトルを矢印で示す

図3 ふたつのベクトルを矢印で示す

つまり,面の裏表を内積で調べようとすると,ふたつのベクトルが必要だということになる。ひとつは面の向きを示すベクトル。もうひとつは視線のベクトルだ。今回の正方形を回す場合なら,ふたつのベクトルの方向はすぐにわかる。面はz軸手前方向,視線は逆のz軸奥向きである。実は,面の向きを調べるだけであれば,ベクトルの大きさはどうでもよい。そういうときは,大きさを1に決めておくと,後あと計算が楽になる。

3次元空間のベクトルを表すために,ActionScript 3.0ではVector3Dオブジェクトを用いる。ということで,ふたつのベクトルの3次元空間座標は次の表1のように定められる。もちろん,面の向きは初期値で,アニメーションによって変わる。それに対して,視線は動かない。

表1視線と面のベクトルの3次元座標

ベクトル座標
視線(0, 0, 1)
面(初期値)(0, 0, -1)

そして,本題の内積だ。面の裏表を調べるには,視線と面のふたつのベクトルの内積から,その値の符号(正負)だけ取出せばよい表2⁠。図4は水平に回る面をy軸の方向,つまり3次元空間の真上から見ている。内積の値は,視線と面のベクトルのなす角が鋭角のときは正,鈍角なら負の値になる。前者は面が裏向き,後者は表向きということだ。そして,内積がちょうど0のとき,面は真横を向いている。

表2視線と面のベクトルの内積となす角

面の向きふたつのベクトルのなす角内積の符号
90度より小さい(鋭角)+(正)
真横90度(直角)0
90度より大きい(鈍角)-(負)

図4 視線と面のふたつのベクトルから面の裏表を調べる

図4 視線と面のふたつのベクトルから面の裏表を調べる

ここまでくれば,内積の計算は簡単だ。Vector3D.dotProduct()メソッドを呼出せばよい。なお,参照する(ターゲットの)Vector3Dオブジェクトと引数のVector3Dオブジェクトは,入替えても内積の値は同じになる。

  • Vector3Dオブジェクト.dotProduct(もうひとつのVector3Dオブジェクト)

面の表と裏を塗替える

では,いよいよ第48回スクリプト2に手を加える。第1に,視線と面のベクトルをVector3Dオブジェクトとして変数(viewVector3DとfaceVector3D)に定める。視線のベクトルの座標(0, 0, 1)は,定数Vector3D.Z_AXISと同じだ。面のベクトルは,視線のベクトルの向きを逆にした(0, 0, -1)だった(前掲表1⁠。

第2は,面の表裏を調べる関数(xIsFront())の定義だ。表のときtrue裏ならfalseを返すことにする。この関数の引数はふたつ,面のVector3Dオブジェクトと変換行列のMatrix3Dオブジェクトにした。なぜなら,視線はもちろん,面のベクトルを示すVector3Dオブジェクトも動かないからだ。第48回につぎのように説明したことを想い起こしてほしい(⁠Utils3D.projectVectors()メソッドを使うの項⁠⁠。

今回3次元空間の頂点座標は動かさない。変換のMatrix3Dオブジェクトに回転を加えて,その結果のオブジェクトを変数(worldMatrix3D)に残す。つまり,座標ではなく変換行列で座標変換を管理するのだ。

したがって,面のベクトルが今どちらを向いているかを調べるには,変換のMatrix3Dオブジェクトが欠かせない。面の表裏を調べる関数(xIsFront())は,まずMatrix3D.transformVector()メソッドを用いて今の面の向きをVector3Dオブジェクト(directionVector3D)として得る(メソッドについては,第42回「Vector3Dクラスの3次元空間座標とインスタンスへの描画」Vector3DインスタンスをMatrix3Dオブジェクトで座標変換するの項参照⁠⁠。つぎに,その面の向きのVector3Dオブジェクトと視線(viewVector3D)の内積を求めて,値が負なら表向きを意味するtrue0以上ならfalseを返す。

// フレームアクションに追加
var viewVector3D:Vector3D = Vector3D.Z_AXIS;   // 視線のベクトル
var faceVector3D:Vector3D = new Vector3D(0, 0, -1);   // 面のベクトル

function xRotate(eventObject:Event):void {
  var nRotationY:Number = mySprite.mouseX * nDeceleration;
  var vertices2D:Vector.<Number> = new Vector.<Number>();
  xTransform(vertices2D, nRotationY);
  var bFront:Boolean = xIsFront(faceVector3D, worldMatrix3D);  // 面の表裏を調べる
  trace(bFront);  // 確認用
  xDraw(vertices2D);
}

// 面の表裏を調べる関数の定義
function xIsFront(myVector3D:Vector3D, myMatrix3D:Matrix3D):Boolean {
  var directionVector3D:Vector3D = myMatrix3D.transformVector(myVector3D);
  var bFront:Boolean = (viewVector3D.dotProduct(directionVector3D) < 0);
  return bFront;
}

面の表裏を調べる関数(xIsFront())は,正方形を回すDisplayObject.enterFrameイベントのリスナー関数(xRotate())から呼出す。実際に表裏のテクスチャを切替えるには,面を塗る関数(xDraw())にも手を加えなければならない。さしあたり確認用のtrace()関数で,戻り値(bFront)[出力]している。実際に[ムービープレビュー]で確かめるときには,フレームレートを落とした方が見やすい図5⁠。

図5 関数の戻り値を[出力]するときフレームレートは落とした方が見やすい

図5 関数の戻り値を[出力]するときフレームレートは落とした方が見やすい

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書