游戏管理器组件给我们提供了在不修改游戏管理器的情况下灵活扩展我们的自定义行为的能力。游戏管理器组件是基于消息来工作的,定义自定义行为的基本 流程就是创建自定义类型的消息,在合适的时候发送消息,创建自定义游戏管理组件并重写自己的消息处理。将自定义组件添加到游戏管理器,游戏管理器会自动将 消息发送给游戏管理器组件(调用组件的消息处理函数)。下面我们首先来结合实例分析该过程的具体流程及其使用方法。

添加自定义行为有两种方式,一种是不定义自己的消息类型和消息,直接用现有的消息类型和消息对象,只是扩展自己的游戏管理器组件,也就是直接对现有 消息扩展消息处理。总之这两种方式都少不了要定义自己的游戏管理器组件。那么我们就先来看看如果定义自己的游戏管理器组件并使其工作。后续我们会介绍如何 扩展自己的消息类型以及消息对象使得我们可以更为灵活的添加自己的游戏行为,该过程可以参考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———通过游戏管理器组件和消息的扩展创建自定义行为 《转》相关推荐

  1. 添加游戏对象到 Windows 7 游戏管理器中。

    在 Windows 7 中点击开始,再点击游戏,就可打开系统游戏管理器了:在里面添加其他游戏也很方便,只需要把游戏快捷方式或游戏执行程序拖进去就可了. 出于对拖游戏执行程序后,显示名称的修改就在系统中 ...

  2. 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler...

    在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...

  3. java游戏管理器 闪退_手游频繁崩溃”闪退”? 从程序上找原因

    文 / 网易 Tjay(QA) 作为玩家,当游戏crash的时候是什么心情,如果这个游戏玩起来还不错的话,那我可能还会打开第二次,如果这个游戏一般的话我可能直接怒删了.当多次出现闪退crash的时候, ...

  4. (转)[Windows]在Win7游戏管理器中添加游戏

    Win7的游戏浏览器是一个不错的特性,它不仅可以显示游戏的完整信息,更重要的是可以开辟一个新的空间,将众多的游戏集中到一起,整整有条.不过这个游戏浏览器有一个很大的缺点,它只支持微软预定义游戏列表中的 ...

  5. [Windows]在Win7游戏管理器中添加游戏

    Win7的游戏浏览器是一个不错的特性,它不仅可以显示游戏的完整信息,更重要的是可以开辟一个新的空间,将众多的游戏集中到一起,整整有条.不过这个游戏浏览器有一个很大的缺点,它只支持微软预定义游戏列表中的 ...

  6. java游戏管理器虚拟按键_Android实现手机游戏隐藏虚拟按键

    手机游戏实现Android隐藏虚拟按键,供大家参考,具体内容如下 在华为等型号的手机会有虚拟按键,在进入游戏的时候,需要全屏隐藏这个按键,并在下拉状态栏的时候,会重新呼出虚拟按键. 游戏的加载过程中, ...

  7. C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器

    必须JobManager初始化 方式1: public void Start()         {             JobManager.AddJob(() => FetchingDa ...

  8. Delta3D(9)教程:添加消息发送和可激活体

    前面的教程我们创建了一个坦克角色,并通过场景编辑器STAGE,将它加载到场景中,并添加了自带的地形,并调整坦克坐标使贴近地面. 本节将设计让坦克响应Space按下事件来启动引擎,并按 ↑ 让坦克移动起 ...

  9. Delta3d组件以及消息机制

    在游戏管理器(GameManager)中维护一个消息队列std::queue(mSendMessageQueue),在GameManager::SendMessage中将消息放入队列中,如下 void ...

最新文章

  1. 36.intellij idea 如何一键清除所有断点
  2. pandas读写结构化数据(read_csv,read_table, read_excel, read_html, read_sql)
  3. python reader循环_python – 多次循环遍历csv.DictReader行
  4. 大批物联网设备扎堆CES 物联网产业化提速
  5. iOS中代码支持多国语言切换的实现(Xcode5+iOS7)
  6. hdu1808-Halloween treats(抽屉原理)
  7. hadoop学习6 运行map reduce出错
  8. MySQL如何把A表查询出来的某个字段的数据插入到新增的字段的下面
  9. Tensorflow游乐场
  10. python爬虫代理教程_Python代理IP爬虫的新手使用教程
  11. 解决Intel Edison 已连接Wifi但无法上网问题
  12. MacPro安装运行Win10虚拟机 (Parallels Desktop)
  13. 计算机视觉 响应_视觉响应式布局的自动化测试
  14. 一文带你重新审视CAP理论与分布式系统设计
  15. MySQL的多表查询-多表关系与相关练习题
  16. html改变复选框颜色,如何利用纯CSS改变html?radio/checkbox默认背景颜色样式
  17. 基于融云SDK实现高仿微信
  18. Ocean Color数据批量下载——海洋物理分布式活动档案中心PO.DAAC
  19. PhotoZoom Pro—图片“无损”放大
  20. [知乎]为什么上知乎?

热门文章

  1. javabean实体类与实体类之间的快速转换
  2. 2022-2028年中国锅仔片行业研究及前瞻分析报告
  3. 2022-2028年中国老年旅游市场深度调研及开发战略研究报告
  4. 错误提示没了_ESC错误排查-系统启动篇
  5. Python中*args和**kwargs的区别
  6. Yolov3 的 OneFlow 实现
  7. Supervisor使用详解
  8. python 16进制转10进制, 8进制转10进制, 2进制转10进制的方法
  9. 微信小程序页面之间数据传递
  10. Uncaught SyntaxError: Unexpected token