Redpoll's 60
第2章 2D空間におけるオブジェクトの運動
$§$2-1 オブジェクトの初期状態$§$2-17 衝突判定 5
$§$2-2 行列による変換の詳細 1$§$2-18 初期状態における頂点情報の取得について
$§$2-3 行列による変換の詳細 2$§$2-19 衝突判定 6 (軸平行な長方形同士の衝突)
$§$2-4 自転と公転$§$2-20 衝突判定 7 (円盤 vs 長方形)
$§$2-5 一体化したオブジェクトの運動 1$§$2-21 衝突判定 8 (回転した長方形同士の衝突)
$§$2-6 一体化したオブジェクトの運動 2$§$2-22 衝突判定 9 (「倉庫番」プログラムの作成)
$§$2-7 一体化したオブジェクトの運動 3$§$2-23 衝突判定 10 (円盤 vs 三角形)
$§$2-8 指定方向へのオブジェクトの移動 1$§$2-24 衝突判定 11 (直線 vs 長方形、円盤、直線)
$§$2-9 指定方向へのオブジェクトの移動 2$§$2-25 円と直線による補間曲線 1
$§$2-10 指定方向へのオブジェクトの移動 3$§$2-26 円と直線による補間曲線 2
$§$2-11 指定方向へのオブジェクトの移動 4 (連射プログラムの実装)$§$2-27 その他の重要事項 1 (UnityのTransformクラスによる記述 ; カメラ移動の基本)
$§$2-12 指定方向へのオブジェクトの移動 5$§$2-28 その他の重要事項 2 (画面に表示されるXY平面の範囲 ; ミニマップの実装)
$§$2-13 衝突判定 1 (点 vs 円盤、長方形 ; ローカル座標からワールド座標への変換 2D)$§$2-29 その他の重要事項 3 (スクリーン座標からワールド座標への変換 2D; スクリーンショットの撮影範囲)
$§$2-14 衝突判定 2$§$2-30 課題 1
$§$2-15 衝突判定 3$§$2-31 課題 2
$§$2-16 衝突判定 4

$§$2-9 指定方向へのオブジェクトの移動 2


前節ではキー操作によるオブジェクトの移動を扱った。そこで対象となったオブジェクトの形状は円であり、オブジェクトの外観も1色のみで構成されていた。円という形状は特殊な形状で、何度回転させても外観上の変化がない。そういった理由で前節のオブジェクトPointについては、向き、すなわちオブジェクトのOrientationを一切設定しなかった。本節では、まず始めに長方形オブジェクトの運動を扱う。長方形を移動させる場合には円と違って、オブジェクトの向きを設定しなければ不自然な場合が発生するが、以下の最初のプログラムにおいて、その具体的な例をまずは見てみよう。


# Code1
図1は、Rectという長方形オブジェクトの初期状態である。

図1 Rect 初期状態
図2 Code1 実行結果

まず、このオブジェクトを$360$°任意の方向に移動させるプログラムである。ここでもまた前節で使われた進行線を、オブジェクトの移動方向を示すために用いている。プログラムは前節のCode7を1箇所のみ変更してそのまま使用している。変更点は移動させるオブジェクトが Pointから Rectに変わるので、Code7で使われていた変数 Pointをここでは Rectという変数名に変更している。
以下に、操作方法とプログラムを示す。

H  :  進行線が反時計周りに回転(回転は$2$°ずつ)。
L  :  進行線が時計周りに回転。
S  :  Rectを進行線の方向に一定の距離だけ移動させる(長押しに対応する)。

[Code1]  (実行結果 図2)
Vector2 curPos = Rect.GetPosition();

if (Input.GetKey(KeyCode.H))
{
    i_forwardDegree += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_forwardDegree -= 2;
}

THMatrix3x3 R = TH2DMath.GetRotation3x3(i_forwardDegree);
Vector2 forwardDir = R * new Vector3(0, 1, 1);

float d = 0.05f;
Vector2 newPos = (Input.GetKey(KeyCode.S)) ? curPos + d * forwardDir : curPos; 

THMatrix3x3 T = TH2DMath.GetTranslation3x3(newPos);
Rect.SetMatrix(T);

