Redpoll's 60
第5章 変換のオブジェクト化

$§$5-8 いくつかの使用例


5-5節から5-7節にかけては独自のTransformクラスであるTHTransformクラスの実装について見てきたが、本節ではTHTransformクラスを実際に使用したプログラムを作成する。プログラムの作成といっても、ここで行うのは単に今までに作成したプログラムの書き換えである。
5-4節では行列によって実装されていたプログラムを、Unityの標準的な変換方法によるプログラムに書き換えた。
Unityの標準的な変換方法によるプログラムと、ここで作成するTHTransformクラスを使ったプログラムの間における違いはほとんどない。Unity標準の方では各オブジェクトは transform というプロパティを使用しており、その transform を経由して localPositionlocalRotationlocalRotation の更新を行っている。THTransformクラスの方では各オブジェクトは thTransform というプロパティを使用しており、その thTransform を経由して thLocalPositionthLocalRotationthLocalRotation の更新を行っている。
簡単に言えば、THTransformクラスを使用したプログラムでは各オブジェクトが使用するプロパティ名に "th" が接頭辞として付けられている。また、Unity標準の方では回転を表す形式としてQuaternionを使用しているのに対し、THTransformクラスの方では回転を表す形式として行列を使用している。

Unityの標準的な変換方法
Obj.transform.localPosition = ...;
Obj.transform.localRotation = .. Quaternionによる回転 ..;
Obj.transform.localScale    = ...;

THTransformクラスを使用した場合
Obj.thTransform.thLocalPosition = ...;
Obj.thTransform.thLocalRotation = .. Matrix4x4による回転 ..;
Obj.thTransform.thLocalScale    = ...;

そして、肝心な点は THTransformクラスの場合、毎フレームの最後に一番上の親オブジェクトが UpdateChain() を実行しなければならない。この記述がなければ何も更新は行われなくなってしまう。




以降のプログラムで使われるオブジェクトはすべてカスタムライブラリーの THBetaObject3D というクラスのインスタンスである。今までのプログラムで使われてきた3Dオブジェクトは全てカスタムライブラリーのTHObject3Dクラスのインスタンスであったが、THBetaObject3DクラスはTHObject3Dクラスのサブクラスであり、thTransform というプロパティを持っている。このプロパティはTHTransform型のプロパティであり、本節のプログラムではこのプロパティを経由してオブジェクトに対し変換を実行する。


# Code1
以下のプログラムは 5-4節 Code1である。
[5-4節 Code1]
i_degRot += 4.0f;
Quaternion rotY = Quaternion.AngleAxis(i_degRot, Vector3.up);
Quaternion rotZ = Quaternion.AngleAxis(-20.0f, Vector3.forward);
Quaternion rot = rotZ * rotY;

i_degRev += 1.0f;
Matrix4x4 rev = TH3DMath.GetRotation4x4(i_degRev, Vector3.up);
Vector3 np = rev * new Vector4(3.0f, 0.0f, 0.0f, 1.0f);
Vector3 pos = np + c_centerPos;

Sphere.transform.localPosition = pos;
Sphere.transform.localRotation = rot;

このプログラムは、4-1節 Code5をUnityの標準的な変換方法によって書き換えたものであった。
以下のプログラムは、上記を THTransformクラスを使用して書き換えたものである。
[Code1]  (実行結果 図1)
i_degRot += 4.0f;
Matrix4x4 rotY = TH3DMath.GetRotation4x4(i_degRot, Vector3.up);
Matrix4x4 rotZ = TH3DMath.GetRotation4x4(-20.0f, Vector3.forward);
Matrix4x4 rot = rotZ * rotY;

i_degRev += 1.0f;
Matrix4x4 rev = TH3DMath.GetRotation4x4(i_degRev, Vector3.up);
Vector3 np = rev * new Vector4(3.0f, 0.0f, 0.0f, 1.0f);
Vector3 pos = np + c_centerPos;

Sphere.thTransform.thLocalPosition = pos;
Sphere.thTransform.thLocalRotation = rot;

// ** UpdateChain()を実行しなければ、何も更新は行われない ** 
Sphere.thTransform.UpdateChain();



# Code2
以下のプログラムは 5-4節 Code2である。
[5-4節 Code2]
if (!i_INITIALIZED)
{
    i_INITIALIZED = true;

    // 親子関係の設定
    Propeller.transform.SetParent(Body.transform);      
}

// Propeller
i_degPropeller += 15;
Quaternion rotProp = Quaternion.AngleAxis(i_degPropeller, Vector3.forward);
Propeller.transform.localPosition = c_attachPos_Propeller;
Propeller.transform.localRotation = rotProp;

