search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

用Java製作一款Steam上的遊戲

哈咯,大家好,又見面啦,今天我們要介紹一下如何用Java來製作一款遊戲,並將其放到Steam上售賣,嘖嘖嘖,看到標題之後,你的想法是不是如下圖啊?哈哈哈

嗯,我知道傳統認為,Java並不適合用來做遊戲,馬上我們將會談到Java不適合做遊戲的原因,隨後我也會一一解釋如何解決這些問題,隨著時間的推移,以前很多認為不可能完成的事,現在也都已經不再是障礙咯,表刻舟求劍哦。

先來看例子,Java做的知名遊戲至少有以下兩款:

Minecraft(俺的世界)

Spiral Knights on Steam(螺旋騎士)

前者創造了M$歷史上至今為止最大一筆收購,後者放在Steam上售賣,打開該遊戲的界面可以看到該遊戲要求Java至少是1.5:

好了,這說明了一點,就是Java是可以用來做遊戲滴,關鍵看製作者的能力如何,人笨不要怪刀鈍。

使用Java的優點不用解釋了,應該很多人都知道,開發快,IDE支持好,網路支持好,類庫多,跨平台等等,下面主要說一下傳統用Java開發遊戲的障礙,為什麼Java並沒有被用來製作遊戲的幾個常見理由:

  • Java的程序運行需要預先在各個操作系統上安裝虛擬機;
  • Java有GC停頓,會阻礙客戶體驗;
  • 沒有流行的遊戲引擎;

先說第一個,這個的解決依賴於大概14年時候Java推出的JavaFX新一代圖形控制項,該控制項有一個native compiling的工具,可以將JavaFX的代碼打成不同操作系統上的獨立運行的包,比如打成Windows上的exe文件,打成MacOSX上的dmg文件,用戶拿到之後,雙擊就可以運行啦。而在最新版本的Java中,JavaFX已經集成進Java,成為Java的一部分啦,所以不需要單獨下載JavaFX,只需要下載Java,JDK之後,就能使用JavaFX,以及native compiling工具,是不是很方便呢?Native Compiling打包可以參考專欄之前寫的一篇文章:JavaFX的幾個新特性,讓Swing徹底過時

有一點可能需要注意,打包的時候,選擇一下all,而不是特定的exe或者dmg這些,這樣會生成一大堆文件,你把你需要用到的部分挑出來,那些不用的直接丟棄便可。

至於哪些是你需要用到的,你自己摸索啦。

這樣就可以發布特定平台相關的應用,是不是很方便呢?

然後第二個,GC停頓,這個是一個很大的topic,Java的GC裡面講究甚多,這裡只能簡單說明一下,Java傳統的GC演算法呢,在啟動GC的時候,會暫停整個虛擬機的執行,然後等GC完成之後,再繼續執行,而人對於超過一定時間的停頓,是可以感知到的,一旦GC停頓超過該感知的界限,玩遊戲的人的體驗就會變差,嗯,這麼說有些過於感性了,我們來一點數字。

一般遊戲的幀數是20幀到60幀之間,少數會衝到90幀,幀數意思就是每一秒遊戲畫面刷新的次數,60幀的意思就是一秒鐘內遊戲畫面刷新60次,為什麼遊戲的幀數會在20-60之間呢?

因為低於20幀,人就能看出畫面的停頓,超過60幀,幀與幀之間的停頓時長,基本上就超出了人可以感知的範圍了,也就是說,如果兩個不同畫面的停頓超過1/20秒,人看到的就是一幅一幅不同的圖片,而不是動畫,而要讓人看到動畫的效果,不同兩個畫面之間的停頓,至少要在1/20秒也就是50ms以內,而人視覺感知的極限,就是1/60秒,也就是16或者17ms,如果停頓時間短於16ms比如是10ms,那麼人在視覺上是很難感知到的,也就是說,一秒刷新60次,跟一秒刷新90次,在人看來,幾乎沒啥差別,所以就能看到iphone或者android手機的廣告,60幀如絲般順滑,blablabla,簡而言之,就是我們要把刷新畫面做到一秒刷新20次以上,60次最佳。

