本日は Unity の小ネタ枠です。
カメラを現在の位置から特定のポイントまでスムーズに移動させる方法を記事にします。
位置と回転をスムーズに変化させる
今回は位置と回転をスムーズに変化させるため、UniRx と Lerp 関数、Ease-in と Ease-Out の計算式を利用します。
Lerp 関数と UniRx の利用手順は以下を参照ください。
docs.unity3d.com
bluebirdofoz.hatenablog.com
Ease-in と Ease-Out の計算式は徐々に加速して徐々に減速する動きを再現するために Lerp 関数と合わせて利用します。
以下の記事を参考にしました。
appleorbit.hatenablog.com
サンプルスクリプト
以下のサンプルスクリプトを作成しました。
ExecuteMove 関数を実行すると、メインカメラを指定のトランスフォームまでスムーズに移動させます。
・CameraMoveSmooth.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using UniRx; public class CameraMoveSmooth : MonoBehaviour { /// <summary> /// カメラの移動先 /// (ContextMenuItemによりInspectorからExecuteMoveを実行できる) /// </summary> [SerializeField, ContextMenuItem("Move", "ExecuteMove")] private Transform p_TargetTransform; /// <summary> /// カメラの移動開始地点 /// </summary> private Vector3 p_StartPosition; private Quaternion p_StartRotation; /// <summary> /// 継続処理の参照 /// </summary> private IDisposable p_Trigger; [SerializeField] private float p_LerpTime = 3.0f; public void ExecuteMove() { // 前回トリガーを終了する p_Trigger?.Dispose(); // カメラの移動開始地点を保存する p_StartPosition = Camera.main.transform.position; p_StartRotation = Camera.main.transform.rotation; // 移動処理を開始する p_Trigger = Observable .IntervalFrame(1, FrameCountType.FixedUpdate) // 1フレーム毎に呼び出す .TimeInterval() // フレーム間の経過時間を取得する .Select(timeInterval => timeInterval.Interval) // TimeSpan型のデータを抽出する .Scan((last, current) => last + current) // 前回までの経過時間を加算する .TakeWhile(intervalTimeSpan => (float)intervalTimeSpan.TotalSeconds < p_LerpTime) // Lerp時間を超えるまで実行する .SubscribeOnMainThread() // メインスレッドで実行する .Subscribe( intervalTimeSpan => { float totalInterval = (float)intervalTimeSpan.TotalSeconds; // Ease-in, Ease-Out の計算式で徐々に加速して徐々に減速する補間を行う float t = Mathf.Min(totalInterval / p_LerpTime, 1.0f); float lerpFactor = (t * t) * (3.0f - (2.0f * t)); // Leap関数を使って徐々にターゲットに近づいていく Camera.main.transform.position = Vector3.Lerp(p_StartPosition, p_TargetTransform.position, lerpFactor); Camera.main.transform.rotation = Quaternion.Lerp(p_StartRotation, p_TargetTransform.rotation, lerpFactor); }, () => { // 最終的に指定のトランスフォームに到達させる Camera.main.transform.position = p_TargetTransform.position; Camera.main.transform.rotation = p_TargetTransform.rotation; } ) .AddTo(this); } }