創作內容

20 GP

【程式】遊戲程式基本架構

作者:Shark│2019-07-22 21:28:47│贊助:1,136│人氣:587
寫篇新作製作進度以外的文章,如果對遊戲引擎內部如何運作有興趣,可以看看。

顯示晶片與Direct3D、OpenGL當然也跟遊戲程式有關,但寫在這篇會很長,有空再另外寫一篇介紹。



以前寫過基本視窗程式介紹,可以看出有GUI的程式是怎麼運作的。
如何建一個視窗—Windows API篇
如何建一個視窗—X Window篇

初始化之後進入一個迴圈,程式會在一個地方進入等待狀態,等有輸入的時候(如移動視窗、按鍵盤、動滑鼠)才進入下一步做事,人沒操作的時候一般是停著不動的。
office、繪圖軟體、瀏覽器等工具軟體是這樣運作的。


遊戲程式和這些軟體有個重要差異:即使人沒在操作,仍然有動態和聲音在跑。
所以不能讓程式停在一個地方等著,即使沒有輸入,也要不斷執行「取得輸入→更新邏輯→更新畫面」的迴圈。


既然是個不斷在跑的迴圈,假如把程式寫成這樣會如何?
while(isRunning){
  pollInput(); //取得輸入
  //………… (處理遊戲邏輯)

  nextFrame(); //更新畫面
}

效能好的電腦能比較快完成工作,光這樣寫在每台電腦上跑得不一樣快。想想看ACT和STG的敵人,在有的電腦上動作慢,有的電腦上動作快,有時候快到人無法反應,這樣遊戲要怎麼玩?

所以遊戲程式還有一件事要做:讓程式以穩定速率運行。


每秒跑幾次上圖的迴圈稱為frames per second,簡稱FPS。
一般是配合螢幕的更新率做成60FPS,有些對流暢度要求不高的遊戲會使用30或20。
如果因為計算量太大、電腦效能不夠強、或有阻塞式IO,不能在1/60秒內把工作做完,就會看到畫面短暫停頓,這稱為lag。

遊戲程式有很多計算量大的工作,像是物理計算和繪製畫面,而且每秒要做這些步驟20、30或60次,所以遊戲比其他種類的程式更重視效能:寫程式很講究如何提升計算速率,玩遊戲也對CPU、記憶體和顯卡的性能有要求。

控制時間要用到作業系統API的計時器和sleep功能,實際程式會這樣寫
while(isRunning){
  //在主要工作之前取得目前時間
  ULONGLONG prevTime;
  QueryUnbiasedInterruptTime(&prevTime);

  pollInput(); //取得輸入
  //………… (處理遊戲邏輯)

  nextFrame(); //更新畫面

  //將目前時間與前一次取得的時間相減,求出經過的時間

  ULONGLONG nowTime;
  QueryUnbiasedInterruptTime(&nowTime);
  //QueryUnbiasedInterruptTime單位是100ns,除以10000換算成ms
  int elapsedTime=(nowTime-prevTime)/10000;
  //暫停一段時間
  //16約等於1000/60
  //elapsedTime有可能>16,要避免給Sleep()的值是負數

  Sleep(__max(16-elapsedTime, 0));
}

上面是Windows API的範例,其他平台要用不同的系統函式控制時間。

除了計時器以外,還有一種定時方法是與螢幕更新同步,即vsync,下面會介紹。



此外介紹兩個遊戲程式裡的概念。

—Double Buffer—

現實中將兩張紙分別畫上角色和背景再重疊,上層那張紙移動時,下層會自動露出來。

不過電腦裡的世界並沒有「兩張紙」的存在,電腦記錄圖像的方式相當於只有一張紙,在上面重覆畫物體,後畫的會蓋掉先前畫的。

(那些十六進位數字是ARGB值)

所以電腦裡想做角色移動的效果,不能像現實中那樣移動紙,而必須把畫面清除,重新把物體一件一件畫上去。
(很多遊戲引擎可以用設x,y,z坐標的方式移動物體,不用手動下畫圖的指令,那是引擎內部把畫圖的工作做掉了)


直接畫在螢幕上會發生一個問題:清除畫面後有一個瞬間畫面是空白的,畫面會閃爍。

這時要用一個技巧:double buffer
在記憶體裡建立另一張點陣圖,先在此處把整個畫面都畫好再貼到螢幕上,這樣才不會閃爍。


不過看一下市面上的遊戲引擎,並沒有要求使用者在算完邏輯以後,呼叫一個「把buffer複製到螢幕上」的指令,因為這是所有遊戲程式都要做的事,遊戲引擎都會在內部處理掉。不過知道一下有double buffer的存在比較好。

