回転を扱いたい場合はQuaternion.Euler()を使うことが多いが、Matrixを使用すると便利なことがあったのでメモ。 図の場合でQuaternion.Euler()を使用した場合とMatrixを使用した場合を考えてみる。
赤いSphereが原点で、(1, 0, 0)にQuadを配置。
Quaternion.Euler()
下のコードのようにQuaternion.Euler()を使用して回転させると、Quadのポジションは(1, 0, 0)のままでその場で回転をすることになる。
/// <summary>
/// Quaternionを使用して回転
/// </summary>
void RotateWithQuaternion()
{
var a = _tf.rotation.eulerAngles;
// deltaTimeをy軸周りの回転に変換
var newA = new Vector3(a.x, a.y + Time.deltaTime * 100, a.z);
var newR = Quaternion.Euler(newA);
_tf.rotation = newR;
}
では、原点を中心に回転させたい場合はどうするかってなると、Quaternion.Euler()ではちょっと難しそう。そこでMatrixを使用する。
Matrix4x4
とりあえずコードから。これでQuadが原点からの距離を一定に保ったまま、原点を中心にぐるぐる回ることになる。
/// <summary>
/// Matrixを使用して回転
/// </summary>
void RotateWithMatrix()
{
// 回転行列を作成
// deltaTimeをy軸周りの回転に変換。変化量を渡す
var angle = new Vector3(0f, Time.deltaTime * 100, 0f);
var rotate = Quaternion.Euler(angle);
var matrix = new Matrix4x4();
matrix.SetTRS(Vector3.zero, rotate, Vector3.one);
// 現在のモデル変換行列に変換行列をかけることによって回転させる
var newM = matrix * _tf.localToWorldMatrix;
// 移動成分をとりだす
var pos = new Vector3(newM.m03, newM.m13, newM.m23);
_tf.position = pos;
// 回転成分をとりだす
var rot = newM.rotation;
_tf.rotation = rot;
// 拡大縮小成分を取り出す
var scale = newM.lossyScale;
_tf.localScale = scale;
}
Quaternion.Eulerと違う点として、求める角度をそのまま渡すのではない点がある。var angle = new Vector3(0f, Time.deltaTime * 100, 0f);
の部分では変化量を渡している。おそらくlocalToWorldMatrixが毎フレーム更新されるので、前回フレームまでの変更を反映したlocalToWorldMatrixに今回の変化量だけを反映すれば良いってことだと思われる。
Matrixを使いまわせる
ってことで、複数オブジェクトに同じ変更を反映させたい場合は同じMatrixを使い回すことができる。 次の例では回転用のMatrixをひとつ作成して、全てのquadを同じように回転させている。
using System.Collections.Generic;
using UnityEngine;
public class TestMatrix : MonoBehaviour
{
[SerializeField] private GameObject quad;
private List<GameObject> quads;
void Start()
{
quads = new List<GameObject>();
for (int i = 0; i < 36; i++) {
var quad = Instantiate(this.quad, transform);
var x = Mathf.Cos(i * 10 * Mathf.Deg2Rad);
var z = Mathf.Sin(i * 10 * Mathf.Deg2Rad);
quad.transform.position = new Vector3(x, 0, z);
var angle = new Vector3(0, -i * 10f, 0);
quad.transform.rotation = Quaternion.Euler(angle);
quads.Add(quad);
}
Destroy(this.quad);
}
private void Update()
{
var matrix = GetMatrix();
foreach (var quad in quads) {
RotateWithMatrix(matrix, quad.transform);
}
}
/// <summary>
/// 回転させる為のMatrix
/// </summary>
/// <returns></returns>
private Matrix4x4 GetMatrix()
{
// 回転行列を作成
// deltaTimeをy軸周りの回転に変換。変化量を渡す
var angle = new Vector3(0f, Time.deltaTime * 100, 0f);
var rotate = Quaternion.Euler(angle);
var matrix = new Matrix4x4();
matrix.SetTRS(Vector3.zero, rotate, Vector3.one);
return matrix;
}
/// <summary>
/// Matrixを使用して回転
/// </summary>
void RotateWithMatrix(Matrix4x4 matrix, Transform tf)
{
// 現在のモデル変換行列に変換行列をかけることによって回転させる
var newM = matrix * tf.localToWorldMatrix;
// 移動成分をとりだす
var pos = new Vector3(newM.m03, newM.m13, newM.m23);
tf.position = pos;
// 回転成分をとりだす
var rot = newM.rotation;
tf.rotation = rot;
// 拡大縮小成分を取り出す
var scale = newM.lossyScale;
tf.localScale = scale;
}
}