Unity で目的地に距離の割合で近づく処理の可変フレームレート対応ができた。
これはマウスポインターの位置を球が追従するプログラム。
「1.」は
自身の位置 += 目的地へのベクトル * 割合
を毎フレーム実行している。割合には 0.1 を入れている。毎フレーム、目的地への距離の10%ずつ近づくことになる。遠ければ速く、近ければ遅く近づく。気持ちの良い動き。画像は左からFPS60、30、10 で動かしたときの比較になっている。FPSが下がるほど、動きが遅くなっている。
「2.」は「1.」を可変フレームレート対応したもの。式は
自身の位置 += 目的地へのベクトル * (1.0 - ((1.0 - 割合) ^ (Time.deltaTime * 60)))
FPSに関係なく 1/60 秒ごとに目的地への距離の10%ずつ近づく意味になる。
「1.」の処理に合わせるために1/60秒ごとの割合で計算しているが、その用途じゃなければ、秒ごとの割合にした方が良い。次の式になる。
自身の位置 += 目的地へのベクトル * (1.0 - (1.0 - 割合) ^ Time.deltaTime))
割合が 0.1 なら 1秒 ごとに 10% ずつ近づく。
ところでこの「距離の割合」。変数名としてどう表現すべきなのか悩む。変数名でわからせようとするとなると「秒速目的地への距離の割合」で、 speedTargetDistanceRatioPerSec
的な感じだろうか?
素直に speed
にしてコメントを振るか。speed
だと秒速かフレーム速のイメージが強いんだよな。。。
以下は球の動きに使った実際のコード。
using UnityEngine; public class Chaser : MonoBehaviour { // 追跡対象オフセット. public Vector3 chaseTargetOffset = new Vector3(0f, 1f, 0f); // 1フレームごとの距離を詰める割合. public float distanceRatio = 0.1f; public int calcType = 1; void Update() { Vector3 targetPos; // マウスとXY平面の交差点を目的地にする. { var pointerScreenPos = Input.mousePosition; var pointerWldPos = Camera.main.ScreenToWorldPoint(pointerScreenPos); var plane = new Plane(Vector3.back, Vector3.zero); var pointerRay = Camera.main.ScreenPointToRay(pointerScreenPos); plane.Raycast( pointerRay, out var distance ); targetPos = pointerRay.GetPoint( distance ); } // posA を posB に毎フレーム ratio の割合で近づける. var posA = transform.position; var posB = targetPos + chaseTargetOffset; var toB = posB - posA; // Bへ向かうベクトル. switch (calcType) { case 1: // フレームレートの変動により時間内の移動量に差が出てしまう. // 60FPS なら 1 / 60 秒ごとに 0.1f 近づく. // 30FPS なら 1 / 30 秒ごとに 0.1f 近づく. posA += toB * distanceRatio; break; case 2: // case 1 のフレームレート依存を解消. // 1 / 60 秒ごとに 0.1f 近づくことになる. // 「* 60f」は case 1 と単位を合わせるためにつけてるもの. // 取り除いて、秒ごとの割合にした方が使い勝手が良い. posA += toB * (1.0f - Mathf.Pow(1.0f - distanceRatio, Time.deltaTime * 60f)); break; } transform.position = posA; } }