因為自己是做球類運動的遊戲,所以之前有稍微玩過一下Unity中的物理引擎
先講個人心得,大致上Unity的物理計算還算是符合牛頓力學公式的 :
 
F = ma  (施力 = 物體質量 * 物體加速度)
 
以下我們先證明Unity的物理運算是否符合牛頓力學公式.
牛頓力學公式 :
F = ma = m * dv/dt 
v = v0 + a * t ( 初速 + 等加速度 * 時間  = 末速度)
d = v0 * t + 1/2 * a * t^2 ( 等加速度系統下的距離是 v = v0 + a*t 的積分總和) 

首先
F = ma 其實在掛上Unity相關的元件後就可以看出一些端倪 
他有一個 use_gravity 的欄位,其實就是指定是否使用重力
 
我先讓一顆球掛上Rigidbody, 並測測他的重力是否符合預期? (比薩斜塔實驗)

我們準備一個plane 和一顆球,球掛 Rigibody, mass = 1, use_gravity 先勾掉
然後我們準備一個 script ,讓他可以啟動gravity(球就會往下掉)
並套用重力加速度的公式
 
 d = v0 * t + 1/2 * a * t^2
 
反推算出球預計落下的時間 

有約3ms 的誤差, 平心而論, 在物理系的眼中這模擬結果是不及格的
但是做遊戲來講,還算可接受(預測) 的結果
 
以上是重力加速度部分,那如果我在一個地點丟一顆球(配合Unity物理計算) , 我是否能透過計算算出落點?
可以,但一樣會有誤差
 
先了解一下Unity的施力怎麼做? 一樣都是Rigidbody component下,你有AddForce 的這個function, 看一下Unity官方手冊 

他提供四種模式 - 等加施力,等加速度,瞬加施力,速度改變
這邊是運用等加施力來計算的
 
我們就來試看看
這邊先提一下套用Unity的物理, 你不能使用Frame Update()這種不精確的loop function
而是要使用它提供的FixedUpdate , 這個loop API是跟著物理引擎來固定update 物理的運算(default 每20ms)
 
我們先試試對一物體施一力量(force), 讓他飛行,然後依照公式計算預測他的落點在哪裡 

我們不用 ForceMode.Impulse , 這個瞬加施力感覺是不考慮FixedUpdate時間的,理論上無法用在力學公式上
所以我們用Force mode, 但Force_mode是等加施力在物體上的, 所以我們須控制讓他在第一段FixedUpdate的時間施力
然後取消施力,這樣我們至少能取出 delta T 的時間,也就是  Time.FixedDeltaTime.
 
程式碼中我們用 按鍵A 生成一顆球,並指定一個力量方向(加一點小亂數值), 一開始球一樣不開啟重力
在一開始生成後,施加力道,並啟動重力—>>這是第一個FixedUpdate, 我用的是等加力量(force)
並且用 useGravity當flag 來等待進入第二個FixedUpdate時,不施加力量並預測落點 

在預測落點的程式碼中, 我們可以先比對計算我們自己從公式算出的速度為何,並跟rigid的速度對照
show在log上
然後計算落點距離,這邊的做法化簡成球從地面開始投擲
所以公式很簡單就是
Vy = G * t
 
Vy 就是y方向的初速,也就是我們一開始施力後的Y方向產生的速度(一樣套 Vy = a * t, 這邊的a 就是施力的y方向力量,而 t 就是FixedDeltaTime)
 
這樣我們就算出一個有初速的球往上拋,因重力加速度到頂點速度為0時的時間了
然後乘2倍就是球整個飛行的時間
利用這時間去各自乘 X, Y方向的速度,就算出X,Y方向的飛行距離了!  

這是Log產生的結果

你會發現值差異不大, 理論上Unity的等加力量是可以被我們透過加速度公式計算出來的!
 
Demo 影片中,每按一A鍵,就投擲球,並產生預測落點, 理論上,球都會落到預測紅點上! 

那你會問,那如果我得知兩物件間的距離,可以用公式計算出該用多少力道將球傳到對方手中嗎?
答案是可以的喔! 不過你要指定你要花多少時間讓球飛行,並用力學公式回算你需要多少力道
 
打個廣告,目前開發的遊戲 <<策略棒球-少棒風雲錄>>
球員的傳接球都是用力學公式算出的, 每個球員甚至有一個傳球速度數值來定義此球員的傳球力道 

至於投手投球的公式,會稍微複雜點,因為考將球的(自)轉數所產生的馬格拉斯力量考慮進去
進而產生所謂上飄的直球,沉球,滑球...等效果
這也是套用Unity物理進行即時運算產生的
不過我先趕Project先,這段等我忙到一定程度再講 

結論

這篇講我在使用Unity物理上的心得,不過說實在他的確有一些缺陷
比如說最著名的問題是速度過快,碰撞偵測就偵測不到了
我也遇到不少”Corner”case ,比如說一顆球打到地面就”鑽"進地面了 
(最後原因是 Colider間有個 0.01的小縫隙, 球的圓心恰巧鑽到那縫隙裡,完全忽視它自己的球半徑...etc)
還有自己做遊戲的心得是 - 先判斷這功能適不適合做物理(或者說適不適合套他的物理引擎)
像物體的移動我就和他的物理引擎脫鉤, 只用上碰撞偵測(Trigger), 連碰撞(Collision)都不用上
畢竟物理引擎計算也是很耗效能,用個簡單的Animation對碰撞做出反應會更有效的做法