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

本記事の最新版はzennに移行しております。


UnityでGameObject移動させたり、相手を向かせたりする方法についていろいろまとめました。あとめっちゃ簡単にぽよんぽよんさせます。

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()
[memo]
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を使う(推奨)
 衝突判定を移動処理に含め、不用意なめり込み等をへらすことを期待している。ただし、移動床の子になれないなど制限がある。
 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); 
キャラクタ制御についてはまた別途まとめる。

2) Rotation の操作

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

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

(1) transform.rotationに Vector3型(角度)を代入する。

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

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


(2) ターゲットの位置(Vector3型(座標))を向かせる。

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

(3) Rotate を使って度数(0~360°)を指定して回転する。
 (ここでは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度の速度で回転。
('20/5/31 当初Sleepを使っていましたが、RotateTowardsに変更しました)

(5) ゆっくりと進んでいる方向を向かせる。
 ゲームでは、入力に対して急な方向転換をさせることができるけれども、それでは不自然な場合がある。その場合、ゆっくりと方向転換させることで対処できる。

// lookingDirection は向かせる方向。通常は移動方向。
transform.rotation = Quaternion.RotateTowards(transform.rotation,
  Quaternion.LookRotation(lookingDirection),
  120.0f * Time.deltaTime);
(少し解説)
・Quaternion.LookRotation (Vector3 forward, Vector3 upwards= Vector3.up);
 指定された forward と upwards 方向に回転する。
 Z軸を前方に、X軸を前方と上方との交積で揃え、Y軸をZとXとの交積で揃える。ここでは、Y軸にそってのみ回転するというおまじない。

3) Scaleの操作

インスペクターから値を入力するか、スクリプトから操作する。
ここだけscaleではなく、localscaleを使うので注意。
スケールを操作することはあまりないと思うが、Vector3でぷるぷるさせるサンプルを添付する。けっこうつかえる。

// 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本の向き → 角度)
 
首だけを標的を向かせたりとか、標的までの角度でふるまいを変えたいことがあるだろう。角度(X軸とかY軸にはそっていない)を得て、角度差がどれくらいかを判定することができる。

// ベクトルの差分で出力されるわけでないので、
// 返した値を利用して物体を回転させる事は出来ない
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);
[memo]
度 -> Radian の変換は deg * Mathf.Deg2Rad (意味は(PI * 2) / 360と同一)
Radian → 度 の変換は rad * Mathf.Rad2Deg

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

// 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;

この記事が気に入ったらサポートをしてみませんか?