// Body
i_degBody -= 2;
Quaternion rotBody = Quaternion.AngleAxis(i_degBody, Vector3.up);
Vector3 posBody = rotBody * new Vector4(8.0f, 10.0f, 0.0f, 1.0f);
Body.transform.localPosition = posBody;
Body.transform.localRotation = rotBody;

このプログラムは、4-2節 Code2をUnityの標準的な変換方法によって書き換えたものであった。
THTransformクラスを使用して書き換えたプログラムを以下に示す。
[Code2]  (実行結果 図2)
if (!i_INITIALIZED)
{
    // 親子関係の設定
    Propeller.thTransform.SetParent(Body.thTransform);

    i_INITIALIZED = true;
}

// Propeller
i_degPropeller += 15;
Propeller.thTransform.thLocalPosition = c_attachPos_Propeller;
Propeller.thTransform.thLocalRotation = TH3DMath.GetRotation4x4(i_degPropeller, Vector3.forward);

// Body
i_degBody -= 2;
Matrix4x4 rotBody = TH3DMath.GetRotation4x4(i_degBody, Vector3.up);
Vector3 posBody = rotBody * new Vector4(8.0f, 10.0f, 0.0f, 1.0f);
Body.thTransform.thLocalPosition = posBody;
Body.thTransform.thLocalRotation = rotBody;

// ** UpdateChain()を実行しなければ、何も更新は行われない ** 
Body.thTransform.UpdateChain();


# Code3
以下のプログラムは 5-4節 Code3である。
[5-4節 Code3]
if (!i_INITIALIZED)
{
    // 親子関係の設定
    Sphere.transform.SetParent(Arm.transform);
    Arm.transform.SetParent(Disk.transform);

    i_INITIALIZED = true;
}

i_shmSphere += 1.0f;
float scale = 0.5f * Mathf.Sin(i_shmSphere * Mathf.Deg2Rad) + 1.5f; // 1から2の間を往復
Sphere.transform.localScale = new Vector3(scale, scale, scale);
Sphere.transform.localPosition = c_attachPos_Sphere;


i_degArm--;
Arm.transform.localRotation = Quaternion.AngleAxis(i_degArm, Vector3.up);
Arm.transform.localPosition = c_attachPos_Arm;

i_shmDisk += 1.0f;
float posY = 4.0f * Mathf.Sin(i_shmDisk * Mathf.Deg2Rad) + 8.0f; // 4から12の間を往復
Disk.transform.localRotation = Quaternion.AngleAxis(-15.0f, Vector3.right);
Disk.transform.localPosition = new Vector3(0.0f, posY, 0.0f);


このプログラムは、4-10節 Code5をUnityの標準的な変換方法によって書き換えたものであった。
THTransformクラスを使用して書き換えたプログラムを以下に示す。
[Code3]  (実行結果 図3)
if (!i_INITIALIZED)
{
    // 親子関係の設定
    Sphere.thTransform.SetParent(Arm.thTransform);
    Arm.thTransform.SetParent(Disk.thTransform);

    i_INITIALIZED = true;
}

i_shmSphere += 1.0f;
float scale = 0.5f * Mathf.Sin(i_shmSphere * Mathf.Deg2Rad) + 1.5f; // 1から2の間を往復
Sphere.thTransform.thLocalScale = new Vector3(scale, scale, scale);
Sphere.thTransform.thLocalPosition = c_attachPos_Sphere;

i_degArm--;
Arm.thTransform.thLocalRotation = TH3DMath.GetRotation4x4(i_degArm, Vector3.up);
Arm.thTransform.thLocalPosition = c_attachPos_Arm;

i_shmDisk += 1.0f;
float posY = 4.0f * Mathf.Sin(i_shmDisk * Mathf.Deg2Rad) + 8.0f; // 4から12の間を往復
Disk.thTransform.thLocalRotation = TH3DMath.GetRotation4x4(-15.0f, Vector3.right);
Disk.thTransform.thLocalPosition = new Vector3(0.0f, posY, 0.0f);

// ** UpdateChain()を実行しなければ、何も更新は行われない ** 
Disk.thTransform.UpdateChain();


Unity標準のプログラムとTHTransformクラスを使用したプログラムとの間には、ほとんど違いがないことがわかるだろう。しかし、冒頭でも述べたがTHTransformクラスを使用する場合は、毎フレームの最後に一番上の親オブジェクトが UpdateChain() を実行する必要がある。

各プログラムの実行結果を以下に示す。

  • 図1 Code1 実行結果
  • 図2 Code2 実行結果
  • 図3 Code3 実行結果


# Code4
以下のプログラムは 4-15節 Code7である。
[4-15節 Code7]  (実行結果 図4)
if (!i_INITIALIZED)
{
    i_degPrimarySide = 0.0f;
    i_degSide = 0.0f;
    i_degTop = 0.0f;
    i_motionCounter = 0;

    i_INITIALIZED = true;
}


