2011-12-04 80 views
4

我目前正在製作一個C++遊戲。 我有我的主循環,其間的邏輯計算,然後畫的精靈等 我想實現「對話框窗口」:當你按下一個NPC前面的觸摸,彈出一個對話窗口的底部屏幕上,你被凍結,直到你按下一個鍵,但遊戲繼續運行(其他角色正在移動等),所以主循環仍在運行。 我設法做到這一點就好了:當一個對象被激活,它將發送一個消息,而本場比賽繼續運行時顯示的文本窗口對話管理器,所以它看起來像:無模式對話框窗口的優秀設計是什麼?

object::OnActivated() { 
GameManager::doWindow("some text"); 
//the game is not blocked here, the game continues to run normally and will display a window on the next frame 
} 

現在問題就來了當我想要在對話結束後發生某些關聯操作時,或者例如如果您對某個問題選擇了「是」時。我想有一個會像這樣工作的落實:

object::OnActivated() { 
GameManager::doWindow("some text"); 
if(GameManager::Accepted()) addGold(100); 
} 

的問題是,這種檢查&行動將盡快在窗口創建活動進行,而不是在關閉窗口/ acepted 。有沒有辦法做到這一點,同時保持OnActivated()函數中的相關操作?我不知道如何正確地做到這一點,而不使用函數指針,這會迫使我對每個我可以用作結果的方法都有一個特定的簽名。 謝謝

編輯:我發佈了一個賞金,因爲我想知道什麼是對這個問題最「典型」的答案。我想這是一個非常普遍的問題(對於很多應用程序和每個現代遊戲),我希望有一個儘可能靈活的解決方案,因爲我不能從今天列出所有可能的「後果」 「對話可能觸發。 更多信息: - 每個對話框將由來自共同「實體」類的對象觸發 - 來自同一類的不同對象幾乎總會有不同的對話/動作關聯(例如,所有NPC對象會沒有相同的對話框) - 我不關心如何將「對話邏輯」移出OnActivated方法,甚至不在Entity類之外。無論如何,這會發生,因爲我希望能夠爲每個NPC添加「隨機」對話框場景,所以對話框等將存儲在別處 - 但我想保持對話框邏輯本身儘可能接近一個對話框。理想情況下,我希望能夠做到像這樣:「result = dialogWindow(」question?「); if(result){...}」。我不確定這是可能的,但

+0

這屬於GameDev.SE,我將其標記爲移動。 – Xeo

+1

@Xeo:不是。他只是想讓相當於無模式的對話窗口,它並不重要,它是用於遊戲。 – Jon

回答

1

Command Pattern用於封裝稍後要執行的代碼。

在你的情況下,object::OnActivated()函數會創建一個合適的命令對象並將其存儲以便稍後查找。當用戶選擇是/否時,可以運行該命令而不需要代碼知道哪個特定命令對象恰好在那裏。

下面是一個「加金」的命令對象的示例:

class DialogResponseCommand 
{ 
public: 
    virtual run() = 0; 
}; 

class AddGoldCommand : public DialogResponseCommand 
{ 
public: 
    AddGoldCommand(int amount) : amount(amount) {} 
    virtual run() 
    { 
    if(GameManager::Accepted()) 
     addGold(amount); 
    } 

private: 
    int amount; 
}; 

現在,由於一些存儲爲即將到來的命令:

shared_ptr<DialogResponseCommand> dialog_command; 

你可以有你的OnActivated()創建命令:

object::OnActivated() { 
    GameManager::doWindow("some text"); 
    dialog_command = make_shared<AddGoldCommand>(100); 
} 

而當用戶最終做出選擇時:

dialog_command->run(); 
+0

我選擇了這個答案,因爲我正在尋找一個現有的和經過驗證的設計模式(因爲這是一個相當常見的問題,我試圖解決)。謝謝大家 – lezebulon

2

您可以創建一個事件類,根據您的需要進行一些預定義的操作。它將有一個實例變量,其中包含一個枚舉值,如EVENT_ADD_GOLD。它也會有一個Perform函數來檢查實例變量並執行適當的操作。其他行動可以根據需要添加。

關於這一點的好處是,每種類型只需要一個實例變量。例如value可能指黃金或損壞的數量。含義由事件類型決定。