プログラム中でつかわれている curPosi_forwardDegreeforwardDir などは、前節のものと使用目的は同じである。すなわち、curPosは Rectの現在位置を表し、i_forwardDegreeは進行線の初期の方向であるy軸プラス方向(単位ベクトルで表せば$(0, 1)$)からの回転角度で、forwardDirは進行線の現在の方向を表している。進行線の現在の方向は、初期の方向であるy軸プラス方向から角度i_forwardDegreeだけ回転させた方向であり、13行目の計算でそれを求めている。Rは角度i_forwardDegreeだけ回転させる回転行列であり、(0, 1, 1)は$(0, 1)$を同次座標で表したものである。
18行目で算出される平行移動行列Tは、newPosだけの平行移動を行うものであるが、Rectの場合は初期状態の位置が原点であるので、このTを実行することにより結果的には newPosに移動することになる (座標で表せば (newPos.x, newPos.y) へ移動することになる)。Rectが初期状態の位置である原点から、毎フレーム計算された位置(newPos)への移動を行うことで実行結果(図2)に見られるように、$360$°任意の方向に移動することになるのである。
このプログラムは、Rectをキー操作によって$360$°任意の方向に移動させるということに関しては、特に問題点はない。しかし、円形オブジェクトを移動させる場合と違って、長方形オブジェクトの移動の場合には、その進行方向に自身の先端 (Rectの場合は、青色の小さい領域がある側)が向いている方が、より自然な印象を与える。そこで、Rectに対して向きの設定をする必要が生じるわけである。


# Code2
図3 Code2 実行結果
Code1では Rectの進行方向は、HやLキーによって$360$°変えることができ、その進行方向は進行線という細長い線によって示されていた。
今回は Rectの先端(青色の小さい領域がある側)が常に進行線の方向と一致するようにプログラムを書き改める。ただし、このプログラムでは Rectの移動は扱わないので、使用するキーはHとLのみである。
実装は簡単である。Code1では、HやLキーで i_forwardDegreeが更新され、i_forwardDegreeだけ回転させる行列Rが12行目で算出された。このRは進行線の初期の方向であるy軸プラス方向を、現在の方向へ回転させる行列である。Rectの先端は初期状態では進行線と同じくy軸プラス方向を向いているので、回転行列Rを初期状態のRectに実行すれば進行線と同じ方向を向くことになる。

[Code2]  (実行結果 図3)
if (Input.GetKey(KeyCode.H))
{
    i_forwardDegree += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_forwardDegree -= 2;
}

THMatrix3x3 R = TH2DMath.GetRotation3x3(i_forwardDegree);
Vector2 forwardDir = R * new Vector3(0, 1, 1);

Rect.SetMatrix(R);

11行目の forwardDir は Rectの現在の向きを表すローカル変数であるが、ここでは計算結果がセットされるだけで それ以降の使用はない。


# Code3
図4 Code3 実行結果
Code2ではRectの向きを$360$°回転できるようにした。ここではRectの先端の方向に、Rectが移動するプログラムを作成しよう。
実行手順は次の通り。
(1) Rectの先端を進ませたい方向に回転させる。
(2) Rectの先端の方向に一定距離進ませる。

操作方法は以前のものと変わるところはない (ここからは進行線は使われないのでHやLキーを押して回転するのは Rectのみである)。
H  :  Rectが反時計周りに回転(回転は$2$°ずつ)。
L  :  Rectが時計周りに回転。
S  :  Rectを進行線の方向に一定の距離だけ移動させる(長押しに対応する)。

[Code3]  (実行結果 図4)
Vector2 curPos = Rect.GetPosition();

if (Input.GetKey(KeyCode.H))
{
    i_forwardDegree += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_forwardDegree -= 2;
}

THMatrix3x3 R = TH2DMath.GetRotation3x3(i_forwardDegree);
Vector2 forwardDir = R * new Vector3(0, 1, 1);

float d = 0.05f;
Vector2 newPos = (Input.GetKey(KeyCode.S)) ? curPos + d * forwardDir : curPos; 

THMatrix3x3 T = TH2DMath.GetTranslation3x3(newPos);
THMatrix3x3 M = T * R;
Rect.SetMatrix(M);

このプログラムはCode1とほとんど同じである。1箇所異なる点は、Rectに実行する変換行列がこのプログラムでは19行目の M = T * R; になっていることである。この変換行列Mは、まず 初期状態の Rectを i_forwardDegreeだけ回転させ(R)、次に newPosだけの平行移動を行う(T) という2つの変換を1つにまとめたものである。

