2012-10-14 27 views
2

我正在爲我創建的遊戲設計一個新的啓動系統。這是一個側面卷軸,強力ups表現爲圓形物體,玩家必須通過觸摸/移動才能獲得力量。然後啓動,然後幾秒鐘後關閉。每次啓動都有自己的持續時間。爲了簡單起見,每隔X秒產生一次電源UPS(放置在屏幕上)。我在這裏違反OOP設計指南嗎?幾個有趣的設計泡菜

我創建了一個PowerUpManager,一個單身人員,他的工作是決定何時創建新的電源UPS,然後放置它們的位置。

然後我創建了Powerup基類,併爲每個新的Powerup從該基類繼承了一個類。每次開機都可以處於以下三種狀態之一:禁用,放在屏幕上,然後由播放器拾取。如果玩家沒有接通電源但繼續運行,加電將退出屏幕,並應從放置狀態返回到禁用狀態,以便再次放置。

我要求的一個要求是,當我編寫一個新的Power up類時,應該有最小的代碼更改。我能做的最好的是一個代碼片段:本PowerUpManager的構造函數,必須在其中添加新的動力,到到保存所有電源UPS的容器:

PowerupManager::PowerupManager() 
{ 
    available = { 
     new PowerupSpeed(), 
     new PowerupAltWeapon(), 
     ... 
    }; 
} 

的PowerUpManager,在更多的細節(問題即將到來!): 包含指向PowerUp(基類)的指針向量,稱爲available。這是最初的容器,每個動力在遊戲中都有一個副本。 爲了處理不同的狀態,它有兩個列表:一個保存指向當前放置的電源指針的指針,另一個列表保存指向當前活動電源指針的指針。 它還有一個方法,每個遊戲都會調用tick來決定是否以及在何處放置新的電源,並清理未拾取的電源ups。最後,它有一個方法,當玩家上電時啓動,激活開機(將其從放置位置移動到活動列表,並調用開機的激活方法)。

最後,一旦你理解了全圖,問題: 我需要一種方法讓客戶端代碼詢問特定的上電是否當前處於活動狀態。例如:玩家有一把武器,但是有一種能力可以暫時取代武器。在我輪詢輸入並認識到玩家想要發射他的武器的地方,我需要調用正確的射擊方法 - 替代武器加強射擊方法,而不是常規武器射擊方法。

我想了一會兒這個特殊的需求,並與此想出了:

template <typename T> 
T* isActivated() // Returns a pointer to the derived Powerup if it exists in the activated list, or nullptr if it doesn't 
{ 
    for(Powerup *i : active) // Active is a list of currently active power ups 
    { 
     T *result = dynamic_cast<T*>(i); 

     if(result) 
      return result; 
    } 

    return nullptr; 
} 

所以客戶端的代碼如下所示:

PowerUpAltWeapon *weapon = powerUpManager->isActivated<PowerUpAltWeapon>(); 
if(weapon) 
    ... 

我認爲解決的辦法是優雅和親切的整潔,但基本上它是試圖將基類型轉換爲派生類型。如果這不起作用,你可以嘗試下一個派生類型......一個if/else的長鏈,如果它只是僞裝成一個循環。這是否違反了我剛剛描述的準則?在if/else的長鏈中,如果直到遇到命中,不會將基類型轉換爲其所有派生類型?還有其他解決方案嗎?

第二個問題是:有沒有辦法擺脫在PowerupManager構造函數中構造所有不同電源的需求?如果您想引入新的功能,那麼這是目前唯一需要進行更改的地方。如果我能擺脫這一點,那會很有趣......

回答

1

這是基於您的設計,但如果是我,我會爲每個PowerUp和一組ID在客戶端中選擇一個ID,並且每次用戶擁有一個PowerUp時,該ID將被添加到其集合中。 ..你知道其餘的。使用這種技術,我可以做快速查找每一個通電和避免dynamic_cast

std::set<PowerUp::ID> my_powerUps; 
template< class T > bool isActivated() { 
    return my_powerUps.find(T::id()) != my_powerUps.end(); 
} 

而關於你的第二個問題,我有一個類似的程序,加載一些插件,而不是PowerUp,我有一個純虛基類,包含該插件所需的所有方法並在共享模塊中實現,然後在啓動時從特定文件夾加載它們。例如每個共享模塊包含一個create_object返回一個plugin*(你的情況,當然PowerUp*),然後我重複的文件夾,加載模塊和調用create_object從他們創造我的插件和我plugin_manager

+0

好一個註冊。我甚至可以自動生成原始電源UPS容器中每次啓動索引的ID。當然,我仍然想對我的第二個問題有些想法。如果C++具有先天反射,那麼我可以運行代碼來掃描Power ups文件夾,從.h文件獲取類名並構建所有這些類。 –

+0

那麼你可能會在某個時候初始化你的遊戲。爲什麼不給你的經理提供允許註冊的方法?然後添加一個使用基類的上電只需要添加一個新類型並在初始化時註冊它。 – Dan

+1

不,你可以定義一個純虛擬基類,它包含所有PowerUp對象使用的所有操作,然後在共享模塊('.DLL'或'.so')中實現PowerUps,然後加載他們動態地,並從該模塊創建它們 – BigBoss