使用するキー操作は以下のとおり。 J, K : Dotの移動 (K によって進み、J によって戻る)
プログラムを以下に示す。 [Code1] (実行結果 図9)
if (!i_INITIALIZED)
{
Vector2[] arrP = GetPositionArray(1);
Vector2[] arrT = GetTangentArray(1);
List<Segment> segments = new List<Segment>();
int numControl = arrP.Length;
for (int i = 0; i < numControl; i++)
{
int i1 = (i + 1) % numControl;
Vector2 P = arrP[i];
Vector2 v = arrT[i];
Vector2 Q = arrP[i1];
Vector2 w = arrT[i1];
Vector3 iR = CalcIntersectionOf2Lines(P, v, Q, w);
InterpolatePQ_Data(P, v, Q, w, iR, segments);
}
Curve = new BasicCurve(segments);
t = 0.0f;
i_INITIALIZED = true;
}
if (Input.GetKey(KeyCode.K))
{
t += 0.002f;
}
else if (Input.GetKey(KeyCode.J))
{
t -= 0.002f;
}
Vector2 pos = Curve.ComputePosition(t);
Dot.SetPosition(pos);
冒頭の GetPositionArray(..)、GetTangentArray(..) は各制御点の位置及び接線の方向を取得するためのメソッドで、戻り値はいずれもVector2[]の配列であり、今回は制御点の数は8個なので戻り値の要素数も8である。 初期化ブロックの内容は上の解説で述べたものであるが、今回使用している補間曲線が閉曲線であるため最初の制御点を最後の制御点としても使っている (10行目の i1 は最後のループにおいてその値が $0$ になる)。 26行目以降は2つのキー J、K によってDotを移動させる処理である。インスタンス変数 t の値は $0$~$1$ に変化するが、この t の値から補間曲線上の位置を計算し(35行目)、その位置へDotを移動させるだけである (実際にはこのプログラムでは t の値は $0$ を下回ったり、$1$ を上回ったりするが、ComputePosition(t) (35行目)の中でその値が自動的に $0$~$1$ の範囲に変換される)。
if (!i_INITIALIZED)
{
Vector2[] arrP = GetPositionArray(2);
Vector2[] arrT = GetTangentArray(2);
List<Segment> segments = new List<Segment>();
int numControl = arrP.Length;
for (int i = 0; i < numControl; i++)
{
int i1 = (i + 1) % numControl;
Vector2 P = arrP[i];
Vector2 v = arrT[i];
Vector2 Q = arrP[i1];
Vector2 w = arrT[i1];
Vector3 iR = CalcIntersectionOf2Lines(P, v, Q, w);
InterpolatePQ_Data(P, v, Q, w, iR, segments);
}
Curve = new BasicCurve(segments);
t = 0.0f;
i_INITIALIZED = true;
}
if (Input.GetKey(KeyCode.K))
{
t += 0.0025f;
}
else if (Input.GetKey(KeyCode.J))
{
t -= 0.0025f;
}
Vector2[] vrr = Curve.ComputePositionAndTangent(t);
Vector2 pos = vrr[0];
Vector2 dir = vrr[1];
THMatrix3x3 T = TH2DMath.GetTranslation3x3(pos);
THMatrix3x3 R = TH2DMath.CalcRotation3x3_V1toV2(Vector2.up, dir);
Vehicle.SetMatrix(T * R);
Code1との違いはVehicleには向きを指定する必要があるため、今回は毎フレーム ComputePositionAndTangent(t) (35行目)によって、t から計算される位置 pos とその位置における接線の方向 dir を取得している (戻り値には最初の要素に位置、次の要素に接線の方向がセットされる)。 Vehicleの進行方向は現在の位置における接線方向であるから、その方向へVehicleを向けるために適当な回転を実行する必要がある。40行目の CalcRotation3x3_V1toV2(..) はカスタムライブラリーのメソッドで、第1引数に指定されたベクトルの向きを第2引数に指定されたベクトルの向きと同じにするための回転を返すものである (第1引数のベクトル、第2引数のベクトルを $\boldsymbol{\mathsf{v_1}}$、$\boldsymbol{\mathsf{v_2}}$ とすれば、このメソッドから取得される回転を $\boldsymbol{\mathsf{v_1}}$ に実行すると、 $\boldsymbol{\mathsf{v_1}}$ の向きは $\boldsymbol{\mathsf{v_2}}$ の向きと同じになる)。 プログラム中の dir は現在位置における接線の方向であるから、Vehicleを dir と同じ向きにすればよいわけである。CalcRotation3x3_V1toV2(..) の引数には Vector2.up と dir が指定されているが、これによって初期状態の向きから dir の方向へ向ける回転(R)が取得される。 したがって、Vehicleに実行される変換行列 T * R (41行目)の内容は、Vehicleを初期状態から dir の方向へ向け、その状態で pos に移動させるという順で行われる。これを毎フレーム繰り返すことによって、Vehicleは進行方向を向いた状態で曲線上(センターライン上)を移動していくことになる (図15)。