終点への放物移動
Unityでの放物移動の記事はRigidbodyを用いたものがほとんどだったので、高校物理の方程式を使ったスクリプトベースの放物移動を紹介します。
始点と終点のy座標のズレにも対応できる放物移動になっています。
動きのベースとなる値は、終点座標と終点座標につくまでの滞空時間です。
高校物理 ~斜方投射~
斜めに投げる斜方投射は、
①水平方向(横)= x,z成分
②鉛直方向(縦)= y成分
の2種類に分けて考えるのでした。
水平方向 x,z成分
指定した滞空時間が経ったらちょうど終点につくように、xとz成分を時間に比例して変化させるだけです。Unityを使っているので、Lerp関数で対処できます。
Lerp関数 リファレンス
https://docs.unity3d.com/ja/2017.4/ScriptReference/Vector3.Lerp.html
鉛直方向 y成分
鉛直方向である、鉛直投げ上げの公式は3つありましたが、ここでは、始点と終点のy成分の差分(変位)を代入できるy、滞空時間を代入できるt が用いられている変位の式を使います。
$$変位\quad\color{blue}{y}=v_0\color{green}{t}+\frac{1}{2}g\color{green}{t^2}\quad ・・・①$$
ここで、 y と t は私たちが決めるものなので、分かっている数値です。残りのy方向の初速度voが分からないので左辺に置くよう変形します。
$$v_0=\frac{\color{blue}{y}-\frac{1}{2}g\color{green}{t^2}}{\color{green}{t}}=\quad ・・・②$$
上の式に、以下のように私たちが指定する要素を代入した初速度をvnとします。
$$v_n=\frac{\color{blue}{始点と終点のy成分の差分}-\frac{1}{2}g\color{green}{滞空時間^2}}{\color{green}{滞空時間}}\quad ・・・③$$
最終的に鉛直方向の初速度vnを初出の①式に代入した④式基づいて、変化する時間tを代入してスクリプトで動かしていきます。
$$変位\quad\color{blue}{y}=\bf{\color{orange}{v_n}}\color{green}{t}+\frac{1}{2}g\color{green}{t^2}\quad ・・・④$$
スクリプト
コールチンを用いて実装しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | using System.Collections; using UnityEngine; public class samp : MonoBehaviour { [SerializeField] Transform endPos; //終点座標 [SerializeField] float flightTime = 2; //滞空時間 [SerializeField] float speedRate = 1; //滞空時間を基準とした移動速度倍率 private const float gravity = -9.8f; //重力 void Start() { StartCoroutine(Jump(endPos.position, flightTime, speedRate, gravity)); } // 現在位置からendPosへの放物運動 private IEnumerator Jump(Vector3 endPos, float flightTime, float speedRate, float gravity) { var startPos = transform.position; // 初期位置 var diffY = (endPos - startPos).y; // 始点と終点のy成分の差分 var vn = (diffY - gravity * 0.5f * flightTime * flightTime) / flightTime; // 鉛直方向の初速度vn // 放物運動 for (var t = 0f; t < flightTime; t += (Time.deltaTime * speedRate)) { var p = Vector3.Lerp(startPos, endPos, t/flightTime); //水平方向の座標を求める (x,z座標) p.y = startPos.y + vn * t + 0.5f * gravity * t * t; // 鉛直方向の座標 y transform.position = p; yield return null; //1フレーム経過 } // 終点座標へ補正 transform.position = endPos; } } |
Jump関数
初期値 21~23行目
初期位置、始点と終点のy成分の差分、前項③式を用いて鉛直方向の初速度vnを求めています。
放物運動による座標更新 26~32行目
時間tの更新 26行目
for文で滞空時間を満たすまで変数tにTime.deltaTimeを足して繰り返していきます。speedRateはオプションで滞空時間を基軸とした動きにおける再生速度です。ここは仮で1倍速にしてます。
水平方向の座標 28行目
Lerp関数の第3引数は0~1に収めないといけないため、flightTimeで割っています。
鉛直方向の座標 29行目
前項④式に基づいています。
1フレーム経過 31行目
コールチンのfor文内で使われた
yield return null;
は 中断するという意味で1フレーム経過したのち、再びfor文内の28行目からスタートします。そのため、for文で一気に計算するのではなく、フレームごとに座標結果を更新していくことができます。
まとめ
コールチンを組み合わせることで、毎フレームごとの座標更新ができるというのが私的に目新しいところだと思いました。
本記事は、今読んでる上の「Unityゲーム プログラミング・バイブル」に掲載されていたものを元に私なりに解説してみたものになります。
細かいセクションで内容が分かれていたので、濃い内容かどうか不安でしたが、空き時間に2年ほど触ってきた私ですが、知らない内容やソースコードのノウハウがあったので読んでみる価値はあると思います。