本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させながら追従させる方法を記事にします。
前提条件
前回記事の続きです。
bluebirdofoz.hatenablog.com
上記の記事の方法では回転の半径をチェックしていないため、指定のトランスフォームが大きく移動すると回転半径が変わってしまう問題が発生します。
Vector3.Distance と Vector3.MoveTowards を利用することで半径を一定に保ちながら、指定のトランスフォームを追従することができます。
Vector3.Distance
Vector3.Distance は2点間の距離を返します。
docs.unity3d.com
public static float Distance (Vector3 a, Vector3 b);
引数 | 説明 |
---|---|
a | 座標1 |
b | 座標2 |
Vector3.MoveTowards
Vector3.Distance は移動元の座標から移動先の座標まで指定した距離を移動します。
移動先までの距離が指定した距離よりも短い場合、返却される座標は移動先の座標と等値になります。
docs.unity3d.com
public static Vector3 MoveTowards (Vector3 current, Vector3 target, float maxDistanceDelta);
引数 | 説明 |
---|---|
current | 移動元の座標 |
target | 移動先の座標 |
maxDistanceDelta | 移動距離 |
Vector3.Cross
2つのベクトルと直交するベクトルを算出します。
本記事では回転軸に直交するベクトルを算出するために利用します。
docs.unity3d.com
public static Vector3 Cross (Vector3 lhs, Vector3 rhs);
引数 | 説明 |
---|---|
lhs | ベクトル1 |
rhs | ベクトル2 |
サンプルシーン
以下の通り、サンプルスクリプトを修正しました。
・RotateAroundTarget.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RotateAroundTarget : MonoBehaviour { [SerializeField, Tooltip("ターゲットオブジェクト")] private GameObject TargetObject; [SerializeField, Tooltip("回転軸")] private Vector3 RotateAxis = Vector3.up; [SerializeField, Tooltip("速度係数")] private float SpeedFactor = 0.1f; [SerializeField, Tooltip("半径距離")] private float RadiusDistance = 1.0f; // Update is called once per frame void Update() { if (TargetObject == null) return; // 指定オブジェクトと自身の現在位置を取得する Vector3 selfPosition = this.transform.position; Vector3 targetPosition = TargetObject.transform.position; // 座標が重なっていた場合は回転軸の直交方向に離れた場所を初期位置とする if (selfPosition.Equals(targetPosition)) { // 直交ベクトルを求めるため、回転軸に平行でないダミーベクトルを作成する Vector3 rotateAxisNormal = RotateAxis.normalized; Vector3 dummyDirectVector = Vector3.forward; if (Mathf.Abs(rotateAxisNormal.y) < 0.5f) dummyDirectVector = Vector3.up; // 回転軸とダミーベクトルから直交ベクトルを算出し、初期位置を設定する Vector3 directVector = Vector3.Cross(RotateAxis, dummyDirectVector).normalized; selfPosition = directVector * RadiusDistance; } // 軸方向の移動量は追従する Vector3 diffVector = selfPosition - targetPosition; float diffMagnitude = diffVector.magnitude; float dot = Vector3.Dot(diffVector, RotateAxis); // 回転軸との内積から回転軸方向への移動量を求める selfPosition -= RotateAxis.normalized * (diffMagnitude * dot); // 現在の距離と半径距離の差分を取得する float diffDistance = Vector3.Distance(selfPosition, targetPosition) - RadiusDistance; // 指定半径の距離になるよう近づく(or離れる) this.transform.position = Vector3.MoveTowards(selfPosition, targetPosition, diffDistance); // 指定オブジェクトを中心に回転する this.transform.RotateAround( targetPosition, RotateAxis, 360.0f / (1.0f / SpeedFactor) * Time.deltaTime ); } }
シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
今回はプレイヤーに追従させるため、[TargetObject]に[MainCamera]を指定します。