図5
図6 コマ送り表示

図5は実行結果(図4)の最初の方のいくつかのフレームにおいて、オブジェクトに実行される変換をアニメーションとして表したものである。図5において白い文字で「R」と表示されている状態は回転行列Rが実行されている過程を意味し、「T」と表示されている状態は平行移動行列Tが実行されている過程を意味している。
指定の向きに回転を行ってから、計算された位置への移動 という処理を毎フレーム実行していくことで実行結果(図4あるいは図6)に見られるように、Rectが自身の先端の向きに進んでいくことになるのである。
図6は実行結果(図4)の最初の方の20フレーム程をコマ送りで表示したものである。


ここでは、キー操作によってオブジェクトを運動させたが、プログラムにおける運動の実装手順は、2-4節(自転と公転)におけるCode2(あるいは Code3)の場合と本質的には変わらない。2-4節 Code2においても、オブジェクトDiskが初期状態での向きから回転し、次に初期状態での位置から現在の位置へ移動したのであった。つまり、毎フレーム初期状態の Diskに対して変換が実行されていた。上記図5でも、毎フレーム初期状態の Rectに対して回転、平行移動という順で変換が実行されているのである。


# Code4
ここから先の節の後半部分では、一体化した2つのオブジェクトをキー操作によって運動させる問題について扱う。

次の3つの図は、左からBody、Barrel、そしてその2つのオブジェクトを一体化した2D形状の戦車である。

  • 図7 Body 初期状態
  • 図8 Barrel 初期状態
  • 図9 Body、Barrelを一体化した状態

まずは Body、Barrelを一体化して戦車を構成するプログラムから始めよう。

この場合の一体化では、Barrelを Bodyにアタッチする。つまり、Bodyが親オブジェクトであり、Barrelが子オブジェクトである。通常、アタッチの際には子オブジェクトを親オブジェクトの指定位置(アタッチポジション)に移動させるが、今回の例では、子オブジェクトの初期状態での位置が既にアタッチポジションなので、アタッチのために子オブジェクトを移動させる必要がない。したがって一体化のプログラムは次のようなものになる。

[Code4]  (実行結果 図9)
THMatrix3x3 localBarrel = THMatrix3x3.identity;
THMatrix3x3 localBody   = THMatrix3x3.identity;
THMatrix3x3 worldBarrel = localBody * localBarrel;
THMatrix3x3 worldBody   = localBody;

Barrel.SetMatrix(worldBarrel);
Body.SetMatrix(worldBody);

プログラム中の行列 local###world###は、ここではすべて identity行列である。今回の処理だけを考えれば、こういった記述は冗長であり必要ではない (Barrelと Bodyに、直接 identity行列をセットしても同じ結果になる)。2-5節から2-7節で見たように、これはオブジェクトを一体化して運動させるための記述であるので、ここではそれを明示的に示したが、もちろん形式的な意味しか持たない。
今回はアタッチのための移動が発生しないので、localBarrelには identity行列がセットされているが、もしアタッチポジションが $(a, b)$ ならば、localBarrelには次のようにセットすればよい。
THMatrix3x3 localBarrel = TH2DMath.GetTranslation3x3(a, b);


# Code5
図10 Code5 実行結果
Code4では一体化しただけでオブジェクトを動かさなかったので、親である Body、子である Barellの両方には identity行列が実行されていた。今回のプログラムでは2つのオブジェクトを一体化して、Code3(Rectの移動プログラム)のように$360$°任意の方向に向きを変え、Body先端の示す方向に移動できるようにする (Bodyの先端は、図7のBody上側の辺の中央部分)。
プログラムは、Code3のキー操作による Rectの移動プログラムとほとんど同じである。
しかし、主な違いとして 2つのオブジェクトを一体化し、その一体化したオブジェクトを運動させるという点、そして、その移動はキーを押し続けることによって行われるのではなく、キーを1回押せば再度押すまで移動が続くという点、また、使用している変数名が異なっているという点が挙げられる (変数名が違うだけで役割は同じ)。

使用するキーは次のとおり。
H  :  Bodyが反時計周りに回転 (回転は$2$°ずつ)。
L  :  Bodyが時計周りに回転。
S  :  Bodyの移動/停止用スイッチ。停止中に押すと、Bodyの向いている方向へ一定速度で移動を開始する (移動中に押すと停止する)。

