偶然翻到 INSIDE、LIMBO 開發商 Playdead 在 Unite 2016 大會中發表的演講影片與簡報檔。
發現 28:34 開始的講題【Scripting Performance】非常有意思,特此分享給大家。
在這段影片中,Playdead 的工作人員用開發 Inside 時的經驗,
分享了他們 10個可以讓 C# Script 執行效率更佳的建議。
Part 2. 連結
= 那就開始囉 =====================
1、Reduce Vector operations (減少 Vector 的運算)
| 1 2 3 4 5 6 7 8 | void before() { var lastPos = transform.position; transform.position = lastPos + wantedVelocity * speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime; } | 
| 1 2 3 4 5 6 7 8 | void after() { var lastPos = transform.position; transform.position = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } | 
在 after() 與 before() 最大的差異,在於他將 speed、speedfactor、someOtherFactor、drag、friction及Time.deltatime 這些 float 變數 都先計算完後再跟 lastPos及 wantedVeloctiy 這兩個Vector3 變數做運算。 這樣的目的是 「減少在程式中進行向量值的計算次數 」。
舉例來說,在 before() 中總共進行了七次的向量計算:
同樣結果,在 after() 僅進行了兩次的向量計算:
- 第一次 :wantedVelocity *= speed*speedfactor* ...Time.deltaTime
- 第二次 :wantedVelocity += lastPos
2、Use cached Transforms (製作並使用 Transforms 快取)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private Transform _transform; void Awake() { _transform = GetComponent<Transform>(); } public void after() { var lastPos = _transform.position; //cached in “void Start()” _transform.position = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } void before() { var lastPos = transform.position; transform.position = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } | 
在上面的程式碼中,在 Start()階段將 Transform 這個 Class 存到 _transform 之中。
之後當有需要用到 transforms 時都直接從取_transforms變數。
按照 Playdead 在影片中口述解釋,直接向Unity調用 transform 時(如下程式碼)
 var lastPos = transform.position;
Unity 為了安全起見並不單純的回傳 transform資料而已,
所以為了提升效能我們可以在 start() 階段先儲存一個快取用的 transform ,
減少反覆呼叫時多餘的浪費。
P.S 這部份我原先看不懂怎麼製做快取,後來直到 Google 到 ...【這篇】
3、Use localPosition (if possible) (盡量使用 localPosition )
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private Transform _transform; void Awake() { _transform = GetComponent<Transform>(); } void after() { var lastPos = _transform.localPosition; _transform.localPosition = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } void before() { var lastPos = _transform.position; //cached in “void Start()” _transform.position = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } | 
應該不難想像用 localPosition 會比起時用 position 來的省效能,因為 Unity 平時是儲存LocalPosition的Data,然後有需要使用世界座標時再轉換依照子母層級作轉換。
因此直接使用LocalPosition 時可以省去子母階層的相關計算。
P.S01 雖然是這麼講...但我自己的實驗沒有差多少效能耶@@
P.S02 但是在 Playdead 的簡報中,效能的提升非常顯著 (如下圖)
P.S03 也許發佈成執行檔版本才會有所差異
4、Reduce engine calls (減少向 Unity 查找資料的頻率 )
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public Vector3 cachedLocalPosition = Vector3.zero; void Awake() { _transform = GetComponent<Transform>(); } public void after() { cachedLocalPosition += wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); _transform.localPosition = cachedLocalPosition; } public void before() { var lastPos = _transform.localPosition; _transform.localPosition = lastPos + wantedVelocity * (speed * speedfactor * Mathf.Sin(someOtherFactor) * drag * friction * Time.deltaTime); } | 
Playdead 提醒大家盡量減少向引擎查找資料。
例如在上面的程式碼中將 before() 中的
var lastPos = _transform.localPosition;
在 after() 中的改為額外透過下面這個全域變數作儲存
public Vector3 cachedLocalPosition = Vector3.zero;
這樣的好處是當我們要索引物件的座標時,
不用多一層透過 Transform 這個 Class 來索取座標。(ㄜ 這樣講應該沒問題吧?)
5、Hmmm… Are getters and setters slow? ( 使用 get、set 會有額外耗能)
| 1 2 3 4 5 6 7 8 9 | public class ScriptPerformanceTest : MonoBehaviour { public float speed { get; set; } public float speedfactor { get; set; } public float someOtherFactor { get; set; } public float drag { get; set; } public float friction { get; set; } } | 
| 1 2 3 4 5 6 7 8 9 | public class ScriptPerformanceTest : MonoBehaviour { public float speed; public float speedfactor; public float someOtherFactor; public float drag; public float friction; } | 
C# 中提供了非常實用的 get、set ,讓我們可以更自由的去設定變數的可存取範圍。
但是如果非必要的情況下可以減少這工具的使用,可以再省下些效能唷!
==============================
 
 

 
![[教學] 我的 Visual Studio Code 插件與自訂教學](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi90P3Z4pwFn3AWJa6uaAUdX_XBp1jhwFJ39lriVrXtuPghgdypY6SP47Uopxc8e0ZTzQY-bCTDSuJYTS9-1rZEj6kOHGlMBBr_zA4QMgycNWD5f-Dsy5Pjox3cfdn6eemE2HxtZL1EczU/w72-h72-p-k-no-nu/vsseting.png) 
![[教學] 用 Visual Studio Code 編輯 Unity](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPy73GNzKib9kVk_HMKmJBsbsawMoInA5cL77_Y8lQWrxJpD6fuugPEONjZsAu4tCl67zZXtRruKrTgEIacKnLwNXQZOj2yKbFYisutYPzt0wW3f60F5JEOaA6yXO27fsdttozjUQe7hI/w72-h72-p-k-no-nu/vscodedownload.png) 
![[Unity] 使用內建的JsonUtility 遊戲讀存檔案 --2017.11.20更新](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiohxWfvXmh6iVj9mDIXh_ESjXm_9ZFEuT3WqKvMQoUOYD89o8nwdMNW1mL4wIuhQ6eUuUx5BvchNxe-IkXM_8wZoWIXapLXK2d0sO83j23yHeKPuhyphenhyphen1L3ddBRIps1EVkaFDb6uOz9IXUw/w72-h72-p-k-no-nu/json160.gif) 
![[心得] 該用Javascript 還是 C# 寫 Unity 呢?](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSOUbLWldpAcfDHjABeTZ-ekHtXb6nsSzhloWsGbokG9II-iDxHpGH_Xmg8iP-OwruvQwaMzPkCxNFqXwidVCIp6l-f8-BYayGmo0_veWgZTU5UrBjGmQzP7OMLhOfGygy8S9TXFNWst4/w72-h72-p-k-no-nu/maxresdefault.jpg) 
![[教學] 在Window工作列顯示資料夾](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwfSrJInI75WMZCWa8Gr5w8fGmd7S5QXUr-g2mdC7jkQm38FRcbIuW09T5pTWj52Jt0WPtoIlFSzJuRiY-4a9OOirwp19rYGTYxuUgizBJgPKtQxe6wGRPDqNvnJS2yEB6KUwTNrbhgbk/w72-h72-p-k-no-nu/taskbar.png) 

作者已經移除這則留言。
回覆刪除