当前位置: 首页 > 编程日记 > 正文

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

相关文章:

【spring】在不联网的情况下查看xml的定义规则的方法

1、打开依赖 2、打开该jar包 3、打开该包 4、找到xml的规则

常用的js判断

常用的js判断 关于注册的时候&#xff1b;对注册信息的判断&#xff1a; 表单 <form id"form" name"form" method"post" action"" οnsubmit"return CheckPost();"> 引入&#xff1a;<script language"JavaS…

解决Chrome中UEditor插入图片的选择框加载过慢问题

解决Chrome中UEditor插入图片的选择框加载过慢问题 ../resources/plugins/ueditor/ueditor.all.js 中line24489/24498中的 accept"image/*" 修改为 accept"image/jpeg,image/jpg,image/png,image/gif,image/bmp"../resources/plugins/ueditor/dialogs/im…

转:[大数据竞赛]夺冠感言:走进业务,提升对世界的认知能力

http://bbs.aliyun.com/read/153103.html?spm5176.7189909.0.0.KWGWap 一、同为推荐&#xff0c;大不同&#xff01;不知道同学们是否经常在天猫购物&#xff0c;但是相信大家一定听过音乐&#xff0c;看过电影&#xff0c;读过新闻和小说。大家在享受各种娱乐信息的时候&…

【转】C/C++中的日期和时间

头文件 time.h 函数用途 函数名 得到处理器时间 clock 得到时间差 difftime 设置时间 mktime 得到时间 time 得到以ASCII码表示的时间 asctime 得到字符串表示的时间 ctime 得到指定格式的时间 strftime 摘要&#xff1a; 本文从介绍基础概念入手&#xff0c;探讨了在C/C中对日…

【spring】di(依赖注入)使用实例

1、xml文件里的配置 <!-- 问题 &#xff1a; 两个bean的顺序可不可以调换&#xff1f; 答 &#xff1a; 可以--><bean id"userDao" class"springboottest.ioc.UserDao"> </bean><bean id"UserService" class"springb…

设置php-fpm使用socket文件

1、在配置文件/usr/local/php/etc/php-fpm.conf文件中找到 <value name "listen_address">127.0.0.1:9000</value> 改为<value name"listen_address"> /var/run/phpfpm.sock</value> 重启php-fpm /usr/local/php/sbin/php-fpm r…

BZOJ1251: 序列终结者

【传送门&#xff1a;BZOJ1251】 简要题意&#xff1a; 给出一个长度为n的序列&#xff0c;有m个操作&#xff0c;3种操作&#xff1a; 1 l r k将l到r的数增加k 2 l r将l到r的数列翻转 3 l r求出l到r的最大值 题解&#xff1a; 裸SPLAY&#xff0c;直接下放两种标记&#xff0c…

Linux笔记 软件管理

一、软件包分类1.软件包分类&#xff1a;源码包、二进制包源码包&#xff1a;源代码1&#xff09;优点&#xff1a;开源&#xff0c;有能力可修改源代码可以自由选择所需的功能软件是编译安装&#xff0c;更适合Linux系统&#xff0c;更稳定效率更高卸载方便。2&#xff09;缺点…

如何有效编写软件的75条建议

1. 你们的项目组使用源代码管理工具了么&#xff1f; 应该用。VSS、CVS、PVCS、ClearCase、CCC/Harvest、FireFly都可以。我的选择是VSS。2. 你们的项目组使用缺陷管理系统了么&#xff1f; 应该用。ClearQuest太复杂&#xff0c;我的推荐是BugZilla。 3. 你们的测试组还在用…

【spring】使用spring的环境配置及从官网获得配置文件所用代码的方法

环境配置 1、添加jar包 spring-beans-4.1.3.RELEASE.jarspring-context-4.1.3.RELEASE.jarspring-core-4.1.3.RELEASE.jarspring-expression-4.1.3.RELEASE.jar 2、配置文件 &#xff08;1&#xff09;在下创建一个config文件夹 &#xff08;2&#xff09;在文件夹下创建一…

C语言:1孩半问题

题目&#xff1a; 一孩半&#xff0c;又称独女户二胎&#xff0c;即中国大陆部分农村的一项计划生育政策&#xff0c;第一胎是女孩的夫妻可以生育第二个子女。如果第二胎有n%人工性别选择干预&#xff08;选择男孩&#xff09;&#xff0c;试问男女比例为多少。&#xff08;10分…

Javascript字符串及数组赋值区别

最近做一个分页的javascript程序&#xff0c;需要先将tbody下面的tr标签全部删除&#xff0c;然后再append新的tr&#xff0c;使用下面的代码 var trs$d("tbdoys").getElementsByTagName("tr");for(var j0;j<trs.length;j){$d("tbdoys").remo…

Linux系统分辨率设置

linux 设置分辨率 如果你需要在linux上设置显示屏的分辨率&#xff0c;分两种情况&#xff1a;分辨率模式存在与分辨率模式不存在&#xff0c;具体如下。 1&#xff0c;分辨率模式已存在 1&#xff09;如何查询是否存在&#xff1a; 图形界面&#xff1a;在System Settings/Dis…

【spring】使用构造方法依赖注入

注 &#xff1a; &#xff08;1&#xff09;使用构造方法依赖注入有两种一种是通过参数顺序一种是按照参数类型的顺序 &#xff08;2&#xff09;所有的依赖注入都必须拥有无参的构造方法&#xff0c;一开始没有添加是因为jvm会自动分配 按照参数的顺序 代码实现&#xff1…