[Code5]  (実行結果 図10)
if (Input.GetKey(KeyCode.H))
{
    i_degBody += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_degBody -= 2;
}

if (Input.GetKeyDown(KeyCode.S))   // 移動/停止
{
    i_MOVE = !i_MOVE;
}

THMatrix3x3 rotBody = TH2DMath.GetRotation3x3(i_degBody);
Vector2 direBody = rotBody * new Vector3(0, 1, 1);

Vector2 curPos = Body.GetPosition();
Vector2 newPos = (i_MOVE) ? curPos + 0.05f * direBody : curPos; 

THMatrix3x3 localBarrel = THMatrix3x3.identity;

THMatrix3x3 traBody = TH2DMath.GetTranslation3x3(newPos);
THMatrix3x3 localBody = traBody * rotBody;

THMatrix3x3 worldBarrel = localBody * localBarrel;
THMatrix3x3 worldBody   = localBody;

Barrel.SetMatrix(worldBarrel);
Body.SetMatrix(worldBody);

19行目までは内容的にはCode3と同じである。ただ、前述したように変数名が異なっていることと、ここでは Sキーは移動/停止用のスイッチとしての役割を持っているので、10~13行目にSキー用の ifブロックが追加されている。この ifブロックでは、Sキーを押すごとに 移動するか停止するか を表す12行目のbool型インスタンス変数 i_MOVE の値が切り替わる。i_MOVEの値がtrueであれば、再度Sキーが押されるまでオブジェクトの移動は続いていく。
プログラム中で使用されている変数 i_degBody は Bodyの回転角度を表すインスタンス変数で、初期状態の方向であるy軸プラス方向から何度回転したかを表している。15行目の rotBody は角度i_degBodyだけ回転させる回転行列であり、16行目の direBody は Bodyの現在の方向を表すVector2型のローカル変数である。direBodyの示す方向が Bodyの進む方向である。
21行目以降は 1箇所を除いてCode4と同じである。それは24行目で localBodyへ行列をセットする箇所である。
THMatrix3x3 localBody = traBody * rotBody;
Code4においては localBodyへセットされていた行列は identity行列であった。ここでは上記のように traBodyrotBodyの積がセットされている。これによって、今回のプログラムでは運動が発生するのである。
21行目以降の処理を解説しよう。
Bodyに実行される行列worldBodylocalBodyのコピーであり、具体的には、まず初期状態から i_degBodyだけ回転させ(rotBody)、次に初期の位置(原点)から このフレームで計算された位置(newPos)へ平行移動させる(traBody) という2つの変換をまとめたものである。
Barrelに実行される行列worldBarrelは、まず Barellを Bodyにアタッチし(localBarrel : 上で述べたように今回のアタッチは何も動かす必要はないのでidentity行列)、そのアタッチ後の位置から Bodyに実行する変換を Barrelに対しても実行する(localBody)。重要なのは Barrelを Bodyにアタッチしてから、Barrelに対し、Bodyに実行するものと同じ変換行列localBodyを実行していることである。これにより、Barrelはアタッチされたその位置からBodyと同じ運動をすることになり、この例でいえば、Bodyの上に載せられた状態で一体化して運動することになるのである。
ただ、今回のプログラムでは localBarrelは identity行列なので、worldBarrelworldBodyの表す変換は同じものである。実行結果(図10)を見ればわかるように、それによって Body、Barrelはともに同じ運動をすることになる。


# Code6
Code4、Code5において Barellに実行される行列worldBarrelは次のようにセットされていた。
THMatrix3x3 worldBarrel = localBody * localBarrel;
Code4、Code5におけるlocalBarrelは identity行列であったので Barrelが独自に運動をすることはなかった。今回は 子オブジェクトBarrelをキー操作によって独自に運動させることについて考えていく。

2-5節から2-7節において、複数のオブジェクトを一体化して運動させる場合に使用した local### という行列は個々のオブジェクトそれぞれの独自の運動を表していた (子オブジェクトを持つ親オブジェクトの場合は、local### は親オブジェクトだけでなくアタッチされている子オブジェクトも一体となって運動するのであった)。今回のプログラムでは Barrelに独自の運動をさせるために、localBarrel にBarrelの独自の運動を定義する。

