Delta3D———通过游戏管理器组件和消息的扩展创建自定义行为 《转》
游戲管理器組件給我們提供了在不修改游戲管理器的情況下靈活擴展我們的自定義行為的能力。游戲管理器組件是基于消息來工作的,定義自定義行為的基本 流程就是創建自定義類型的消息,在合適的時候發送消息,創建自定義游戲管理組件并重寫自己的消息處理。將自定義組件添加到游戲管理器,游戲管理器會自動將 消息發送給游戲管理器組件(調用組件的消息處理函數)。下面我們首先來結合實例分析該過程的具體流程及其使用方法。
添加自定義行為有兩種方式,一種是不定義自己的消息類型和消息,直接用現有的消息類型和消息對象,只是擴展自己的游戲管理器組件,也就是直接對現有 消息擴展消息處理??傊@兩種方式都少不了要定義自己的游戲管理器組件。那么我們就先來看看如果定義自己的游戲管理器組件并使其工作。后續我們會介紹如何 擴展自己的消息類型以及消息對象使得我們可以更為靈活的添加自己的游戲行為,該過程可以參考testAAR例子。
首先我們來看游戲管理器組件,該類有四個虛函數是非常重要的,那就是virtual void DispatchNetworkMessage(const Message& message)和virtual voidProcessMessage(const Message& message)以及virtual voidOnAddedToGM();和virtual void OnRemovedFromGM();我們在定義自己的組件時,至少會重寫它們中的一個,第一個是處理網絡消息,第二個是處理本地消息。有一點要特別注 意,前兩個函數都接受一個Message對象作為輸入參數。添加自己的行為,只需要根據具體情況重寫這四個函數即可。最簡單的兩個例子就是 dtGame:: DefaultMessageProcessor類和dtGame::BaseInputComponent類,在我們擴展自己的行為時可以參考。擴展了 自己的游戲管理器組件類后,我們就可以創建組件對象并將組件對象添加到游戲管理器對象中,具體就是調用dtGame::GameManager的 voidAddComponent(GMComponent& component, const ComponentPriority& priority= ComponentPriority::NORMAL)函數。
???????? 好了,上面就是簡單的如何擴展自己的游戲管理器組件并讓其起作用的簡單描述。下面我們看看它是具體如何工作的,也就是這幾個函數是在哪調用的。通過 AddComponent函數的實現代碼可以看到OnAddedToGM就是在這里調用的,OnRemovedFromGM是在 RemoveComponent中調用的,那么DispatchNetworkMessage和ProcessMessage呢?我來告訴你,是在 void GameManager::DoSendMessageToComponents(const Message&message, bool toNetwork)中調用的,在該函數中會遍歷GameManager的所有組件,同時我們可以看到如下代碼:
if (toNetwork)
??????????? {
??????????????component->DispatchNetworkMessage(message);
??????????? }
??????????? else
??????????? {
??????????????component->ProcessMessage(message);
??????????? }
這里就是我們說的不需要修改游戲管理器就能添加自定義行為的過程的關鍵所在,但是還沒有到頭兒,我們繼續向上追溯,我們發現void GameManager::DoSendNetworkMessages()
和void GameManager::DoSendMessage(const Message& message)
中調用了上面的函數。繼續追蹤
void GameManager::DoSendMessage(constMessage& message)
發現
void GameManager::DoSendMessage()
中調用了
void GameManager::DoSendMessage(constMessage& message),
繼續追蹤發現
void GameManager::PreFrame(doubledeltaSimTime, double deltaRealTime)
中調用了void GameManager::DoSendNetworkMessages()
和
void GameManager::DoSendMessage(),
參數中的消息從哪來的?就是在這兩個函數中遍歷消息隊列從消息隊列獲取來的。你還沒感覺到已經到了源頭嗎?那我們接著跟蹤,你會發現最終是在
void GameManager::OnMessage(MessageData*data)
中又調用了
void GameManager::PreFrame(doubledeltaSimTime, double deltaRealTime)。
到這里我們應該明白了整個流程,System觸發
void GameManager::OnMessage(MessageData*data)
后引起了這一連串動作。這個過程已經明確說明了游戲管理器和游戲管理器組件是如何工作的了。我們說到這里,你應該能夠通過擴展自己的游戲管理器組件添加自己的行為了。
???????? 但在應用開發中,情況往往會更復雜,針對不同的應用情況,系統自帶的消息類型以及消息對象不能滿足需要,這時候我們不但需要擴展自己的游戲管理器組件對象,同時還要擴展自己的消息。下面我們就來看看消息如何擴展。
這個問題涉及到消息和消息類型兩種對象,主要問題就是如何創建自己的消息類型和消息,如何發送自己的消息。
每一個消息對象都對應一個消息類型,我們可以通過同一個消息類來搭載不同消息類型,消息類型也是通過一個類對象來實現,一個消息類中有一個消息類型 對象,我們可以只擴展消息類型而用現有的消息類來構造該類型的消息對象,但有時情況很復雜,現有的消息類不能滿足我們的要求,比如我們需要在消息對象中傳 遞我們自己定義的一些參數,這時候就需要我們擴展自己的消息類。
我們首先來看看如何擴展自己的消息類型。要擴展自己的消息類型非常簡單,一般我們可以創建一個頭消息類型頭文件(我們以testAAR例子為例),在該文件中通過宏添加我們的消息類型的聲明如下:
DT_DECLARE_MESSAGE_TYPE_CLASS_BEGIN(TestAARMessageType,TEST_AAR_EXPORT)
?
?????static TestAARMessageType PLACE_ACTOR;
?????static TestAARMessageType RESET;
?????static TestAARMessageType REQUEST_ALL_CONTROLLER_UPDATES;
?????static TestAARMessageType PRINT_TASKS;
?????static TestAARMessageType UPDATE_TASK_CAMERA;
?????static TestAARMessageType PLACE_IGNORED_ACTOR;
?
DT_DECLARE_MESSAGE_TYPE_CLASS_END()
?
在實現文件中添加:
DT_IMPLEMENT_MESSAGE_TYPE_CLASS(TestAARMessageType);
?
TestAARMessageTypeTestAARMessageType::PLACE_ACTOR("PLACE_ACTOR", "Place actormessage", "", USER_DEFINED_MESSAGE_TYPE + 1,DT_MSG_CLASS(dtGame::Message));
TestAARMessageTypeTestAARMessageType::RESET("RESET", "Reset message","Resets the scene", USER_DEFINED_MESSAGE_TYPE + 2,DT_MSG_CLASS(dtGame::Message));
TestAARMessageTypeTestAARMessageType::REQUEST_ALL_CONTROLLER_UPDATES("REQUEST_UPDATES","Requests for updates from the controller", "",USER_DEFINED_MESSAGE_TYPE + 3, DT_MSG_CLASS(dtGame::Message));
TestAARMessageTypeTestAARMessageType::PRINT_TASKS("PRINT_TASKS", "Prints thetasks", "", USER_DEFINED_MESSAGE_TYPE + 4,DT_MSG_CLASS(dtGame::Message));
TestAARMessageTypeTestAARMessageType::UPDATE_TASK_CAMERA("UPDATE_TASK_CAMERA","Updates the task camera", "", USER_DEFINED_MESSAGE_TYPE +5, DT_MSG_CLASS(dtGame::Message));
TestAARMessageTypeTestAARMessageType::PLACE_IGNORED_ACTOR("PLACE_IGNORED_ACTOR","Place ignored actor message", "",USER_DEFINED_MESSAGE_TYPE + 6, DT_MSG_CLASS(dtGame::Message));
?
我們注意到上面的DT_MSG_CLASS(dtGame::Message),這就是告訴系統該消息類型對應的消息類是什么。
系統默認自帶了很多消息類型供我們使用,詳情可參看MessageType類。
下面我們通過將上面的宏展開看看消息類型是如何讓游戲管理器識別的。
我們將宏展開后如下:
#defineDT_DECLARE_MESSAGE_TYPE_CLASS_BEGIN(CLS, EXPORT_MACRO)
class EXPORT_MACRO CLS : publicdtGame::MessageType
??{
??DECLARE_ENUM(CLS)
??protected:
?????
?????template<typename MessageClass>
?????CLS(const std::string& name, const std::string& category,
????????? const std::string& description,const unsigned short id, const MessageClass*)
?????: dtGame::MessageType(name, category, description, id, (constMessageClass*)(NULL))
?????{
???????? AddInstance(this);
?????}
?????virtual ~CLS() {}
??public:
?
};
再將其中的DECLARE_ENUM宏展開如下:
#define DECLARE_ENUM(EnumType)????????????????????????? ???????\
public:???????????????????????????????????????????????????????\
??typedef std::vector<EnumType*> EnumerateListType;?????????? \
??????????????????????????????????????????????????????????????\
??static const EnumerateListType& EnumerateType()? ???????????\
??{??????????????????????????????????????????????????????????\
?????return EnumType::mInstances;???????????????????????????? \
??}??????????????????????????????????????????????????????????\
??????????????????????????????????????????????????????????????\
??static const std::vector<dtUtil::Enumeration*>& Enumerate() \
??{??????????????????????????????????????????????????????????\
?????return EnumType::mGenericInstances;????????????????????? \
??}??????????????????????????????????????????????????????????\
??????????????????????????????????????????????????????????????\
??static EnumType* GetValueForName(const std::string& name);? \
??????????????????????????????????????????????????????????????\
private:??????????????????????????????? ???????????????????????\
??static EnumerateListType mInstances;??????????????????????? \
??static std::vector<dtUtil::Enumeration*> mGenericInstances; \
??static void AddInstance(EnumType* instance);??????????????? \
public:
?
所以最后DT_DECLARE_MESSAGE_TYPE_CLASS_BEGIN宏展開后完整如下:
class EXPORT_MACRO CLS : publicdtGame::MessageType
??{
??public:???????????????????????? ???????????????????????????????
??typedef std::vector<EnumType*> EnumerateListType;??????????
??????????????????????????????? ???????????????????????????????
??static const EnumerateListType& EnumerateType()????????????
??{??????????????????????????????????????????????????????????
?????return EnumType::mInstances;????????????????????????????
??}??????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
??static const std::vector<dtUtil::Enumeration*>& Enumerate()
??{??????????????????????????????????????????????????????????
?????return EnumType::mGenericInstances;?????????????????????
??}??????????????????????????????????????????????????????????
??????????????????????????????????????????????????????????????
??static EnumType* GetValueForName(const std::string& name);?
??????????????????????????????????????????? ???????????????????
private:??????????????????????????????????????????????????????
??static EnumerateListType mInstances;???????????????????????
??static std::vector<dtUtil::Enumeration*> mGenericInstances;
??static void AddInstance(EnumType* instance);???????????????
public:
?
??protected:
?????
?????template<typename MessageClass>
?????CLS(const std::string& name, const std::string& category,
????????? const std::string& description,const unsigned short id, const MessageClass*)
?????: dtGame::MessageType(name, category, description, id, (constMessageClass*)(NULL))
?????{
???????? AddInstance(this);
?????}
?????virtual ~CLS() {}
??public:
?
};
對于我們上面的例子就是把上面的CLS替換成TestAARMessageType就可以了。
?
實現文件中的宏展開后如下:
#defineIMPLEMENT_ENUM(EnumType)?????????????????????????????? \
??EnumType::EnumerateListType EnumType::mInstances;?????????? \
??std::vector<dtUtil::Enumeration*> EnumType::mGenericInstances; \
??void EnumType::AddInstance(EnumType* instance)???????????????? \
??{??????????????????????????????????????????????????????????\
?????EnumType::mInstances.push_back(instance);??????????????? \
?????EnumType::mGenericInstances.push_back(instance);??? ?????\
??}??????????????????????????????????????????????????????????\
??EnumType* EnumType::GetValueForName(const std::string& name)?? \
??{??????????????????????????????????????????????????????????\
?????for (unsigned i = 0; i < mInstances.size(); ++i)???????? \
?????{???????????????????????????????????????????????????????\
???????? if ((*mInstances[i]) == name)???????????????? \
???????? {????????????????????????????????????????????????????\
??????????? return mInstances[i];????????????????????? ????????\
???????? }????????????????????????????????????????????????????\
?????}???????????????????????????????????????????????????????\
?????return NULL;???????????????????????????????????????????? \
??}
?
實現文件中該宏后面的代碼就是調用消息類型的構造函數來構造對應的全生命周期對象(靜態對象):
MessageType(const std::string&name, const std::string& category,
???????????????????? const std::string&description, const unsigned short id, const MessageClass*)
??????????? : dtUtil::Enumeration(name)
??????????? , mCategory(category)
??????????? , mDescription(description)
??????????? , mId(id)
???????? {
??????????? AddInstance(this);
???????????dtGame::MessageFactory::RegisterMessageType<MessageClass>(*this);
???????? }
我們可以看到,在構造函數中首先將該消息類型對象保存了起來,然后將該消息類型及其對應的消息注冊到消息工廠。注冊代碼如下:
template<typename DerivedType>
?????bool RegisterType(UniqueIdType id)
?????{
???????? if (this->objectTypeMap.find(id) !=this->objectTypeMap.end())
???????? {
??????????? return false;
???????? }
?
???????? this->objectTypeMap[id] =&construct<BaseType,DerivedType>;
????????return true;
?????}
將該消息類型注冊進來并創建了相應的消息對象創建函數實例,以后就從這個objectTypeMap中返回這個對象創建函數構造一個新的對象供用戶使用了。
簡單說就是我們擴展了消息類型后并創建對應的全生命周期對象,在創建對象時,對象自己會自動將其以及其對應的消息注冊到 消息對象工廠,當創建指定類型的消息時通過游戲管理器的消息工廠進行創建,消息工廠會從注冊的消息類型以及消息對象實例映射表中創建指定消息類型對應的消 息對象實例并返回。具體可參見函數MessageFactory::CreateMessage。
???????? 擴展消息類型已經明白了,擴展消息非常簡單,我們可以把消息看做一個數據結構,不同的消息主要就是參數不同而已,在創建我們自己的消息時直接從 Message類派生自己的消息類,并在構造函數中添加自己的自定義參數即可。要發送自定義的消息只要在你需要的地方創建該類型的消息并調用游戲管理器的 SendMessage
???說到這里,我們對于如何在程序中定義自己的行為有了大致了解,三種策略可用,也可以組合一起用,就是擴展游戲管理器組件,擴展消息類型,擴展消息。
轉載于:https://www.cnblogs.com/kanego/articles/2314147.html
總結
以上是生活随笔為你收集整理的Delta3D———通过游戏管理器组件和消息的扩展创建自定义行为 《转》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个qq网名英文!
- 下一篇: 【转】C/C++中的日期和时间