不過在遊戲中通常也用不到這麼精細的計算,這邊使用的公式也都是以點為主,而完美拋物線也只需要考慮重力的作用即可。
如果只是單純地要在遊戲投射出物體,像是炸彈、手榴彈等等,其實直接使用Unity內部的物理運算即可,也比較輕鬆,不使用Unity的物理系統自己手動運算移動軌跡根本就是自找麻煩,不過從這邊讓我也對拋物線有更深一點的了解。
首先就要來看拋射物的公式,請參考WIKI。
http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
- g: the gravitational acceleration—usually taken to be 9.81 m/s2 near the Earth's surface
- θ: the angle at which the projectile is launched
- v: the velocity at which the projectile is launched
- y0: the initial height of the projectile
- d: the total horizontal distance traveled by the projectile
--
這邊有幾個重要參數,做成Component之後,有些可以直接在Inspector中設定,有些可以經由計算取得:
g重力,直接設定。
θ角度可以由兩種方式給,一種就是直接設定角度,另一種就是藉由發射點座標跟發射目標的座標求得向量,再由這向量計算取得θ角。
V初速度直接設定。
Y0發射點座標,這邊我若從原點發射,設定座標是Vector3(0, 0, 0),這個會影響物體飛行時間,因為這邊假設y=0的位置為水平面,物體y<0算落地。
d水平飛行的距離,先由Vy這個Y軸初速度加上g的參數,計算出飛行至落地時間,再由飛行時間乘上X軸的初速度Vx,即可求得飛行距離。
--
有了基本數值,接著就可以來設定物體的座標了。
transform.position.x 為物體當前的X座標
transform.position.y 當前的Y座標
nextX 物體下一個frame的X座標,這邊使用Time.deltaTime來預測下個位置
nextX= (transform.position.x - startPos.x) + Time.deltaTime;
nextY 物體下一個frame的Y座標
這邊Y座標的計算同樣參考上面的WIKI,根據X座標來計算Y座標位置的公式
nextY= y0 + nextX * Mathf.Tan(theta) - (g * Mathf.Pow(x,2))/(2 * Mathf.Pow(v * cos(theta),2) );
--
實作測試,Speed調整物件移動的速度,V0調整初速度,投射的目標可以用滑鼠拖拉
--
拋物線移動物件Component
public class ParabolaObject : MonoBehaviour { public GameObject target; //初始投射的目標方向,用gameObject只是方便在editor中方便拖拉編輯 public float g = 9.81f; //g = 9.81 m/s^2 public float speed = 0.8f; //物體transform在拋物線路徑上移動的速度,此速度不影響拋物線的形狀 public float V0 = 8.0f; //初速度 public float ground = 0f; void Start () { if(target != null) StartCoroutine("Move", target.transform.position); } IEnumerator Move(Vector3 target) { ground = 0; if (target.y < ground) ground = target.y; Vector3 startPos = transform.position; //初始座標 Vector3 dir = (target - transform.position).normalized; //先取得初始投射方向的向量 if (dir.x == 0) dir.x = 0.001f; //避免x=0無法移動 float theta = Mathf.Atan2(dir.y, dir.x); //再由向量求得初始角度 //運算求得基本數值,飛行時間t(受到V0、theta、Y0的影響),飛行距離(受到V0、t的影響),最高點位置h //這邊幾個數值,在下面物體移動的計算上目前其實用不到,但計算出來如果有需要就可以使用 //float Y0 = startPos.y; //把Y0獨立出來看得比較清楚,其實可以直接使用startPos //float t = (V0 * Mathf.Sin(theta) + Mathf.Sqrt(Mathf.Pow(V0 * Mathf.Sin(theta), 2) + 2 * g * Y0)) / g; //float distance = t * V0 * Mathf.Cos(theta); //float h = transform.position.y + (distance / 2) * Mathf.Tan(theta) - (g * (distance / 2) * (distance / 2)) / (2 * (V0 * Mathf.Cos(theta)) * (V0 * Mathf.Cos(theta))); while (true) { float nextX = (transform.position.x - startPos.x) + speed * Time.deltaTime; //使用公式 y = y0 + x * tan(theta) - (g * x^2)/(2 * (v * cos(theta))^2); 來取得y座標 float nextY = startPos.y + nextX * Mathf.Tan(theta) - (g * Mathf.Pow(nextX, 2)) / (2 * Mathf.Pow(V0 * Mathf.Cos(theta), 2)); //移動物體,這邊就直接設定座標了 transform.position = new Vector3(nextX, nextY, 0); //如果y座標小於地板座標就停止移動 if (transform.position.y < ground) break; yield return null; } } }
No comments:
Post a Comment