[Unity][C# Script] GameObject の transformコンポーネントを操作するアレコレ

9 min read読了の目安(約8500字

UnityでGameObjectを移動させたり、相手を向かせたりするには、transformを操作しますが、わりといろんな方法がありますので、それらについてまとめました。
 あとめっちゃ簡単に3Dのオブジェクトをぽよんぽよんさせます。

1.transformコンポーネント

GameObjectには必ずtransformコンポーネントがついています。そして下の3つのパラメータを持っています。それらの操作方法についてまとめてみました。

・Position Vector3型 GameObjectの座標
・Rotation Quaternion型 GameObjectの向き
・Scale Vector3型 GameObjectの相対的な大きさ(標準 1,1,1)

1) Position の操作
 インスペクターから値を入力するか、スクリプトから以下の方法で操作できます。
 Position は親がいる場合は親との相対座標、そうでない場合はワールド座標となります。

(1) transform.position に Vectore3型を直接代入する
 初期設定などに使います。

transform.position = new Vector3()

Unityでは左手座標系を採用しています。
ここでは、座標は以下のように表現されます。
・ (-)左 右(+) = x
・ (+)上 下(-) = y
・ (+)奥手前(-) = z

(2) transform.Translateをつかう
 弾や自動的に動く地形などに使うのがよさそうです。よくカクカクするのでキャラクタには CharactorController コンポーネントか Rigidbody をつかい、この操作は行わないことにしています。(マイルール)

// movementSpeed は別途定義

//コントローラ入力からの入力 横軸 を取得
float horizontalInput = Input.GetAxis("Horizontal");

//コントローラ入力からの入力 縦軸 を取得
float verticalInput = Input.GetAxis("Vertical");

//コントローラ入力があった場合のみ
if (horizontalInput != 0f || verticalInput != 0f)
{
  // GameObject の方向を移動方向に向ける。
  transform.rotation = Quaternion.LookRotation(new Vector3(horizontalInput, 0, verticalInput));

  // Vector3.forward 方向に勧めると、GameObject の向いている方向に進む。
  transform.Translate(Vector3.forward * movementSpeed * Time.deltaTime);
}

(3) CharactorControllerコンポーネントを使う(推奨)
 CharactorControllerコンポーネントの Moveを使ってみます(推奨)
 衝突判定を移動処理に含め、不用意なめり込み等をへらすことなどが期待できます。ただし、移動床の子になれないなど制限がありますし、isGroundedメソッドはいまいちあてにできません。
 なおCharactorCotroller の各種設定は今回は取り扱いません。

// float stoppingPower、addSpeed と Vector3 moveDirection は別途定義

//コントローラ入力からの入力 横軸 を取得
float horizontalInput = Input.GetAxis("Horizontal");

//コントローラ入力からの入力 縦軸 を取得
float verticalInput = Input.GetAxis("Vertical");

// 入力をカメラ補正して Vector3型に代入
Vector3 inputDirection = cameraForward * inputVertical + Camera.main.transform.right * inputHorizontal;

// 加速・減速操作
moveDirection = moveDirection + (inputDirection * addSpeed * Time.deltaTime);
moveDirection = moveDirection * stoppingPower; //  ここは改良の余地あり

// controller はCharactorControllerコンポーネントとする
controller.Move(moveDirection * Time.deltaTime);

(4) RigidBody コンポーネントを使う(推奨2)
 rigidbody の AddForce ( ForceMode.VelocityChange )を使ってみます。
 相手との物理挙動をそれらしくしたり、移動床などとそれらしく整合性をとるには rigidbody のほうが都合がよい場合があります。
 rigidbody を使うには、Cupsle Collider を組み込んだりする必要がありますが、今回は取り扱いません。

// float stoppingPower、addSpeed と Vector3 moveDirection は別途定義

//コントローラ入力からの入力 横軸 を取得
float horizontalInput = Input.GetAxis("Horizontal");

//コントローラ入力からの入力 縦軸 を取得
float verticalInput = Input.GetAxis("Vertical");

// 入力をカメラ補正して Vector3型に代入
Vector3 inputDirection = cameraForward * inputVertical + Camera.main.transform.right * inputHorizontal;

// 加速・減速操作
moveDirection = moveDirection + (inputDirection * addSpeed * Time.deltaTime);
moveDirection = moveDirection * stoppingPower; //  ここは改良の余地あり

// rbController は Rigitbody コンポーネントとする
rbControlle .Move(moveDirection * Time.deltaTime,  ForceMode.VelocityChange);

なお、プレイヤーキャラクタの制御についてはいったんnoteにまとめました。

https://note.com/k1togami/n/n75b6c1659654

2) Rotation の操作
 インスペクターから値を入力するか、スクリプトから以下の方法で操作します。ちなみに、Vector3型は角度の場合と座標の場合がありますが、ここでは角度です。
 また Rotation は、親がいる場合は親に対しての回転、そうでない場合はワールド座標に対しての回転となります。

