關於語言選擇 - Golang
語言之爭
語言是一個容易引起爭議的話題,選擇 Golang 只是因為 -- 簡單&快速,在過去10年的時間裡也使用過各種不同的語言,但是最終 Golang 成為了我的歸宿。
我並不喜歡用腳本語言做大型項目,做過的同學都知道: 「動態語言一時爽,代碼重構火葬場」,腳本語言適合做上層的膠水語言,底層還是靜態語言比較舒適。但是我也沒有想過用 C做底層用類似 lua/js 做上層的實踐,這會讓代碼沒有連貫性,雖然只是20%的時間才會修改底層代碼,但是同樣需要80%的底層語言的知識,這等於需要同時掌握兩門語言。如果能用一種語言就搞定上層和底層應該是很爽的事情.
C#和Java在遊戲引擎領域真正做底層語言的還是比較少的,我知道有些引擎是完全用C#寫的,比如 Xenko Game Engine 這是一款很快的C#遊戲引擎(代碼質量也很高),很多 Java 程序員用 libgdx 來寫遊戲,Minecraft 是用 Lightweight Java Game Library 開發的,但是這類JVM語言的內存模型並不容易控制,很容易帶來性能問題,另外就是語法特性已經越來越多,我喜歡更簡潔的語言。
C/C++ 很屌,現在流行的遊戲引擎幾乎都是C/C++開發的,但是開發效率低是不可迴避的問題,使用C/C++要時刻注意內存管理,每次編譯都要等待很長時間。在C/C++領域已經存在無數非常優秀的遊戲引擎,再造一個輪子也沒有任何意義。能夠代表現在語言的是 Swif/Go/Rust,Rust 很快但是 Rust 的學習成本要明顯高於 Swift/Golang(還是需要做內存管理), 對於2D遊戲引擎而言 Rust 是牛刀小用。所以接下來是在 Swift 和 Go 之間做出選擇,它們幾乎都是完美的語言。Swift的幾次版本發布都做到了不兼容更新這給人一種不穩定的感覺,而且Bug很多。相對而言 Golang 已經很成熟了,有大量的工程實踐證明,這是一門值得信賴的語言。
Golang 很簡單很容易上手,這無形中避免了小白用戶追逐語言特性的問題。另外她在開發效率和性能之間有一個很好的平衡點,雖然沒有C/C++快但是已經很接近,但是在開發效率方便確是十倍/百倍的提升。編譯速度極快,啟動一個大型 Golang 項目可以不到1秒的時間(再也沒有借口到樓下買致癌咖啡了)。總結來說:
- 很快 雖然不是C的速度但是接近於C
- 簡單 自動內存管理,類似腳本的語法極易上手
- 內存 Golang的內存模型和C一樣容易控制, 容易實現內存友好的代碼
- 編譯 對C++來說這是不可想像的事情,瞬間編譯運行.
- Cgo 方便調用現有的C庫
- 並發 自帶並發模型(雖然現在引擎還沒有使用)
一段簡單的 Golang 代碼:
package mainimport "fmt"func main() { fmt.Println("Hello, 世界")}
你可以在 Go語言官網 運行這個小例子。
現存的問題
Go語言本身也有一些的缺點比如:
- 不支持泛型/模板/宏
- 不支持運算符重載
- GC導致的性能抖動
泛型問題,是目前討論最多的話題,各種方案層出不窮。本質來說支持泛型有兩種選擇,一者在編譯期間支持(這樣會降低編譯速度,C#方案)自動展開所有的泛型表達式,一者在運行時支持(這樣會降低運行時速度,Java方案)。Golang的主要作者 Russ Cox 已經多次表態不會做出任何妥協當然也不會支持泛型。所以泛型問題基本無解的。
運算符重載這是在做圖形/物理編程時才遇到的特殊情況,因為圖形/物理編程需要做一些數學運算,沒有運算符重載是很難過的,比如在C++中,可以寫:
Vec2 a, b, c; c = a + b
但是在 Golang 中,需要寫:
var a, b, c vec2c[0] = a[0] + b[0]c[1] = a[1] + b[1]
這是很痛苦的事情,也是無解的。
Golang 的GC方案是偏向高頻低延遲的,這篇文章 Modern garbage collection - A look at the Go GC strategy 對此有一個比較詳細的分析。這種GC模型對遊戲來說還是有些好處的,但是GC總是一個問題。在開發中也會使用一些手段來解決GC問題,比如在做 fps-smoothing 的時候,故意丟掉最高和最低的幀(這些幀可能是垃圾回收導致的跳動);數據以值的形式保存在連續的數組中,並使用索引而非指針來引用,這樣可以大量的減少指針的使用也減輕了gc的負擔。另外GC是個好東西,不需要手動管理內存,這樣可以專註程序邏輯,自然的提升了產出。
其實還有一個少為人知的問題 - Cgo. Cgo is not Go | Dave Cheney 這篇文章對 Cgo 有一個較為詳細的剖析。Cgo的問題其實還是一個比較嚴重的問題,因為Golang中調用C是有成本的。現在做一次Cgo的調用大概會消耗60ns左右,而一次Go的調用僅消耗1ns不到的時間,所以說使用Cgo會嚴重降低性能而且是60倍!(在Golang的一些版本中可能會消耗180ns或更多的時間)。這是確確實實會導致性能的問題,所以我們的開發中除了最基本的OpenGL調用,基本上沒有再調用其它的C端API(所以也不要嘗試使用C來加速Golang程序可能會適得其反)。對於大部分系統比如渲染底層,音頻,動畫等都有比較不錯的C/C++實現,但是為了不再 Cgo 上消耗時間我們都用 Golang 重寫了。但是也沒有那麼嚴重1ms=1000000ns,經過Batch到達底層的DrawCall數量最多在幾百個左右,已經可以處理足夠多的Cgo調用了。
解決Cgo問題需要生態建設,只要更多的庫是基於純 Go 構建,那麼自然就可以不必再依賴於 Cgo 調用。一年前的時候我還在把 Box2D 翻譯成 Golang 實現(當時還沒有Box2D的Golang版本),但是現在已經有了比如 Box2D.go, 相信以後會越來越好。
Golang 也是沒有繼承的概念的,這是面向對象語言的核心概念。事實上任何一門圖靈完備的語言都可以做到互通。用Golang也是可以實現繼承/虛類/覆蓋的之類的事情(用C也可以實現)。只是這樣做並沒有什麼意義,使用Golang的時候需要用Golang的方式來思考問題。
其它
關於 Korok 這個名字源自於塞爾達傳說中的那個樹精靈,這個單詞是一個對稱的單詞只有三個字元容易記住是個不錯的名字。另外前幾天重寫了官網的主頁:Korok.io , 我對這個頁面非常喜歡。
另:這裡用了一副圖片是存在版權問題的,現在也在嘗試解決,如果有同學有通道的話也感謝幫助解決。
推薦閱讀:
※我們做了一款遊戲,希望能關注我們,感謝!
※獨立遊戲工作室現在已經不再適合涉足手游開發了嗎?
※Stephens Sausage Roll的關卡設計
※關於Korok2D遊戲引擎
※為什麼在中國遊戲市場的今天,我們還要去做獨立遊戲?