但如果想用遊戲引擎以外的GUI函庫做動畫(如Windows API、Java Swing),就要自己建立一個點陣圖物件做為double buffer。



—Vsync—

顯示記憶體裡的資料有變化時,螢幕其實不會立刻把它秀出來,而是每隔一段時間讀取顯示記憶體的資料更新一次,大部分螢幕是每秒更新60次。
上面說到另一種定時方法是vsync:讓程式暫停,等到下一次螢幕更新才繼續執行,這樣程式就自然以60FPS執行,不需要計時器定時。


D3D和OpenGL有提供功能,呼叫一個函式就可以自動偵測螢幕更新的時間並等待。
//D3D11
//swapChain型態為IDXGISwapChain*
//第一參數填1代表要用vsync

swapChain->Present(1, 0);
//OpenGL, Windows
//先用這個函式設定

wglSwapIntervalEXT(1);

//之後swap buffer時就會使用vsync
SwapBuffers(hdc);
//OpenGL, X Window
//先用這兩個函式設定
//哪個函式有效每台電腦不一樣,兩個都呼叫比較保險

glXSwapIntervalSGI(1);
glXSwapIntervalMESA(1);

//之後swap buffer時就會使用vsync
glXSwapBuffers(display, window);

vsync(vertical synchronization)字面意思是「垂直同步」,名稱來自以前的CRT(陰極射線管)顯示器。CRT的顯示方式是將電子束射向螢幕,從上方開始第一列、第二列、第三列往下逐列掃瞄,同步的方法是等待這個垂直方向的更新。
現在的液晶顯示器已經不是用這種方法更新畫面,但vsync的名稱還是繼續使用。

螢幕做成更新率60Hz是因為這是人眼能辨識的極限,超過這個頻率人眼就分辨不出是60、80、還是120了,因此個人認為把程式寫成一秒更新畫面超過60次用處也不大,不如把效能省下來用在其他地方。
較高頻率的物理計算有時候有用處,可以做成物理計算120Hz,畫面60Hz,即兩次物理計算才畫一次物體。



關於插圖

這個角度繪製難度很高。

不過想進步就要挑戰各種難關,一直練習相同的事功力是不會提升的。

這個角色在二代會登場,這一作除了程式語言以外還有其他角色登場。


最近工作比較忙,更新慢了一些。
但依照先前的經驗,辭職在家裡做進度並不會比較快,有工作反而比較不會偷懶。
引用網址:https://home.gamer.com.tw/TrackBack.php?sn=4469237
All rights reserved. 版權所有,保留一切權利

相關創作

同標籤作品搜尋:程式|遊戲製作

留言共 5 篇留言

出包小太陽
........................

07-23 02:47

駭靈大君
這麼營養的文章~只能GP了~

07-23 09:37

stormcorn
已推文,讚

07-23 11:13

微笑的貘
簡單地看了一下你的小屋,
請問你是打算開發遊戲引擎嗎?
從 openGL 這層開始寫?

07-23 11:47

Shark
其實已經用自製引擎完成過兩個遊戲了
-Monster Musume Hunter
-Cyber Sprite
所以引擎是實證過能用的。

是從作業系統API和D3D、OpenGL開始寫。07-24 20:10
微笑的貘
太厲害了!
是有什麼特別的需求促使你開發遊戲引擎?
還是興趣使然

07-24 21:37

Shark
一開始是興趣使然,試用順序是Java → Allegro(一個C函式庫) → D3D和OpenGL。
之後試了幾個別人的引擎,發現自製引擎有編譯快、效能好、容易debug的好處,就決定不換引擎。

此外因為在公司有寫Linux程式的經驗,作品除了Windows以外順便做了Linux版。07-25 01:05
我要留言提醒:您尚未登入,請先登入再留言

20喜歡★shark0r 可決定是否刪除您的留言,請勿發表違反站規文字。

前一篇:【進度】挑戰Blende... 後一篇:FF34遊戲攤位一覽...

追蹤私訊

作品資料夾

momo697諸位阿彌陀佛
請您拯救世界吧 交給您了看更多我要大聲說昨天10:05


face基於日前微軟官方表示 Internet Explorer 不再支援新的網路標準,可能無法使用新的應用程式來呈現網站內容,在瀏覽器支援度及網站安全性的雙重考量下,為了讓巴友們有更好的使用體驗,巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁面呈現和功能。
屆時建議您使用下述瀏覽器來瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業系統版本才可使用)

face我們了解您不想看到廣告的心情⋯ 若您願意支持巴哈姆特永續經營,請將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學】