继续上一篇的创建游戏角色部分内容…
4、创建坦克角色
为了简单起见,我们暂时不按官方文档来,本教程的目的是让大家熟悉流程,太多其他的功能会影响大家的关注点,因此我这里创建的角色暂时先不添加消息处理,可激活体注册等这些代码,只是先创建一个静态角色能加载到场景中显示即可,后续会专门开篇写怎么添加消息处理函数、怎么注册可激活体等内容。
坦克角色部分代码如下:
// HoverTandActor.h
#ifndef HoverTankActor_h__
#define HoverTankActor_h__
#include "delta3d_export.h"
#include <dtActors/gamemeshactor.h>
class DELTA3D_EXPORT HoverTankActor : public dtActors::GameMeshDrawable
{
public:
// 所有的角色类都需要以GameActorProxy为参数的构造函数
HoverTankActor(dtGame::GameActorProxy &proxy);
void setVelocity(float velocity);
float getVelocity() const { return m_velocity; }
public:
// 当角色加载到场景中时由框架调用,常用于初始化角色
virtual void OnEnteredWorld();
protected:
// 自动内存管理,需要把析构函数隐藏
virtual ~HoverTankActor();
private:
// Mesh由父类提供,可直接使用
float m_velocity; // 此属性暂时在实现中不会使用,仅作代理绑定属性时使用
};
#endif // HoverTankActor_h__
(原文档这里继承的是dtActors::GameMeshActor,发现构造函数并不支持此类,查阅源码后,发现此类的基类是角色代理dtGame::GameActorProxy,而这应该继承角色基类,所以这里需要改成dtActors::GameMeshDrawable,这才是角色基类)
角色的构造函数中需要传入一个游戏角色代理,所有的角色都需要有一个这样的构造函数
仅创建静态角色,以上代码就足够了,下面来看一下实现(主要是加载静态物体网格):
// HoverTandActor.cpp
#include "HoverTankActor.h"
HoverTankActor::HoverTankActor(dtGame::GameActorProxy &proxy)
: dtActors::GameMeshDrawable(proxy)
{
this->SetName("HoverTank"); // 设置角色名称
}
void HoverTankActor::setVelocity(float velocity)
{
m_velocity = velocity;
}
void HoverTankActor::OnEnteredWorld()
{
// 设置此角色的网格数据文件
this->SetMesh("data/StaticMeshes/bmp_hover.ive");
//这里调用父类的同名函数
dtActors::GameMeshDrawable::OnEnteredWorld();
}
HoverTankActor::~HoverTankActor()
{
}
5、创建角色代理
我们在第4步创建了一个游戏角色,如前所说,我们还需要一个角色对应的代理。代理在GameManager框架中是非常重要的。不管我们在角色中写了多少代码,GameManager都只识别代理,角色只能通过角色代理来对外暴露角色的属性并通过可激活体处理来自游戏管理器的消息。下面来创建代理:
// HoverTankActorProxy.h
#ifndef HoverTankActorProxy_h__
#define HoverTankActorProxy_h__
#include "delta3d_export.h"
#include <dtActors/gamemeshactor.h>
// GameMeshActor是继承自GameMeshActorProxy,并实现及一些通用功能,因此可继承此类,实现自己的代理
class DELTA3D_EXPORT HoverTankActorProxy : public dtActors::GameMeshActor
{
public:
HoverTankActorProxy();
// 通过此方法创建自定义的属性,并暴露给外部,如STAGE编辑器
virtual void BuildPropertyMap();
// 绑定可激活体,绑定后,游戏管理器即可通知角色。默认已经绑定TickLocal、TickRemote、ProcessMessage
// 此教程我们暂时不用绑定任何可激活体,使用默认的
// virtual void BuildInvokables();
protected:
~HoverTankActorProxy();
// 原文档是CreateActor,2.8.0中已改为CreateDrawable
virtual void CreateDrawable();
// 当代理添加到GameManager中时,在AddActor函数中被调用,此方法在Actor中也有,可在这两个地方使用
virtual void OnEnteredWorld();
};
#endif // HoverTankActorProxy_h__
这里面有几个函数需要了解下:
- BuildInvokables()
虽然在教程中我们不使用此函数,但是在创建我们自己的可激活体的时候就会用到,通过重载此函数,我们可窗创建各种不同的消息的消息处理器(可激活体),并在角色第一次被创建时调用,GameMeshActorProxy已经帮我们创建了三个可激活体,TickLocal(),TickReomte(),ProcessMessage(),因此在重载此函数时,需要首先调用父类的同名函数:dtActors::GameMeshActor::BuildInvokables()。
- OnEnteredWrold()
在此函数中,处理当角色进入世界时希望的各种不同的行为,若播放声音、触发游戏事件等。该函数在GameManager::AddActor中被调用,且是在角色属性和可激活体创建完成后调用。此处我们需要在此注册tick事件及游戏事件,这样,角色类在重载OnTickLock,OnTickRemote,ProcessMessage函数后,即可处理相应的消息
- BuildPropertyMap()
此函数用于绑定角色属性,并暴露给GameManager及STAGE编辑器,即可可视化的修改角色的属性值,具体实现,查看代理实现,如下:
// HoverTankActorProxy.cpp
#include "HoverTankActorProxy.h"
#include "HoverTankActor.h"
#include <dtCore/floatactorproperty.h>
#include <dtUtil/functor.h>
HoverTankActorProxy::HoverTankActorProxy()
{
// 设置名称
SetClassName("HoverTank");
}
void HoverTankActorProxy::BuildPropertyMap()
{
// 首先调用父类方法
dtActors::GameMeshActor::BuildPropertyMap();
// 分组,也就是在角色库添加在STAGE编辑器中是,此属性会分组到指定的名称下面
const std::string GROUP = "HoverTank";
HoverTankActor *actor = GetDrawable<HoverTankActor>();
// Velocity属性
AddProperty(new dtCore::FloatActorProperty(
"Velocity", // 属性名称
"速度", // 标签
dtCore::FloatActorProperty::SetFuncType(actor, &HoverTankActor::setVelocity), // 修改属性值的方法
dtCore::FloatActorProperty::GetFuncType(actor, &HoverTankActor::getVelocity), // 获取属性值的方法
"Set/Get the Hover tank's velocity.", // 属性描述
GROUP)); // 分组
}
HoverTankActorProxy::~HoverTankActorProxy()
{
}
void HoverTankActorProxy::CreateDrawable()
{
// 创建游戏角色Actor
SetDrawable(*new HoverTankActor(*this));
}
void HoverTankActorProxy::OnEnteredWorld()
{
dtActors::GameMeshActor::OnEnteredWorld();
// 注册游戏消息,会自动发送到ProcessMessage可激活体
RegisterForMessages(dtGame::MessageType::INFO_GAME_EVENT);
// 注册TickLocal、TickRemote,只能选其中之一
if (!IsRemote())
{
RegisterForMessages(dtGame::MessageType::TICK_LOCAL, dtGame::GameActorProxy::TICK_LOCAL_INVOKABLE);
}
else
{
RegisterForMessages(dtGame::MessageType::TICK_REMOTE, dtGame::GameActorProxy::TICK_REMOTE_INVOKABLE);
}
// XYZActor Invokable
// RegisterForMessages(FireFighterMessageType::GAME_STATE_CHANGED, "EventName");
// 注册消息有两种方式
/*
RegisterForMessageAboutOtherActor() 和 RegisterForMessageAboutSelf()
第二种可以只处理自己Actor发送的消息,即别的Actor关闭了引擎,不会影响自己
需要在创建事件时设置角色Id,eventMsg->setAboutActorId();
*/
}
以上我们就完成了角色和角色代理的创建,此时编译工程,即可生成成功,但离我们加载到STAGE场景编辑器或能被游戏管理器识别,还差一个角色注册器,下一部分介绍…
6、在场景编辑器中加载角色
待续