Posted at

オブジェクトを放物線を描いて移動させる【Unity】


はじめに

終了地点と開始地点を指定し、放物線を描きながら物体が移動する方法の紹介です。

放物線の方程式は高校数学の範囲ですし、もちろんすでに先人の方々が紹介済みですからググれば情報は山のようにヒットします。

でも、せっかくUnityを使うのならもっと簡単にそれっぽい動きが表現できそうだと思ったのでまとめてみました。

環境は以下の通りです

win10 64bit, Unity2018.3.3f1


方法

やり方は至ってシンプルです。

まず、開始地点p0と終了地点p2の中点p1を求めます。

次に、中点p1に高さを足し、弧を描いて飛んでいく途中の最高到達地点を求めます。

最後に、2次ベジェ曲線を使ってp0~p2の3点を補間します。

ベジェ曲線を使った補間に関する参考リンク:「一から学ぶベジェ曲線


実装


中点を求める

StartThrow()の引数には、

1. 飛ばす対象

2. 大体の高さ

3. 開始地点

4. 終了地点

5. 補間スピード(フレーム数)

を指定しています。

public void StartThrow(GameObject target, float height, Vector3 start, Vector3 end, float duration) {

// 中点を求める
Vector3 half = end - start * 0.50f + start;
half.y += Vector3.up.y + height;

StartCoroutine(LerpThrow(target, start, half, end, duration));
}


ベジェ曲線で補間

LerpThrow()はtargetがdurationフレーム数をかけて、start地点から始まり、half地点を通過して、end地点にたどり着くためのコルーチンです。

CalcLerpPoint()はp0とp1を1次補間した位置aと、p1とp2を1次補間した位置bを求め、そのaとbをさらに2次補間して最終的な位置を求めます。


コルーチン

IEnumerator LerpThrow(GameObject target, Vector3 start, Vector3 half, Vector3 end, float duration) {

float startTime = Time.timeSinceLevelLoad;
float rate = 0f;
while(true) {
if(rate >= 1.0f)
yield break;

float diff = Time.timeSinceLevelLoad - startTime;
rate = diff / (duration / 60f);
target.transform.position = CalcLerpPoint(start, half, end, rate);

yield return null;
}
}



補間

Vector3 CalcLerpPoint(Vector3 p0, Vector3 p1, Vector3 p2, float t) {

var a = Vector3.Lerp(p0, p1, t);
var b = Vector3.Lerp(p1, p2, t);
return Vector3.Lerp(a, b, t);
}


最後に

放物線の最高地点を求めると書きましたが、ベジェ曲線の性質上、3点の補間では正確にp1が最高地点とは言えないので、なんちゃって放物線ですね。でも複雑な計算式を必要としない、なんとなく弧を描いているように見せたい場合にはこれで十分な気もします。

次回はオンライン関係の記事が書けるようにがんばります。