【技术贴】火狐的悬停激活标签扩展插件下载。Tab Focus

火狐专用鼠标悬停激活标签&#xff0c;像360和搜狗浏览器那样的把鼠 标放在标签上&#xff0c;一般都是设置200ms激活此标签。 https://addons.mozilla.org/zh-CN/firefox/addon/tab-focus/ 在组件里可以设置Tab Focus &#xff0c;我都是设置1ms激活。比较爽。

数据结构_顺序栈的代码实践

#include <iostream> using namespace std; #define Maxsize 100//预先分配空间&#xff0c;这个数值根据实际情况预估确定 typedef struct SqStack{int *base;//栈底指针int *top;//栈顶指针 }SqStack;bool InitStack(SqStack &S)//构造空栈 {S.base new int…

C#字符串与享元(Flyweight)模式

写这个文章&#xff0c;主要是因为网上对C#字符串和享元模式的误解比较多。 Flyweight模式 先说这名字&#xff0c;fly呢&#xff0c;就是苍蝇&#xff0c;没错这里面不是飞的意思&#xff0c;是苍蝇的意思&#xff0c;weight大家都知道&#xff0c;就是重量&#xff0c;苍蝇的…

CarTool 使用,获取图片资源

程序&#xff1a;gitHub: 项目地址 使用方法&#xff1a; 1.拿到资源包 在itunes里找到喜欢的应用&#xff0c;然后下载&#xff0c;直接将app拖到桌面。得到一个一个ipa资源包&#xff0c;如图 2.将资源包改成zip格式 3.解压zip资源包&#xff0c;随后打开&#xff0c;显示包…

【spring】p命名空间的使用

1、在xml文件中添加配置 xmlns:p"http://www.springframework.org/schema/p" 2、在xml中进行更改 更改前&#xff08;使用属性依赖注入&#xff09;&#xff1a; 代码实现&#xff1a; <bean id"UserService" class"springboottest.ioc.UserSe…

ARM的位置无关程序设计在Bootloader中的应用

http://www.mcuol.com/tech/107/26052.htm 引言 基于位置无关代码PIC&#xff08;PositionIndependent Code&#xff09;的程序设计在嵌入式应用系统开发中具有重要的作用。 尤其在裸机状态下开发Bootloader程序及进行内核初始化设计&#xff1b;利用位置无关的程序设计方…

CentOS 7 添加系统开机服务

CentOS 7的服务systemctl脚本存放在&#xff1a;/usr/lib/systemd/&#xff0c;有系统&#xff08;system&#xff09;和用户&#xff08;user&#xff09;之分&#xff0c;需要开机不登陆就能运行的程序&#xff0c;存在系统服务里&#xff0c;即&#xff1a;/usr/lib/systemd…

UITableView HeaderView,FooterView 使用SnapKit布局导致约束异常

今天做一个APP里面设置页面(个人中心) 就是一个列表菜单 顶部是一个头像和账户标题, 底部为一个退出登录按钮 当然我第一时间就想到了UITableView, HeaderView, FooterView // 我创建了两个类文件, 用来做UITableView 的header, footer class SettingHeaderView: UIView {//里面…

【spring】专项配置文件的使用

1、编写专项配置文件&#xff08;数据库专项文件&#xff09; 代码实现&#xff1a; mysql-urljdbc:mysql://localhost:3306/test mysql-drivercom.mysql.jdbc.Driver 2、在xml文件里配置contest 代码实现&#xff1a; xmlns:context"http://www.springframework.org…

Ubuntu换回Gnome界面

安装了Ubuntu11.10&#xff0c;Unity界面实在是不怎么习惯。遂换回Gnome。 1. 首先安装Gnome sudo apt-get install gnome-shell sudo apt-get install gnome-tweak-tool 2. 设置自动登录Gnome shell classic sudo /usr/lib/lightdm/lightdm-set-defaults -s gnome-classic  …

Linux设备文件简介。

概述 设备管理是linux中比较基础的东西&#xff0c;但是由于Linux智能程度的越来越高&#xff0c;Udev的使用越来越广泛&#xff0c;使得越来越多的Linux新用户对 /dev 目录下的东西变得不再熟悉。有时候遇见问题就会变得抓狂。 Linux 中的设备有2种类型&#xff1a;字符设备(无…

羊车门问题分析

1、按照你的第一感觉回答&#xff0c;你觉得不换选择能有更高的几率获得汽车&#xff0c;还是换选择能有更高的几率获得汽车&#xff1f;或几率没有发生变化&#xff1f;答&#xff1a;换。2、请自己认真分析一下“不换选择能有更高的几率获得汽车&#xff0c;还是换选择能有更…

python多版本共存使用pip

有时候大家使用python的时候都会用到两个版本&#xff0c;虽说可以独立设置环境变量独立使用&#xff0c;但是用到pip的时候就不行了&#xff0c;python2和python3中的都用相同的pip。在这里我就把我的经验分享给大家。 这是python设置的环境变量 下面是python2的名字 下面是py…

Ruby开发环境

假设您已经安装了Eclipse&#xff0c;那么只需按照如下步骤即可&#xff1a;Please copy the following Update Site URL to your clipboard and then follow the steps listed below to add this URL to your Available Software Sites list. Attempting to access this URL u…

【spring】spel表达式

格式&#xff1a; #{ } 简单使用实例 一、DButil.java 代码实现&#xff1a; package springboottest.ioc.speltest;public class DBUtil {private UserService us;public UserService getUs() {return us;}public void setUs(UserService us) {this.us us;} }二、UserSe…