実際に、キー操作による Barrelの独立した運動を実装しよう。
Code5では、HあるいはLキーによって Bodyと Barrel、すなわち 戦車全体が回転した。ここでは、さらに次のキー操作を追加してBarrelだけが(Bodyとは別に)回転できるようする。
D  :  Barrelが反時計周りに回転(回転は$4$°ずつ)。
F  :  Barrelが時計周りに回転。

H及びLキーは今まで通り戦車全体、すなわち Bodyとその上に載っている Barrelの回転を表す(回転角度は1フレームあたり$2$°ずつの回転である)。
また、Barrelの回転のために新しいインスタンス変数を1つ追加する。
int i_degBarrel = 0;
i_degBarrelは Barrelの現在の回転角度のことで、初期の方向であるBodyの先端から何度回転したかを表す (初期値は$0$)。ここで注意すべきことは、Barrelは初期状態で$(0, 1)$の方向、すなわち y軸プラス方向を向いているが(図8)、i_degBarrelの表す角度はy軸プラス方向からの回転角度ではなく、Bodyの先端からの回転角度であるということである。
以下の図11は、Bodyが回転していない状態で Barrelを$30$°回転させた図である。そして、隣の図12は、図11の状態から Bodyを$45$°回転させた図である。

図11 Barrelの30°回転
図12 Barrelの30°回転 (Bodyも45°回転している)

Bodyの先端とは Bodyの上側の辺の中央部分のことで、上図においては赤い点線上に存在する。したがって、Body先端からの回転角度とは赤い点線からの回転角度に等しい。図12では Bodyが$45$°回転しているので、Barrelはy軸プラス方向から$75$°回転させた方向を向いているが、i_degBarrelは Bodyの先端からの回転角度なので、図12におけるその値は$30$である。
以下の図13は、Bodyが回転していない状態で Barrelを$60$°回転させた図である。図14は、図13の状態から Bodyを$120$°回転させた図である。図14では Bodyが$120$°回転しているので、Barrelはy軸プラス方向から$180$°回転させた方向を向いているが、i_degBarrelの値は$60$である。

図13 Barrelの60°回転
図14 Barrelの60°回転(Bodyも120°回転している)

[Code6]  (実行結果 図15)
if (Input.GetKey(KeyCode.H))
{
    i_degBody += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_degBody -= 2;
}

if (Input.GetKey(KeyCode.D))
{
    i_degBarrel += 4;
}
else if (Input.GetKey(KeyCode.F))
{
    i_degBarrel -= 4;
}

THMatrix3x3 rotBarrel = TH2DMath.GetRotation3x3(i_degBarrel);
THMatrix3x3 localBarrel = rotBarrel;

THMatrix3x3 rotBody = TH2DMath.GetRotation3x3(i_degBody);
THMatrix3x3 localBody = rotBody;

THMatrix3x3 worldBarrel = localBody * localBarrel;
THMatrix3x3 worldBody   = localBody;

Barrel.SetMatrix(worldBarrel);
Body.SetMatrix(worldBody);

図15 Code6 実行結果
今回のプログラムでは移動は扱っていない。Barrelの独自の回転のみが目的である。
10~17行目は、D、Fキーによる Barrelの回転角度i_degBarrelの更新処理である。19行目で i_degBarrelだけ回転させる行列rotBarrelを取得し、これを localBarrelにそのままコピーしている(20行目)。今回は移動が発生しないので、Bodyに実行する変換も回転だけであり、その回転行列は22~23行目で取得されている。
ここでも重要なのは25行目の Barrelに実行される行列worldBarrelである。この行列が表す変換は、まず Barrel自身の回転を行い(localBarrel)、その回転した状態から Bodyに対して実行する回転と同じ回転を実行するのである(localBody)。こうすることによって、Barrelが Bodyの上で独自に回転することが実現されるのである。
もし、D、Fキーを1度も押さなければ、localBarrelは何も回転を行わない行列になり、実際に Barrelに実行される回転は localBodyのみとなる。その場合には、Barellと Bodyは同じ回転をすることになるので、Barrelが Bodyの上に固定された状態で回転することになる。

