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變量追蹤方向。當咱們執行完代碼,執行位置丟失了,因此咱們不得不顯式存儲足夠多的數據以便下一幀恢復。