在代碼中,「我們不需要再顯示對話框!」您可以撥打Event對象的Perform方法。因爲在任何時候都有一個以上的事件是沒有意義的,所以你可以讓一個實例變量保存一個引用。

+0

這是我最可能實現的解決方案,但這意味着我需要爲每個可以由對話框觸發的「動作」創建一個獨特的事件類型,並且我猜不可能在「generic 「的方式(即某些事件需要2個整數,有些需要對某個對象進行引用等) – lezebulon

+0

您可能會重載構造函數。 ([這個頁面的一半爲構造函數重載](http://www.cplusplus.com/doc/tutorial/classes/)) – FakeRainBrigand

3

很難給出具體的答案,因爲您沒有指定(或標記)該平臺的用途,所以我會寫一個通用答案。

的回答你的問題:

「?有沒有辦法做到這一點,同時保持相關措施的OnActivated()函數」

很可能是「否」

有一個嘗試過和真正的模式家族來解決你所描述的問題。這個模式族是各種Model-View-XXX模式(MVC,MVP,文檔視圖等) 這些模式的基本前提是有一個構造,通常是對象的圖形,封裝了當前狀態(The Model)和一組用戶界面元素(The Views),它們向用戶顯示這個狀態。 每當模型更改視圖更改以匹配新狀態。模型更改和視圖更新的細節設置了家庭中不同的模式,以及使用哪種模式取決於特定系統如何處理輸入的細節。 MVC是互聯網應用和基於循環的許多遊戲的良好匹配,因爲用戶輸入只有一個入口點。 MVP,DV和MVVM(有人說這與MVP相同)更適合桌面應用程序,其中輸入到GUI中的主動控件。

使用這些模式的缺點是用於創建視圖的代碼很少伴隨着相關操作的代碼,但其好處遠遠超過這個缺點。

在你的情況下,你的模型應該有對話框文本的屬性和一個存儲當前輸入處理器(狀態模式)的屬性。 你的主循環將執行以下操作:

  1. 獲取當前輸入處理程序更新基於用戶輸入的模型,如果有的話(例如更改用戶精靈的位置)。
  2. 更新模型的其餘部分以反映遊戲中的其他元素。
  3. 更新基於當前模型

時,在界面中的NPC前面的用戶按下,缺省輸入處理變化來處理輸入的具體對話觸發和通用視圖爲對話框將文本顯示給用戶。

當用戶在對話框中選擇操作時,處理程序將恢復爲默認輸入處理程序,並且對話框的屬性返回爲空。

步驟1和2構成MVC模式中的Controller,步驟3是非事件驅動的視圖更新;相反,您可以使用Observable-Observer模式,並讓模型拋出相應更改的視圖觀察到的事件。

+0

感謝您對MVC的解釋,但這並不能解釋如何(如果可能的話) )一旦對話框關閉,我可以觸發「通用」事件 – lezebulon

1

我想你在這裏失蹤的是回調。解決您的問題的方法是在onActivated方法中提供回調。當無模式對話框被接受時,對話管理器將調用這個回調函數或方法,那就是你可以執行你想要的行爲的地方。

你沒有提供足夠的遊戲細節,所以我不能給你一個明確的方法來解決這個問題。如果對於任何給定的對象,你總是會需要相同的動作,那麼你可以簡單地提供一個方法OnAccepted。是這樣的:

object::OnActivated() { 
    GameManager::doWindow(this, "some text"); // note I'm passing the object to the dialog manager 
} 

// the dialog manager calls this when the dialog box is accepted 
void object::OnAccepted() { 
    addGold(100); 
} 

上面假定所有表示對象的類屬於同一層次結構,使得OnAccepted方法可以被聲明爲在基類的虛擬功能。

如果這是一個過於簡單化的方法,可以使它更加詳細,但它總是會有一些回調數據傳遞給doWindow方法,對話管理器可以用來在適當的時候觸發回調。

如果你需要一些非常複雜的東西,並且可以訪問boost或者具有std::functionstd::bind的C++ 11實現,那麼你甚至可以支持具有任意參數的回調。這個想法是,傳遞給doWindow的參數是一個function對象。該對象可以包裝一個常規函數或某個對象內的方法,如果有其他參數,則可以使用std::bind將它們綁定到函數對象中。

相關問題