if (Input.GetKey(KeyCode.J))
{
    i_motionCounter++;
    if (i_motionCounter > 240) { i_motionCounter = 240; }
}
else if (Input.GetKey(KeyCode.K))
{
    i_motionCounter--;
    if (i_motionCounter < 0) { i_motionCounter = 0; }
}


if (0 <= i_motionCounter && i_motionCounter <= 60)
{
    float t = i_motionCounter / 60.0f;
    i_degPrimarySide = -90.0f * t;
}
if (60 <= i_motionCounter && i_motionCounter <= 180)
{
    float t = (i_motionCounter - 60) / 120.0f;
    i_degSide = 90.0f * t;
}
if (180 <= i_motionCounter && i_motionCounter <= 240)
{
    float t = (i_motionCounter - 180) / 60.0f;
    i_degTop = -90.0f * t;
}

Matrix4x4[] localFace = new Matrix4x4[6];
localFace[5] = TH3DMath.GetTranslation4x4(0.0f, 0.0f, 1.0f) *   
                TH3DMath.GetRotation4x4(i_degTop, Vector3.right);

Matrix4x4 rotSide = TH3DMath.GetRotation4x4(i_degSide, Vector3.forward);
Matrix4x4 traSide = TH3DMath.GetTranslation4x4(1.0f, 0.0f, 0.0f);
localFace[4] = traSide * rotSide;
localFace[3] = traSide * rotSide;
localFace[2] = traSide * rotSide;
localFace[1] = TH3DMath.GetRotation4x4(i_degPrimarySide, Vector3.right);

localFace[0] = Matrix4x4.identity;     

for (int i = 5; i >= 0; i--)
{
    Matrix4x4 worldM = localFace[0];
    for (int j = 1; j <= i; j++)
    {
        worldM *= localFace[j];
    }

    Face[i].SetMatrix(worldM);
}

このプログラムは立方体の展開された状態、及び組み立てられた状態の間をキー操作によって変化させられるようにするものであった。下図4はここで使われているオブジェクト Face0 ~ Face5 の階層構造であり、図5はこのプログラムの実行結果である。

図4 オブジェクトの階層構造
図5 Code4 実行結果

以下のプログラムは、上のプログラムをTHTransformクラスを使用して書き換えたものである。
[Code4]  (実行結果 図5)
if (!i_INITIALIZED)
{
    i_degPrimarySide = 0.0f;
    i_degSide = 0.0f;
    i_degTop = 0.0f;
    i_motionCounter = 0;

    // 親子関係の設定
    Face[1].thTransform.SetParent(Face[0].thTransform);
    Face[2].thTransform.SetParent(Face[1].thTransform);
    Face[3].thTransform.SetParent(Face[2].thTransform);
    Face[4].thTransform.SetParent(Face[3].thTransform);
    Face[5].thTransform.SetParent(Face[4].thTransform);

    i_INITIALIZED = true;
}


if (Input.GetKey(KeyCode.J))
{
    i_motionCounter++;
    if (i_motionCounter > 240) { i_motionCounter = 240; }
}
else if (Input.GetKey(KeyCode.K))
{
    i_motionCounter--;
    if (i_motionCounter < 0) { i_motionCounter = 0; }
}

if (0 <= i_motionCounter && i_motionCounter <= 60)
{
    float t = i_motionCounter / 60.0f;
    i_degPrimarySide = -90.0f * t;
}
if (60 <= i_motionCounter && i_motionCounter <= 180)
{
    float t = (i_motionCounter - 60) / 120.0f;
    i_degSide = 90.0f * t;
}
if (180 <= i_motionCounter && i_motionCounter <= 240)
{
    float t = (i_motionCounter - 180) / 60.0f;
    i_degTop = -90.0f * t;
}


Face[5].thTransform.thLocalPosition = new Vector3(0.0f, 0.0f, 1.0f);
Face[5].thTransform.thLocalRotation = TH3DMath.GetRotation4x4(i_degTop, Vector3.right);

for (int i = 2; i <= 4; i++)
{
    Face[i].thTransform.thLocalPosition = new Vector3(1.0f, 0.0f, 0.0f);
    Face[i].thTransform.thLocalRotation = TH3DMath.GetRotation4x4(i_degSide, Vector3.forward);
}

Face[1].thTransform.thLocalRotation = TH3DMath.GetRotation4x4(i_degPrimarySide, Vector3.right);

Face[0].thTransform.UpdateChain();

5-4節でも述べたように、毎フレーム同じ値がセットされるプロパティについては、初期化ブロックに移すことができる。例えば、このプログラムではFace5のアタッチポジションへの移動(47行目)、Face2~Face4のアタッチポジションへの移動(52行目)は毎フレーム同じ処理であるので、これらを初期化ブロックへ移しても実行結果は変わらない。














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