2011-03-07 72 views
11

我正在爲自己的樂趣和培訓而設計一款小遊戲。遊戲的真實身份與我的實際問題毫不相干,假設它是Mastermind遊戲(它實際上是:)控制檯和GUI的通用設計

我在這裏的真正目標是有一個接口IPlayer它將用於任何玩家:計算機或人,控制檯或gui,本地或網絡。我也打算有一個GameController,它只處理兩個IPlayer

的IP層接口會是這個樣子:

class IPlayer 
{ 
public: 
    //dtor 
    virtual ~IPlayer() 
    { 
    } 
    //call this function before the game starts. In subclasses, 
    //the overriders can, for example, generate and store the combination. 
    virtual void PrepareForNewGame() = 0; 
    //make the current guess 
    virtual Combination MakeAGuess() = 0; 
    //return false if lie is detected. 
    virtual bool ProcessResult(Combination const &, Result const &) = 0; 
    //Answer to opponent's guess 
    virtual Result AnswerToOpponentsGuess(Combination const&) = 0; 
}; 

的GameController類會做這樣的事情:

IPlayer* pPlayer1 = PlayerFactory::CreateHumanPlayer(); 
IPlayer* pPlayer1 = PlayerFactory::CreateCPUPlayer(); 

pPlayer1->PrepareForNewGame(); 
pPlayer2->PrepareForNewGame(); 

while(no_winner) 
{ 
    Guess g = pPlayer1->MakeAguess(); 
    Result r = pPlayer2->AnswerToOpponentsGuess(g); 
    bool player2HasLied = ! pPlayer1->ProcessResult(g, r); 
    etc. 
    etc. 
} 

通過這種設計,我願意做GameController類不變,即是,我在其中填充了正義的遊戲規則,沒有其他任何東西,所以既然遊戲本身已經建立,那麼這個類就不應該改變。對於遊戲機遊戲來說,這種設計可以完美運行我會HumanPlayer,在其MakeAGuess方法會從標準輸入讀取CombinationCPUPlayer,這會在一定程度隨機生成它等

現在,這裏是我的問題:IPlayer接口,隨着GameController類,在本質上是同步的。我無法想象當GUIHumanPlayerMakeAGuess方法必須等待(例如,某些鼠標移動和點擊)時,我將如何實現具有相同GameController的遊戲的GUI變體。當然,我可以啓動一個等待用戶輸入的新線程,而主線程會阻塞,以模仿同步IO,但不知何故,這個想法讓我感到厭惡。或者,也可以將控制器和播放器設計爲異步。在這種情況下,對於控制檯遊戲,我必須模仿異步,這比第一個版本更容易。

您是否對我的設計和我對選擇同步或異步設計的擔憂發表評論?另外,我感覺我比GameController類對玩家類承擔了更多的責任。等等。

非常感謝您提前。

P.S.我不喜歡我的問題的標題。隨意編輯:)

+7

我喜歡標題:-) – 2011-03-07 12:33:36

+1

我刪除了C#標記,因爲在這個問題附近沒有C#任何地方。 – Puppy 2011-03-07 12:46:40

+1

由於語言無關緊要,我希望得到C#專家的意見 – 2011-03-07 14:36:17

回答

11

而不是使用的各種IPlayer方法的返回值,考慮引入一個觀察者類IPlayer對象,像這樣:然後

class IPlayerObserver 
{ 
public: 
    virtual ~IPlayerObserver() { } 
    virtual void guessMade(Combination c) = 0; 
    // ... 
}; 

class IPlayer 
{ 
public: 
    virtual ~IPlayer() { } 
    virtual void setObserver(IPlayerObserver *observer) = 0; 
    // ... 
}; 

IPlayer方法應該調用相應的已安裝IPlayerObserver的方法,而不是返回一個值,如:

void HumanPlayer::makeAGuess() { 
    // get input from human 
    Combination c; 
    c = ...; 
    m_observer->guessMade(c); 
} 

你GameController類可以然後實現IPlayerObserver,使其只要玩家做了一些有趣的事情就會得到通知,比如 - 猜測。

有了這個設計,如果所有的IPlayer方法都是異步的,那就沒問題了。事實上,這是可以預料的 - 它們全都返回void!您的遊戲控制器在活動播放器上調用makeAGuess(這可能會立即計算結果,或者它可能爲多人遊戲執行一些網絡IO,或者等待GUI執行某些操作),並且每當玩家選擇時,遊戲控制器可以放心,guessMade方法會被調用。而且,玩家對象對遊戲控制器還是一無所知。他們只是處理一個不透明的'IPlayerObserver'界面。

1

與控制檯相比,GUI的獨特之處在於GUI是事件驅動的。這些事件發生在GUI線程上,因此,如果您在GUI線程中託管遊戲代碼,則會出現問題:您的呼叫讓玩家進行阻止GUI線程,這意味着您無法獲取在該呼叫返回之前的任何事件。 [編輯:插入下面的句子。]但是電話不能返回,直到它得到事件。所以你陷入僵局。

如果您只是將遊戲代碼託管在另一個線程上,那麼問題就會消失。你仍然需要同步這些線程,所以MakeAGuess()在準備好之前不會返回,但它肯定是可行的。

如果你想保持單線程的一切,你可能需要考慮一個不同的模型。遊戲可以通過一個事件通知玩家輪到他們,但讓玩家在遊戲中發起操作。