このプログラムでは H、Lキーの ifブロックと D、Fキーの ifブロックはつながっていないので、Bodyの回転と Barrelの回転を同時に行うことができる。例えば、Hキーを押した状態で、Fキーを押し続けると Bodyが反時計周りに回転しながら、その上で Barrelが時計周りに回転をすることになる。
仮に D、Fキーを次のように else if によって、H、Lキーの ifブロックとつなげたとしよう。
    if (Input.GetKey(KeyCode.H))
    {
        ...    
    }
    else if (Input.GetKey(KeyCode.L))
    {
        ...        
    }
    else if (Input.GetKey(KeyCode.D))
    {
        ...    
    }
    else if (Input.GetKey(KeyCode.F))
    {
        ...    
    }

この場合は、Bodyの回転中に Barrelを回転させることはできない。Barrelを回転させるためには、一旦 Bodyの回転を中断しなければいけない(具体的には、HとDが同時に押されている場合は、順序的に上に位置するHのブロックに入るのでDのブロックには入らない。つまり、Bodyの回転は行われるが Barrelの回転は行われない)。
Bodyと Barrelの回転を同時に行うためには、D、Fキーの ifブロックは独立させておくことが必要なのである。


# Code7
図16 Code7 実行結果
では最後に、Code6に Bodyの移動処理を追加しよう。この移動処理はすでに、何度か見てきているSキーによる操作である。
最終的なキー操作は以下のようになる。
H  :  Bodyが反時計周りに回転(回転は$2$°ずつ)。
L  :  Bodyが時計周りに回転。
D  :  Barrelが反時計周りに回転(回転は$4$°ずつ)。
F  :  Barrelが時計周りに回転。
S  :  Bodyの移動/停止用スイッチ。停止中に押すと、Bodyの向いている方向へ一定速度で移動を開始する (移動中に押すと停止する)。

プログラムは今までのものをまとめただけで、新たな処理はない。
[Code7]  (実行結果 図16)
if (Input.GetKey(KeyCode.H))
{
    i_degBody += 2;
}
else if (Input.GetKey(KeyCode.L))
{
    i_degBody -= 2;
}

if (Input.GetKey(KeyCode.D))
{
    i_degBarrel += 4;
}
else if (Input.GetKey(KeyCode.F))
{
    i_degBarrel -= 4;
}

if (Input.GetKeyDown(KeyCode.S))   // 移動/停止
{
    i_MOVE = !i_MOVE;
}

// localBarrel
THMatrix3x3 rotBarrel = TH2DMath.GetRotation3x3(i_degBarrel);
THMatrix3x3 localBarrel = rotBarrel;

// localBody
THMatrix3x3 rotBody = TH2DMath.GetRotation3x3(i_degBody);
Vector2 direBody = rotBody * new Vector3(0, 1, 1);

Vector2 curPos = Body.GetPosition();
Vector2 newPos = (i_MOVE) ? curPos + 0.05f * direBody : curPos; 
THMatrix3x3 traBody = TH2DMath.GetTranslation3x3(newPos);
THMatrix3x3 localBody = traBody * rotBody;

// world matrix
THMatrix3x3 worldBarrel = localBody * localBarrel;
THMatrix3x3 worldBody   = localBody;

Barrel.SetMatrix(worldBarrel);
Body.SetMatrix(worldBody);

図17は、このプログラムの実行結果(図16)の最初のいくつかのフレームにおいて、オブジェクトに実行される変換をアニメーションとして表したものである。

図17
図18

白い文字で「localBarrel」と表示されている状態は変換行列localBarrelが実行されている過程であり、白い文字で「localBody」と表示されている状態は変換行列localBodyが実行されている過程である。38行目、39行目からわかるように localBarrel は Barrelのみに実行され、localBody は Barrelと Bodyの両者に実行される。図17のアニメーションでは「localBarrel」と表示されている状態では Barrelのみが動いており、「localBody」が表示されている状態ではBarrel、Bodyの両方が動いているが、これは38行目、39行目に記述されていることが反映されているのである。
また、「localBody」の表示中その下部に「traBody * rotBody」という文字も表示されているが、これは変換行列localBodyは、回転行列rotBodyと平行移動行列traBodyの2つの変換をまとめたものであることを意味している。「rotBody」が青色で表示されているときは回転行列rotBodyが実行されていることを意味し、「traBody」が青色で表示されているときは平行移動行列traBodyが実行されていることを意味している。
図18は実行結果(図16)の最初の20フレーム程をコマ送りで表示したものである。












© 2020-2024 Redpoll's 60 (All rights reserved)