2010-02-09 42 views
21

我正在設計一個簡單的遊戲,它使用Java 2D和牛頓物理。目前,我的主要「遊戲圈」看起來像:面向對象方式的遊戲設計

do { 
    for (GameEntity entity : entities) { 
    entity.update(gameContext); 
    } 

    for (Drawable drawable : drawables) { 
    drawable.draw(graphics2d); 
    } 
} while (gameRunning); 

當實體被指示自行更新,將根據當前的力量應用到它調整它的速度和位置。但是,我需要實體展現其他行爲;例如如果一個「壞人」被玩家擊中,該實體應該被銷燬並從遊戲世界中移除。

我的問題:以面向對象的方式達到此目的的最佳方法是什麼?到目前爲止,我見過的所有例子都將遊戲循環合併到一個名爲Game的God類中,該類執行以下步驟:檢測碰撞,檢查是否壞人死亡,檢查是否玩家死亡,重新繪製等封裝了所有的遊戲狀態(剩餘生命等)。換句話說,這是非常程序所有的邏輯是在遊戲類。任何人都可以推薦更好的方法?

這裏是我想過的,到目前爲止的選項:

  • 傳遞一個GameContext從中如果需要,實體可以去除自身或更新的遊戲狀態的每個實體(例如,「不跑」,如果玩家被殺死)。
  • 註冊每個GameEntity作爲聽衆的中心Game類和採取事件導向的方法;例如碰撞會導致CollisionEvent被髮射給碰撞中的兩名參與者。
+2

我想我發現你的錯誤 - 這是'而(gameRunning)'你想有 –

回答

14

我有兩個商業遊戲引擎密切合作,他們遵循類似的模式:

  • 對象代表遊戲實體的組成部分或方面(如物理,渲染,等等),而不是整個實體。對於每種類型的組件,都有一個巨大的組件列表,每個組件都有一個實體實例。

  • '遊戲實體'類型本身只是一個唯一的ID。每個巨大的組件列表都有一個映射來查找與實體ID相對應的組件(如果存在)。

  • 如果組件需要更新,它將由服務或系統對象調用。每個服務都直接從遊戲循環更新。或者,您可以從調度程序對象調用服務,該調度程序對象從依賴關係圖中確定更新順序。

下面是這種方法的優點是:

  • 您可以自由而無需編寫新的類,每 組合或使用複雜的繼承 樹木相結合的功能 。

  • 幾乎不存在的功能, 你可以假設所有遊戲 實體,你可以把遊戲中的 實體基類(什麼呢光 有共同與賽車或 天空盒?)

  • 的ID與部件的外觀起坐似乎 昂貴,但服務是由 做 最密集的工作,通過所有部件的特定類型的 迭代。在這些情況下, 它可以更好地將您需要的所有數據 存儲在一個整齊的列表中。

+0

@Evan:謝謝你的回答。我打算問你:在這種情況下你將如何處理一個實體的移除?例如,假設您有一個Collidable方面,並且您的CollisionManager檢測到一個衝突,這會導致該實體被移除。據推測,CollisionManager僅引用實體的Collidable方面,那麼您會採取什麼方法從各個列表中刪除實體的所有**方面(可繪製,可碰撞等)? – Adamski

+1

我沒有提到的缺失部分是消息或事件。每個系統可以訂閱任何消息類型或發佈消息。物理系統可能會發布遊戲系統可能訂閱的「碰撞」消息,並可能發佈「刪除實體」消息作爲迴應。另一個負責創建和刪除實體的系統可能會訂閱刪除實體的消息類型。這比直接函數調用要多得多,但它都是以解耦爲名。 –

6

在我工作的一個特定引擎中,我們將邏輯從圖形表示中分離出來,然後將對象發送給他們想要做的事情。我們這樣做是爲了讓我們可以在本地機器上存在遊戲或聯網,並且從代碼的角度來看,它們彼此不可區分。 (命令模式)

我們還在一個單獨的對象中完成了實際的物理建模,可以在運行中對其進行更改。這讓我們很容易陷入重力等等。

我們大量使用事件驅動代碼(偵聽器模式)以及許多定時器。

例如,我們有一個可以偵聽碰撞事件的可交叉對象的基類。我們將其劃分爲健康箱。碰撞時,如果它被一個玩家實體擊中,它會發送一個命令給它應該獲得健康的對撞機,發送一條消息向所有可以聽到它的聲音廣播一個聲音,停用碰撞,激活動畫以移除圖形場景圖形,並設置一個計時器以便稍後重新實施。這聽起來很複雜,但事實並非如此。

如果我記得(已經有12年了),我們有抽象的場景概念,所以遊戲是一系列場景。場景完成後,會觸發一個事件,通常會發出一個命令,取消當前場景並啓動另一個場景。

+0

謝謝 - 這裏有一些很好的建議。 – Adamski

+0

在這裏使用中介模式來促進事件調用/註冊/處理和數據可能是一個好主意,否則你將不得不將所有子系統和遊戲實體耦合到彼此。 – dvide

3

我不同意,因爲你有一個主要的遊戲類,所有的邏輯必須在該類中發生。

過於簡單化這裏模仿你的例子只是爲了讓我的觀點:

mainloop: 
    moveEntities() 
    resolveCollisions() [objects may "disappear"/explode here] 
    drawEntities()  [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself] 
    cleanDeadEntities() 

現在你有一個氣泡類別:

Bubble implements Drawable { 

handle(Needle needle) { 
    if (needle collide with us) { 
     exploded = true; 
    } 
} 

draw (...) { 
    if (!exploded) { 
     draw(); 
    } 
    } 
} 

所以,當然,有一個主循環需要的護理在實體之間傳遞消息,但與Bubble和Needle之間的碰撞相關的邏輯絕對不在主遊戲類中。

我很確定,即使在你的情況下,與運動有關的所有邏輯都不會發生在主類中。

所以我不同意你的陳述,你用粗體寫出,「所有的邏輯都發生在主類中」。

這根本不正確。至於優秀的設計:如果你可以很容易地提供你的遊戲的另一個「視圖」(比如說,一個迷你地圖),並且如果你可以很容易地編寫一個「幀到幀的完美播放器」,那麼你的設計可能並不是那麼糟糕(即:只記錄輸入和發生的時間,你應該能夠完全按照它們的播放方式重新創建遊戲,這就是帝國時代,魔獸爭霸3等等。重放:只有用戶的輸入和他們發生的時間被記錄[這也是重播文件通常非常小的原因])。

+0

謝謝。我並不主張完全消除遊戲循環,只是儘可能簡單地保持遊戲循環,所以你所說的話是有道理的。 – Adamski

1

這個game是保持模型和視圖分離的實驗。它使用觀察者模式來通知遊戲狀態變化的視圖,但事件可能會提供更豐富的上下文。最初,該模型是由鍵盤輸入驅動的,但分離使得添加定時器驅動的動畫變得很容易。

附錄:您需要將遊戲的模型分開,但您可以根據需要將該模型重新分爲多個類。

2

我寫我自己的引擎(原始&骯髒),但有一個體面的OO模型的預建引擎是Ogre。我建議看看它(它是對象模型/ API)。節點分配有點時髦,但它看起來越多就越有意義。它也有很多有效的遊戲例子。

我從中學到了一些技巧。

+0

偉大的東西 - 將檢查出來。 – Adamski