Unity の rotationは内部でクォータ二オンを採用していますが、インスペクターでは編集しやすいように同等のオイラー角で表しています。

(1) transform.rotationに Vector3型(角度)を代入する。
 transform.rotationはクォータニオン型なので、Vector型を直接代入はできません。Quaternion.EulerでVector3型にするか、かわりにtransform.eulerAnglesに代入します。

// a)
transform.rotation = Quaternion.Euler( 0f, 0f, 10f); // Z軸に10°回転

// b)
transform.eulerAngles = new Vector3( 0f, 0f, 10f); // 同上

(2) ターゲットの位置(Vector3型(座標))を向かせる。
 ターゲットの方向を向かせるだけならば、LookAtが便利です。

transform.LookAt ( target.transform.position ); // target は GameObject型

(3) Rotate を使って度数(0~360°)を指定して回転する。
 Rotateを使って、何度回転させるか指定します。 
 サンプルは、1秒ごとに120度回転します。

transform.Rotate( 0f, 120.0f * Time.deltaTime ,0f );

(4) ゆっくりとターゲットの位置(Vector3型(座標))を向かせる。
 (2)のバリエーションです。なめらかに回転するので、ホーミングミサイルなどゲームキャラクタの旋回に使うと便利です。

transform.rotation = Quaternion.RotateTowards ( transform.rotation, 
  Quaternion.LookRotation( target.transform.position - transform.position ),
  120.0f * Time.deltaTime );

(少し解説)
・Vector3 direction = target.transform.position - transform.position;
 Vector3型(方向)を算出
・Quaternion rotation = Quaternion.LookRotation( direction );
 Vector3型(方向)を Quaternion型に変換。
・Quaternion. RotateTowards (Quaternion from, Quaternion to, float maxDegreesDelta);
 from から to への回転を得る。
 maxDegreesDeltaの角度ステップ分だけtoに向かって回転する。
 ここでは1秒間に120度の速度で回転。

(5) ゆっくりと進んでいる方向を向かせる。
 ゲームでは、キャラクターが急な方向転換をするものがよくありますが、あまり急に回転すると不自然な場合があります。なめらかに回転するので、プレイヤーキャラクタの旋回に使うと便利です。

// lookingDirection は向かせる方向。通常は移動方向。
transform.rotation = Quaternion.RotateTowards(transform.rotation,
  Quaternion.LookRotation(lookingDirection),
  120.0f * Time.deltaTime);

(少し解説)
public static Quaternion LookRotation (Vector3 forward, Vector3 upwards= Vector3.up);
・forward 向かせたい方向
・upwards 上方向を定義するベクトル。 Vector3.up は Vector3(0, 1, 0) と同意。

3) Scaleの操作
 Scaleはインスペクターから値を入力するか、スクリプトから操作します。
ここだけscaleではなく、localscaleを使うので注意が必要です。
スケールを操作することはあまりなさそうに思えますが、複雑なアニメーションを仕込まなくても、ぷるぷるさせることができるので、ゲームの演出に向いているのではないでしょうか。積極的に使っていきたいと思います。

// StartCoroutine("PuruDo"); で呼びます