那這個時候我們就能看出來啦,GC停頓如果超過50ms,客戶就能感受到GC,如果低於16ms,完美。

這裡說一下,雖然超過50ms就能感知到,但是一般情況下,只要這種50ms級別的停頓不頻繁發生,客戶體驗並不差,比如10min觸發一次50ms的停頓,這有關係嗎?玩個遊戲又不是搞導彈攔截,偶爾來個50ms的卡頓會死人還是會懷孕啊?而且網游裡面,公網的延遲經常超過50ms這個量級,尤其是手游的破網路。

那腫么做到呢?

有技巧,首先要修改GC的策略,在新版本的Java中,加入了新的GC策略G1,該策略將會在Java9中成為預設的GC策略,該策略允許設置目標停頓時間,解釋一下目標停頓時間的意思,就比如我們設置目標停頓時間為10ms,那麼GC會儘可能在10ms內結束,如果完不成該目標,則暫停GC,程序恢復執行,等下一次GC繼續,如此反覆,所以G1策略的總停頓時間,會超過CMS策略,但是每一次GC停頓,都會控制在一定時長以內,但是不能保證一定在該時長以內結束,只是虛擬機會盡量完成這個目標,這是一。在打包時候,ant build file裡面用fx:deploy, fx:platform和fx:jvmarg來設置JVM參數,which包括了GC策略等參數。

其次,我們要減少GC的時長,腫么做?將對象pool起來,也就是說,需要復用對象,不能頻繁生成並銷毀對象,這樣非常消耗GC資源,會明顯增加GC停頓時長。舉個例子,如果我們做的是一個射擊遊戲,那麼發射出去的子彈,我們需要pool起來,比如把子彈對象都放在一個list裡面,子彈出了邊界之後,標記對象為無效狀態,但是並不從list中remove掉,這樣因為有list的引用在,該子彈對象不會被GC掉,下一次發射的子彈,先找一遍有沒有標記為無效狀態的對象,如果用,則使用該對象,如果找不到,再增加一個新的對象,放入list,這樣子彈對象數量隨著遊戲的進行,會趨近於一個定值,而因為有list這個引用在,所以對象不會被GC掉,這樣GC的壓力就小了很多,即便觸發了full gc,其執行時間也在一定範圍之內,有助於我們實現目標GC停頓時間。

再其次,多線程併發執行,先將代碼通過MVC分離之後,m也就是model的部分可以併發執行,這個技巧客觀滴說,很多遊戲都做不到,包括鋼鐵雄心,到現在還在跑單線程,玩到後面卡得一塌糊塗,尤其是當蘇聯有了上千個師的時候,還有一些大廠的後台,居然跑的也是單線程,智商實在是感人,不過遊戲後台這個技巧將來留給Vert.x部分再說。

最後,渲染時候儘可能調用底層的API,也就是View的部分,請使用諸如Canvas之類的控制項,在安卓上,就用SurfaceView,在IOS上好一點,有SpriteKit可以使用,而不是其它的控制項,比如ImageView等,因為普通的ImageView需要換Image的時候,需要重新生成一個Image對象,乖乖,圖像對象可是很大一個東東,頻繁生成銷毀這種對象,GC不卡才見鬼。

做到了這幾點之後,一般GC停頓實測,MacBook Pro 15年版,基本上GC在30ms左右,如果是網吧的Windows機器,基本上都在15ms以內完成,也就意味著,能夠實現我們的目標啦。遊戲的本質其實就是一個UI線程不停滴刷新屏幕,不要打斷這個UI線程的這麼一個過程,其本質跟Vert.x的Eventloop線程有一定相似之處,扯遠了。

本來想寫完的,但是寫到這裡發現,東西還挺多,那就先這樣吧,下一篇我們再解釋如何製作一個簡單的遊戲引擎,然後發布到Steam上去。

最後來一個最愛的哈拉魚表情,萌死了

bye



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