本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させながら追従させる方法を記事にします。
今回は2つの異なるトランスフォームを参照して動的に回転半径と回転軸を変動させてみます。
前提条件
前回記事の続きです。
bluebirdofoz.hatenablog.com
上記の記事では回転の半径と回転軸をInspectorで指定していました。
今回は更に別のトランスフォームを参照し、その動きに合わせて動的に回転の半径と回転軸を変動させてみます。
Vector3.Dot
2つのベクトルの内積を算出します。
本記事では2オブジェクト間のベクトルとY軸の平行を検出するために利用します。
docs.unity3d.com
public static float Dot (Vector3 lhs, Vector3 rhs);
引数 | 説明 |
---|---|
lhs | ベクトル1 |
rhs | ベクトル2 |
Mathf.Abs
数値の絶対値を求めます。
本記事では2オブジェクト間のベクトルとY軸の平行を検出するために利用します。
docs.unity3d.com
public static float Abs (float f);
引数 | 説明 |
---|---|
f | 数値 |
サンプルシーン
以下の通り、サンプルスクリプトを修正しました。
開始位置となるスタートオブジェクトと、回転の中心となるターゲットオブジェクトを指定します。
実行中は2つのオブジェクト間の距離を回転半径、オブジェクト間のベクトルのY軸方向の直交ベクトルを回転軸とします。
・RotateAroundTargetFromStart.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotateAroundTargetFromStart : MonoBehaviour { [SerializeField, Tooltip("開始位置オブジェクト")] private GameObject StartObject; [SerializeField, Tooltip("ターゲットオブジェクト")] private GameObject TargetObject; [SerializeField, Tooltip("速度係数")] private float SpeedFactor = 0.1f; [Tooltip("回転軸")] private Vector3 RotateAxis = Vector3.up; [Tooltip("半径距離")] private float RadiusDistance = 1.0f; private void Start() { // 各オブジェクト位置を取得する Vector3 startPosition = StartObject.transform.position; Vector3 targetPosition = TargetObject.transform.position; // 初期位置はスタートオブジェクトと同じ位置に設定する this.transform.position = startPosition; // 2オブジェクト間の距離を半径距離とする RadiusDistance = CalcRadiusDistance(startPosition, targetPosition); // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする RotateAxis = CalcRotateAxis(startPosition, targetPosition); } // Update is called once per frame void Update() { if (StartObject == null || TargetObject == null) return; // 各オブジェクト位置を取得する Vector3 startPosition = StartObject.transform.position; Vector3 targetPosition = TargetObject.transform.position; Vector3 selfPosition = this.transform.position; // ターゲットオブジェクトの軸方向の移動量はそのまま追従する Vector3 diffVector = selfPosition - targetPosition; float diffMagnitude = diffVector.magnitude; float dot = Vector3.Dot(diffVector, RotateAxis); // 回転軸との内積から回転軸方向への移動量を求める // 離れすぎるとこの演算でエラーが出るので要改良 selfPosition -= RotateAxis.normalized * (diffMagnitude * dot); // 2オブジェクト間の距離を半径距離とする RadiusDistance = CalcRadiusDistance(startPosition, targetPosition); // 現在の距離と半径距離の差分を取得する float diffDistance = Vector3.Distance(selfPosition, targetPosition) - RadiusDistance; // 指定半径の距離だけ近づく(or離れる) this.transform.position = Vector3.MoveTowards(selfPosition, targetPosition, diffDistance); // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする RotateAxis = CalcRotateAxis(startPosition, targetPosition); // 指定オブジェクトを中心に回転する this.transform.RotateAround( targetPosition, RotateAxis, 360.0f / (1.0f / SpeedFactor) * Time.deltaTime ); } /// <summary> /// 回転半径を計算する /// </summary> /// <param name="a_StartPos">開始オブジェクト位置</param> /// <param name="a_TargetPos">ターゲットオブジェクト位置</param> /// <returns>回転半径</returns> private float CalcRadiusDistance(Vector3 a_StartPos, Vector3 a_TargetPos) { // 2点間の距離を回転半径とする return Vector3.Distance(a_StartPos, a_TargetPos); } /// <summary> /// 回転軸ベクトルを計算する /// </summary> /// <param name="a_StartPos">開始オブジェクト位置</param> /// <param name="a_TargetPos">ターゲットオブジェクト位置</param> /// <returns>回転軸ベクトル</returns> private Vector3 CalcRotateAxis(Vector3 a_StartPos, Vector3 a_TargetPos) { // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする Vector3 diffVector = (a_StartPos - a_TargetPos).normalized; // 2点間のベクトルとY軸が平行な場合は回転軸をZ軸で指定する if (Mathf.Abs(Vector3.Dot(diffVector, Vector3.up)) == 1.0f) { return Vector3.forward; } // 側面方向のベクトルを取得するため、2点間のベクトルとY軸との直交ベクトルを求める Vector3 sideVector = Vector3.Cross(diffVector, Vector3.up); // 2点間のベクトルと側面方向のベクトルの直交ベクトルを回転軸とする return Vector3.Cross(diffVector, sideVector); } }
シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
追従させるターゲットとして Cube オブジェクトを配置し手指定します。
開始位置のターゲットには[MainCamera]を指定します。