// 2秒間だけオブジェクトを ぷるぷる させるコルーチン
IEnumerator PuruDo()
   {
       // 2秒間
       float time = 0f;

       while (time < 2f)
       {
           // ぷるぷるにSinを使います。
           float puruW = (2f - time) * 0.2f * (Mathf.Sin(time * 20f)) + 1f;
           float puruH = (2f - time) * -0.12f * (Mathf.Sin(time * 20f)) + 1f;

           transform.localScale = new Vector3( puruW , puruH , puruW );
           time += Time.deltaTime;
           yield return null;
       }
   }

2.3Dベクトルの操作

以下にtransformを扱うのときに必要そうな、基本的な移動と回転をまとめます。

1) 単純な移動(元座標 → 指定座標)
特定の座標に等速で近づけます。

//maxDistanceDeltaぶんフレーム毎に移動します。
transform.position = Vector3.MoveTowards (Vector3 transform.position, 
                                          Vector3 targetPosition,
                                          float maxDistanceDelta);

2) 標的の方向を向く(現在向き → 向ける方向)
 標的の方向に向けて、等速で回転させるときに使います。
 車両のドリフト状態などは、進む方向と GameObject の向きが異なりますが、これを表現するためには内部的に進行方向を持つ必要があります。そのときに使うとよさそうでしょうか。

// target は標的となる GameObjectです。
// currentLookAt は Vector3 
currentLookAt = Vector3.RotateTowards(currentLookAt,
                                      target.position,
                                      lookAtSmoothing * Time.deltaTime);
// その方向を向かせるときは、以下の行のコメント記号を削除
// transform.LookAt(currentLookAt); 

3) 標的までの角度差を得る(2本の向き → 角度)
 首だけを標的を向かせたりする場合、回転できる角度を制限しないと首だけ真後ろを向いたりしますよね。そんなことを防ぐためには、自分が向いている角度と、相手の方向の角度の差を得る必要があります。とりあえずそれらしい角度を得るには、Vector3.Angleが簡単で便利です。

// ベクトルの差分で出力されるわけでないので、
// 返した値を利用して物体を回転させる事は出来ない
Vector3 targetDir= target.transform.position - transform.position;
float angle = Vector3.Angle(targetDir, transform.forward);

if (angle < 5.0f)
           print("close(近い)");

おそらくこの角度は、targetDirまでの直線と自身の向きという直線の2本で定まる平面上での角度になると思われます。
双方のxz平面が同一であれば、得られる回転はy軸にそったものになるはずです。

4) 標的までの角度差(Y軸に沿った角度差)を得る
 Unityでは角度差をあまり意識せずにプログラムが組めるよう配慮されているのですが、やっぱりちょっと泥臭いことをしなければならないときもありそうです。
 もっといい方法があったら知りたいものです。

// a)
float angleY = Mathf.Atan2( target.transform.position.x
                            - transform.position.x
                            , target.transform.position.z
                            - transform.position.z
                            ) * Mathf.Rad2Deg;

// b)
Vector3 targetDir= target.transform.position - transform.position;
targetDir.y = 0f;
float angleY = Vector3.Angle(targetDir, transform.forward);

>-> Radian の変換は deg * Mathf.Deg2Rad (意味は(PI * 2) / 360と同一)
>Radian → 度 の変換は rad * Mathf.Rad2Deg

**5) 最大速度を制限する**
 移動の速度を増減させるときのうち、特に加速制御するとき、一定の速度内にクリッピングしたいときがあります。そのときは一旦正規化するとよいでしょう。

```cs
// float maxSpeed、vector3 moveDirection は別途定義 

if ( moveDirection.magnitude >= maxSpeed )
{
     moveDirection = moveDirection.normalized * maxSpeed;
}   

3. クォータニオンの操作を少々

ターゲットの向きをクォータニオンで計算してみます。冗長ですよね。
 もっといい方法があったら知りたいです。

// targetの向きをクォータニオンで取得
Quaternion targetDirection = Quaternion.LookRotation(target.transform.position - transform.position); 

// 必要な回転量を得る(Inverse したクォータニオンを乗算)
Quaternion requiredRotation = targetDirection * Quaternion.Inverse(transform.rotation);

// xz平面上に回転を限定
requiredRotation = new Quaternion(0, transform.rotation.y, 0, transform.rotation.w);

// クオータニオンからVectorに変換
float requiredDegree = requiredRotation.eulerAngles.y;

2021年2月19日 noteより転記