《遊戲程序設計模式》 2.3 - 更新方法

intent
oop

    一次通知一堆獨立對象處理一幀的行爲
編碼

motivationspa

    玩家控制的角色正在執行一個任務,任務內容是偷取死了好久的巫師王骸骨上的珠寶。它猶豫地靠近地穴的入口。詛咒的雕塑並無向他雷電攻擊。沒有不死士衛巡邏門口。它直接走進去,拿到戰利品,遊戲結束。你贏了。
設計

    好吧。那永遠不會出現。
code

    這個地穴須要幾個守衛-咱們的英雄能夠與之搏鬥的敵人。首先,咱們須要一個守衛在門口巡邏。若是你忽略一些細節,最簡單的實現守衛來回巡邏的代碼可能就像這樣:
對象

while (true)
{
  // Patrol right.
  for (double x = 0; x < 100; x++)
  {
    skeleton.setX(x);
  }
  // Patrol left.
  for (double x = 100; x > 0; x--)
  {
    skeleton.setX(x);
  }
}

    問題是,雖然角色左右移動了,可是玩家看不到。程序被卡在無限循環中,這不是個好的體驗。咱們真正想要的是每一幀移動一步。
遊戲

    咱們必須移除那些循環,並使用外部循環。這保證遊戲在守衛巡邏時,依然能夠響應輸入,能夠渲染。像:
事務

Entity skeleton;
bool patrollingLeft = false;
double x = 0;
// Main game loop:
while (true)
{
  if (patrollingLeft)
  {
    x--;
    if (x == 0) patrollingLeft = false;
  }
  else
  {
    x++;
    if (x == 100) patrollingLeft = true;
  }
  skeleton.setX(x);
  // Handle user input and render game...
}

    後一種比前一種代碼更復雜。左右巡邏上面是兩個簡單的for循環。如今咱們使用外部循環,每一幀都從離開的位置從新開始,並使用patrollingLeft標記方向。
ip

    可是這個大約能夠工做,咱們繼續。這些骷髏守衛沒什麼更多動做,接下來咱們添加魔法塑像。他們射出箭狀閃電,阻擋角色。
input

    繼續咱們的「最簡單的實現方式」,像:

// Skeleton variables...
Entity leftStatue;
Entity rightStatue;
int leftStatueFrames = 0;
int rightStatueFrames = 0;
// Main game loop:
while (true)
{
  // Skeleton code...
  if (++leftStatueFrames == 90)
  {
    leftStatueFrames = 0;
    leftStatue.shootLightning();
  }
  if (++rightStatueFrames == 80)
  {
    rightStatueFrames = 0;
    rightStatue.shootLightning();
  }
  // Handle user input and render game...
}

    你能夠分辨出這不是咱們喜歡維護的代碼。咱們會將每個遊戲實體的大量變量和必要的代碼放在遊戲循環中。爲了使他們一塊兒工做,咱們把代碼攪在一塊兒。

    解決這個模式很是簡單,可能你已經知道了:每個實體應該封裝本身的行爲。這樣可使遊戲循環代碼整潔,並且很容易添加和刪除實體。

    爲此,咱們須要一個抽象層,經過定義一個update方法建立它。遊戲循環維護一個對象的集合,可是不知道具體的類型。它知道的是它們都能update。這使得每一個對象的行爲與遊戲循環、其它對象的行爲分離開來。

    每一幀,遊戲循環遍歷對象調用update。經過調用update,全部對象同時表現行爲。

    遊戲循環有一個動態對象集合,因此添加和刪除都是很簡單的-就是直接從集合中添加和刪除便可。再也不有硬編碼了,咱們甚至可使用文件來填充關卡,這正是關卡設計師想要的。

the pattern

    遊戲世界維護一個對象的集合。每個對象都實現了update方法,模擬一幀的行爲。每一幀,遊戲循環更新每個對象。

when to use it

    若是把Game Loop比做麪包,那麼update就是黃油。與玩家交互的至關多的遊戲對象採用這個模式或類似的模式。若是一個遊戲有太空陸戰隊,龍,火星人,鬼,還有運動員,那麼這是使用這個模式的好機會。

    然而,若是一個遊戲更加抽象,遊戲對象不怎麼活動更像是棋盤的棋子,那麼這個模式不適合。像象棋這種遊戲,沒必要模擬全部的棋子,甚至沒必要每一幀都更新一個卒子。

    更新方法當這些狀況時好用:

  • 當遊戲同時運行大量對象或系統時。

  • 每個對象的行爲都是獨立的。

  • 對象行爲隨時間流逝來模擬

keep in mind

    這個模式至關簡單,沒有什麼使人驚訝的內容。

    可是,每一行代碼都有衍生版本。

 sliptting code into single frame slices makes it more complex

    當你比較前兩段代碼,你會發現第二段更復雜。都是簡單地使守衛左右巡邏,可是後者把這個過程分散到每一幀進行。

    這個修改是必須的,由於要處理用戶輸入,渲染還有其餘的事務,因此第一個不實際。可是記住將代碼分散會增長很大的複雜性。

you have to store state to resume where you left off each frame

    在第一個例子中,咱們並無一個變量表示向左向右。它隱式取決於哪段代碼正在執行。

    當咱們把它改爲「一幀一次」的形式,咱們不得不建立一個patrolLeft變量追蹤方向。當咱們執行完代碼,執行位置丟失了,因此咱們不得不顯式存儲足夠多的數據以便下一幀恢復。

相關文章
相關標籤/搜索