301.Transformコンポーネント

コンポーネント概要

 コンポーネントとはGameObject追加することでその機能を有効にするオブジェクトです。
 コンポーネントには、大きく分けて更新系コンポーネント描画系コンポーネントがあります。
 更新系コンポーネントは動きや回転、スケーリングなどの動的な変化を制御します。描画系コンポーネント形状の指定色や明るさの設定そして描画方法の指定を行います。シェーダとの橋渡しの意味合いもあります。
 コンポーネントを実装するにはGameObjectOnCreate()関数などで、
        auto Ptr = AddComponent<コンポーネント型>(引数);
 のように記述します。引数はないものが多いです(全部ではない)。AddComponent()関数テンプレート関数です。を渡すことでその型のインスタンスを作成し、GameObjectに結び付けます。
 戻り値は、作成したコンポーネントのshared_ptrです。そのポインタを利用して、コンポーネントのパラメータの設定などを行います。
        auto Ptr = AddComponent<コンポーネント型>(引数);
        Ptr->Function();
 上記Function()はそのコンポーネントが持っている関数の例です。多くはアクセサや振る舞いの設定などです。
 AddComponent()関数はすでにそのコンポーネントが設定されていたらそのポインタを返します。なかった場合だけ新規に作成します。ですから、取得後も常にAddComponent()関数でコンポーネントの取得も行えますが、関数名的にも的確ではありません。ですのですでに作成したコンポーネントを利用する場合は、多くはOnUpdate()などでの処理になりますが
        auto Ptr = GetComponent<コンポーネント型>(なかった時に例外を送出するかどうか);
        Ptr->Function();
 のようにGetComponent()関数を使用します。
 引数はtrueかfalseを指定します。デフォルト引数になっていまして、デフォルト値はtrue(送出する)です。
 falseを渡した場合nullptrが返ってくる可能性があります。ですから、そのコンポーネントが間違いなく存在する場合
        auto Ptr = GetComponent<コンポーネント型>();
        Ptr->Function();
 のような記述をします。しかし、存在したら何かの処理をする場合
        auto Ptr = GetComponent<コンポーネント型>(false);
        if(Ptr){
            //存在した場合の処理
            Ptr->Function();
        }
        else{
            //存在しなかったときの処理
        }
 のように記述します。
 更新系コンポーネントが評価される(計算される)タイミングはOnUpdate()関数が呼ばれた後で、かつ、OnLastUpdate()関数が呼ばれる前です。
 この項では更新系コンポーネントのなかでも、特殊なTransformコンポーネントについて述べます。

Transformコンポーネント

 Transformコンポーネントは一番大事で、そして特殊なコンポーネントです。Transformだけは、すべてのGamaObjectがデフォルトで保持しています。
 Transformスケーリング、重心、回転、移動の4つのパラメータを持ち、1ターン前スケーリング、重心、回転、移動を持ちます。
 デフォルトで保持しているので、AddComponent()関数を実行しなくても取得できます。例えばOnCreate()関数
        auto PtrTrans = GetComponent<Transform>();
        PtrTrans->SetPosition(Vec3(0.0f,0.0f,0.0f));
 のように位置の設定を行います。

 次項以降で説明するAction系コンポーネントRigidbodyコンポーネント、そしてCollision系コンポーネントなどはこのTransformの値を変更します。
 ですから直接Transformを変更するのかもしくは更新系コンポーネントに任せるのかあるいは任せつつも直接変更もするのかは、オブジェクトの作成方法にもよりますが、慎重に設計する必要があります。
 前述しましたように、GameObjectの更新処理(仮想関数が呼び出されるタイミング)と、更新系コンポーネントが計算されるタイミングは以下のような順番になります。
【1】各GameObjectの、OnUpdate()関数呼び出し
【2】各GameObjectが持つコンポーネントの計算
【3】衝突判定と衝突メッセージの発行
【4】各GameObjectの、OnUpdate2()関数呼び出し
【5】ステージのビューの計算
 また【2】各GameObjectが持つコンポーネントの計算は、特殊なコンポーネントとそうでないコンポーネントの評価順があります。以下にその順番を述べます。
*各GameObjectが持つコンポーネントの計算内の計算順番
1、特殊なコンポーネント以外のコンポーネントの計算(場合によってはTransformに影響を与える)
2、Gravityコンポーネント(重力コンポーネント)の計算(Transformに影響を与える)
3、Rigidbodyコンポーネント(速度を管理するコンポーネント)の計算(Transformに影響を与える)
4、Transformの計算(何もしない。ワールド行列は描画系コンポーネントが取得する)
 またその後
【3】衝突判定と衝突メッセージの発行
 が行われます。
 このような形になっていますので、【1】各GameObjectの、OnUpdate()関数呼び出しで、Transformの値を変更しても、その後、各コンポーネントが影響を与えますので、あまり意味がありません。(場合によっては変な動きになります)
 ですから、Transformの値を直接変更したいのであればUpdate系コンポーネントは追加しないか、もしくは【4】各GameObjectの、OnUpdate2()関数呼び出しで行ったほうが比較的思い通りに行くことがあります。
 基本的に以下の方針で設計すればよいと思います。
*Transformの値を直接変更したい場合は、できれば、更新系コンポーネントは追加しない。
*RigidbodyやCollisionを利用したい場合は、Rigidbodyの速度や、Gravity行動で現在の速度を変更する
*OnUpdate2()関数ならTransformの値を直接変更しても比較的安全。
 直接Transformの値を変更するサンプルとしてFullTutorial002があります。
 このサンプルではTransform以外の更新系コンポーネントは持ちません。OnUpdate()関数でオブジェクトの位置を変更しています。