CryENGINE3初探Entities (二)----使用C++创建自定义Entity(一)
经历了一个月的考试,终于又有机会抱着书啃啃CE3了,接着上回,我们继续来看CryENGINE3中的Entity。
首先我必须吐槽一点,可能是自己能力不足,书上这部分内容真是晦涩难解,很多要注意的地方都含糊不清,书中这一部分具体内容在本节后面列出。在开始之前,还有一个概念要大概说一下,CE3中除了entity,还有个和游戏物体有关的词,叫做GameObject,书中的解释是:当entity需要更多更高级的功能的时候,需要用到gameObject。而如果想要创建gameObject,需要创建gameObject extensions(扩展)。而这个gameobject extensions暂时还只能使用C++创建,Lua和C#都不可以。CryENGINE为了方便创建gameobject extensions,为我们创建了一个模板:CGameObjectExtensionsHelper类,这个类里面封装了很多重复的不必要我们逐个重写的内容。
然后,我们就开始动手创建gameobject extensions:
1.浏览官方的例子。
其实,在官方给我们提供的资源中,有一部分功能比较复杂的entity就是使用gameobject extensions创建的,具体位置见下图:
随便打开两个里面的.h文件,我们会发现一些问题,比如
//MyTestEntity2.h
#pragma once
#include "IGameObject.h"
class CMyTestEntity2 :public CGameObjectExtensionHelper < CMyTestEntity2, IGameObjectExtension >
{
public:CMyTestEntity2();virtual ~CMyTestEntity2();//IGameObjectExtension///virtual void GetMemoryUsage(ICrySizer *pSizer) const;virtual bool Init(IGameObject * pGameObject);virtual void PostInit(IGameObject * pGameObject);virtual void InitClient(int channelId);virtual void PostInitClient(int channelId);virtual bool ReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms);virtual void PostReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms);virtual bool GetEntityPoolSignature(TSerialize signature);virtual void Release();virtual void FullSerialize(TSerialize ser);virtual bool NetSerialize(TSerialize ser, EEntityAspects aspect, uint8 profile, int pflags);virtual void PostSerialize();virtual void SerializeSpawnInfo(TSerialize ser);virtual ISerializableInfoPtr GetSpawnInfo();virtual void Update(SEntityUpdateContext& ctx, int updateSlot);virtual void HandleEvent(const SGameObjectEvent& event); virtual void ProcessEvent(SEntityEvent& event);virtual void SetChannelId(uint16 id);virtual void SetAuthority(bool auth);virtual const void * GetRMIBase() const; virtual void PostUpdate(float frameTime);virtual void PostRemoteSpawn();//IGameObjectExtension///
};
//MyTestEntity2.cpp
#include "stdafx.h"
#include "MyTestEntity2.h"
#include "IActorSystem.h"CMyTestEntity2::CMyTestEntity2()
{
}CMyTestEntity2::~CMyTestEntity2()
{
}
//IGameObjectExtension///
void CMyTestEntity2::GetMemoryUsage(ICrySizer *pSizer) const
{}
bool CMyTestEntity2::Init(IGameObject * pGameObject)
{}
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{}
void CMyTestEntity2::InitClient(int channelId)
{
}
void CMyTestEntity2::PostInitClient(int channelId)
{
}
bool CMyTestEntity2::ReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{}
void CMyTestEntity2::PostReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{}
bool CMyTestEntity2::GetEntityPoolSignature(TSerialize signature)
{return true;
}
void CMyTestEntity2::Release()
{delete this;
}
void CMyTestEntity2::FullSerialize(TSerialize ser)
{}
bool CMyTestEntity2::NetSerialize(TSerialize ser, EEntityAspects aspect, uint8 profile, int pflags)
{return true;
}
void CMyTestEntity2::PostSerialize()
{}
void CMyTestEntity2::SerializeSpawnInfo(TSerialize ser)
{}
ISerializableInfoPtr CMyTestEntity2::GetSpawnInfo()
{return nullptr;
}
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{}
void CMyTestEntity2::HandleEvent(const SGameObjectEvent& event)
{}
void CMyTestEntity2::ProcessEvent(SEntityEvent& event)
{}
void CMyTestEntity2::SetChannelId(uint16 id)
{}
void CMyTestEntity2::SetAuthority(bool auth)
{}
const void * CMyTestEntity2::GetRMIBase() const
{return CGameObjectExtensionHelper::GetRMIBase();
}
void CMyTestEntity2::PostUpdate(float frameTime)
{}
void CMyTestEntity2::PostRemoteSpawn()
{}
//IGameObjectExtension///
其实走到这一步,我一直有一个疑问,C++的类向导的纯虚函数这一栏怎么什么也没有?要怎么写才能让系统帮忙生成默认的代码呢?还望知道的朋友能不吝赐教。
bool CMyTestEntity2::Init(IGameObject * pGameObject)
{SetGameObject(pGameObject);return true;
}
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{pGameObject->EnableUpdateSlot(this, 0);
}
这一部分内容,在官网的文档上有中文说明,链接如下:
bool CMyTestEntity2::ReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{ResetGameObject();return true;
}
第四个要写的函数
void CMyTestEntity2::Release()
{delete this;
}
第五个
ISerializableInfoPtr CMyTestEntity2::GetSpawnInfo()
{return nullptr;
}
第六个:这个函数我目前还不清楚他的具体含义。等我弄清楚了,再来向大家解释。
const void * CMyTestEntity2::GetRMIBase() const
{return CGameObjectExtensionHelper::GetRMIBase();
}
其余的函数,只要是bool型返回值,都只用写return true。void的都不用动。
其实稍微总结一下就是:除了回收资源的release函数,和两个初始化函数,一个GetRMIBase函数,其他的bool都返回真,void不用管,指针返回空即可。
#include "stdafx.h"
#include "MyTestEntity2.h"
#include "IActorSystem.h"CMyTestEntity2::CMyTestEntity2()
{
}CMyTestEntity2::~CMyTestEntity2()
{
}//IGameObjectExtension///void CMyTestEntity2::GetMemoryUsage(ICrySizer *pSizer) const
{pSizer->Add(this);
}
bool CMyTestEntity2::Init(IGameObject * pGameObject)
{SetGameObject(pGameObject);return true;
}
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{pGameObject->EnableUpdateSlot(this, 0);
}void CMyTestEntity2::InitClient(int channelId)
{
}
void CMyTestEntity2::PostInitClient(int channelId)
{
}
bool CMyTestEntity2::ReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{ResetGameObject();return true;
}
void CMyTestEntity2::PostReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{}
bool CMyTestEntity2::GetEntityPoolSignature(TSerialize signature)
{return true;
}
void CMyTestEntity2::Release()
{delete this;
}
void CMyTestEntity2::FullSerialize(TSerialize ser)
{}
bool CMyTestEntity2::NetSerialize(TSerialize ser, EEntityAspects aspect, uint8 profile, int pflags)
{return true;
}
void CMyTestEntity2::PostSerialize()
{}
void CMyTestEntity2::SerializeSpawnInfo(TSerialize ser)
{}
ISerializableInfoPtr CMyTestEntity2::GetSpawnInfo()
{return nullptr;
}
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{}
void CMyTestEntity2::HandleEvent(const SGameObjectEvent& event)
{}
void CMyTestEntity2::ProcessEvent(SEntityEvent& event)
{}
void CMyTestEntity2::SetChannelId(uint16 id)
{}
void CMyTestEntity2::SetAuthority(bool auth)
{}
const void * CMyTestEntity2::GetRMIBase() const
{return CGameObjectExtensionHelper::GetRMIBase();
}
void CMyTestEntity2::PostUpdate(float frameTime)
{}
void CMyTestEntity2::PostRemoteSpawn()
{}
//IGameObjectExtension///
这个语句共有三个参数,第二个参数是注册的类名,第三个是用到的lua文件路径,我们这里还没有写,所有为空
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{pGameObject->EnableUpdateSlot(this, 0);IEntity *pEnt = GetEntity();if (pEnt){pEnt->LoadGeometry(0, "GameSDK/Objects/Objects/default/primitive_box.cgf", (const char*)NULL, IEntity::EEntityLoadFlags::EF_AUTO_PHYSICALIZE);}
}
你可以直接用winrar等类似的压缩软件解压编辑器提供给我们的Object文件,然后你就会得到这些静态网格物体。关于导入模型之类的,网上的教程太多了,我这里不赘述应该用不了多久,我这边就会更新到跟深入一点的内容了。
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{IEntity *pEnt = GetEntity();//获取当前的entityif (pEnt)//如果entity存在(获取陈宫){SEntityProximityQuery Query;//proximity 距离比较靠近的,建立一个距离entity比较近的查询Query.box = AABB(pEnt->GetWorldPos(), 5);//具体距离设置为5单位Query.nEntityFlags = 0;Query.pEntityClass = nullptr;gEnv->pEntitySystem->QueryProximity(Query); }
}
这段代码其实说的是在我们的entity外面形成了一个距离entity为5单位的一个矩形区域。我总感觉像是Unity里面的Trigger。这时候我们调试,进入编辑器会没有任何反应。
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{IEntity *pEnt = GetEntity();if (pEnt){SEntityProximityQuery Query;//proximity 距离比较靠近的Query.box = AABB(pEnt->GetWorldPos(), 5);Query.nEntityFlags = 0;Query.pEntityClass = nullptr;gEnv->pEntitySystem->QueryProximity(Query);for (int i = 0; i < Query.nCount; i++){IEntity *pQueryEnt = Query.pEntities[i];/*Physics System Programming:区域内添加推力impulse*/IPhysicalEntity *pQueryPhysEnt = pQueryEnt->GetPhysics();if (pQueryPhysEnt){pe_action_impulse Impulse; Impulse.iApplyTime = 0;Vec3 Dir = (pQueryEnt->GetWorldPos() - pEnt->GetWorldPos()).normalized();Dir.SetLength(300);Impulse.impulse = Dir;pQueryPhysEnt->Action(&Impulse);}}}
}
我们首先遍历查询,找到区域中的所有entity,然后判断每一个entity是不是有物理属性,如果有的话,添加一个推力impulse,if语句里面说明了这个推力的方向和大小。这部分的内容就是之前提到了的有关物理引擎的编程。好了,我们继续调试运行,按F5调试编辑器,拖放entity到场景,我们会发现,人物靠近entity的时候会被一个无形的力向外退,无法靠近entity。
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{IEntity *pEnt = GetEntity();if (pEnt){SEntityProximityQuery Query;//proximity 距离比较靠近的Query.box = AABB(pEnt->GetWorldPos(), 5);Query.nEntityFlags = 0;Query.pEntityClass = nullptr;gEnv->pEntitySystem->QueryProximity(Query);for (int i = 0; i < Query.nCount; i++){IEntity *pQueryEnt = Query.pEntities[i];/*Physics System Programming:区域内添加推力impulse*/IPhysicalEntity *pQueryPhysEnt = pQueryEnt->GetPhysics();if (pQueryPhysEnt && (pQueryEnt != pEnt)&&(pQueryEnt != gEnv->pGame->GetIGameFramework()->GetClientActor()->GetEntity())&&(pQueryEnt->GetClass()!=gEnv->pEntitySystem->GetClassRegistry()->FindClass("MyTestEntity2"))){pe_action_impulse Impulse; Impulse.iApplyTime = 0;Vec3 Dir = (pQueryEnt->GetWorldPos() - pEnt->GetWorldPos()).normalized();Dir.SetLength(300);Impulse.impulse = Dir;pQueryPhysEnt->Action(&Impulse);}//获得entity对应的actor并对其生命值做加减法IActor *pQueryActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pQueryEnt->GetId());if (pQueryActor){pQueryActor->SetHealth(pQueryActor->GetHealth() - 1);}}}
}
做了上面的修改,我们再启动调试,进入编辑器,为了能够感受到主角生命值的变化,我们写一个简单的FG测试,具体如图:
编辑器运行截图
if (pQueryPhysEnt && (pQueryEnt != pEnt)&&(pQueryEnt != gEnv->pGame->GetIGameFramework()->GetClientActor()->GetEntity())&&(pQueryEnt->GetClass()!=gEnv->pEntitySystem->GetClassRegistry()->FindClass("MyTestEntity2")))
接着F5调试,我们发现玩家不会被弹开,走进区域内的玩家的生命值一直在减少。
#include "stdafx.h"
#include "MyTestEntity2.h"
#include "IActorSystem.h"CMyTestEntity2::CMyTestEntity2()
{
}CMyTestEntity2::~CMyTestEntity2()
{
}//IGameObjectExtension////*
init块在实例变量初使化后执行
postinit块在对象完全初始化后执行
请看下面的测试类:
class Test
{//第三步执行postinit{println("postinit:{a}")}//第二步执行init{println("init:{a}")}//第一步执行public var a=1; }
输出的结果是: init:1 postinit:1 可以很清楚的看到执行的顺序
也就是说,init先执行,然后postinit再开始执行
*/
void CMyTestEntity2::GetMemoryUsage(ICrySizer *pSizer) const
{pSizer->Add(this);
}
bool CMyTestEntity2::Init(IGameObject * pGameObject)
{SetGameObject(pGameObject);return true;
}
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{pGameObject->EnableUpdateSlot(this, 0);IEntity *pEnt = GetEntity();if (pEnt){pEnt->LoadGeometry(0, "GameSDK/Objects/Objects/default/primitive_box.cgf", (const char*)NULL, IEntity::EEntityLoadFlags::EF_AUTO_PHYSICALIZE);}
}
void CMyTestEntity2::InitClient(int channelId)
{
}
void CMyTestEntity2::PostInitClient(int channelId)
{
}
bool CMyTestEntity2::ReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{ResetGameObject();return true;
}
void CMyTestEntity2::PostReloadExtension(IGameObject * pGameObject, const SEntitySpawnParams ¶ms)
{}
bool CMyTestEntity2::GetEntityPoolSignature(TSerialize signature)
{return true;
}
void CMyTestEntity2::Release()
{delete this;
}
void CMyTestEntity2::FullSerialize(TSerialize ser)
{}
bool CMyTestEntity2::NetSerialize(TSerialize ser, EEntityAspects aspect, uint8 profile, int pflags)
{return true;
}
void CMyTestEntity2::PostSerialize()
{}
void CMyTestEntity2::SerializeSpawnInfo(TSerialize ser)
{}
ISerializableInfoPtr CMyTestEntity2::GetSpawnInfo()
{return nullptr;
}
void CMyTestEntity2::Update(SEntityUpdateContext& ctx, int updateSlot)
{IEntity *pEnt = GetEntity();if (pEnt){SEntityProximityQuery Query;//proximity 距离比较靠近的Query.box = AABB(pEnt->GetWorldPos(), 5);Query.nEntityFlags = 0;Query.pEntityClass = nullptr;gEnv->pEntitySystem->QueryProximity(Query);for (int i = 0; i < Query.nCount; i++){IEntity *pQueryEnt = Query.pEntities[i];/*Physics System Programming:区域内添加推力impulse*/IPhysicalEntity *pQueryPhysEnt = pQueryEnt->GetPhysics();if (pQueryPhysEnt && (pQueryEnt != pEnt)&&(pQueryEnt != gEnv->pGame->GetIGameFramework()->GetClientActor()->GetEntity())&&(pQueryEnt->GetClass()!=gEnv->pEntitySystem->GetClassRegistry()->FindClass("MyTestEntity2"))){pe_action_impulse Impulse; Impulse.iApplyTime = 0;Vec3 Dir = (pQueryEnt->GetWorldPos() - pEnt->GetWorldPos()).normalized();Dir.SetLength(300);Impulse.impulse = Dir;pQueryPhysEnt->Action(&Impulse);}/*区域内添加推力*///获得entity对应的actor并对其生命值做加减法IActor *pQueryActor = gEnv->pGame->GetIGameFramework()->GetIActorSystem()->GetActor(pQueryEnt->GetId());if (pQueryActor){pQueryActor->SetHealth(pQueryActor->GetHealth() - 1);}}}
}
void CMyTestEntity2::HandleEvent(const SGameObjectEvent& event)
{}
void CMyTestEntity2::ProcessEvent(SEntityEvent& event)
{}
void CMyTestEntity2::SetChannelId(uint16 id)
{}
void CMyTestEntity2::SetAuthority(bool auth)
{}
const void * CMyTestEntity2::GetRMIBase() const
{return CGameObjectExtensionHelper::GetRMIBase();
}
void CMyTestEntity2::PostUpdate(float frameTime)
{}
void CMyTestEntity2::PostRemoteSpawn()
{}
//IGameObjectExtension///
#include<IEntityClass.h>
class MyEntityClass :public IEntityClass
{};
然后我这边VS直接报错,说IEntityClass不是类名或者结构体名。最后我找了半天,发现#include写错了,因为IEntityClass.h文件根本不在GameSDK这个工程中。这边我直接通过类向导建立一个类MyEntityClass,父类IEntityClass,这样系统直接帮忙生成如下代码:
#pragma once
#include "F:\Engine Study\CE3\CRYENGINE_PC_v_3_5_4_1509_freesdk\Code\CryEngine\CryCommon\IEntityClass.h"
class MyEntityClass :public IEntityClass
{
public:MyEntityClass();~MyEntityClass();}
IEntityClassRegistry::SEntityClassDesc classDesc;classDesc.sName = "MyEntityClassName";classDesc.editorClassInfo.sCategory = "MyCategory";IEntitySystem *pEntitySystem = gEnv->pEntitySystem;IEntityClassRegistry *pClassRegistry = pEntitySystem->GetClassRegistry();bool result = pClassRegistry->RegisterClass(new CMyEntityClass(classDesc));
然后书中这就算讲完了怎么用C++创建entity了。
CryENGINE3初探Entities (二)----使用C++创建自定义Entity(一)相关推荐
- ROS实战篇(二)如何创建自定义的msg文件以及自己编写launch文件?
一.如何创建自定义的msg文件? msg文件介绍: 1.msg文件是用来描述ROS中自定义的消息类型的,可以被不同语言调用. 2.msg文件以 .msg 结尾,必须存放在软件包的msg文件夹下. 3. ...
- CryENGINE3初探Flowgraph(二)----FG简单概念及操作流程
接着上篇博客,我们通过简单控制TimeOfDay来一步步深入FG. (一)完成对TimeOfDay简单控制的实例 1.在场景中添加FlowgraphEntity. CE3大部分FG都是依托在Entit ...
- MATLAB强化学习实战(十二) 创建自定义强化学习算法的智能体
创建自定义强化学习算法的智能体 创建环境 定义策略 自定义智能体类 智能体属性 构造函数 相关函数 可选功能 创建自定义智能体 训练自定义智能体 自定义智能体仿真 本示例说明如何为您自己的自定义强化学 ...
- cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单
1.在项目中添加引用:AcCui.dll,这个DLL存放在CAD的安装目录中.但是,好像CAD2007之前的版本里没有这个DLL),我电脑上安装了CAD2004.CAD2005.CAD2008.CAD ...
- cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单(AcCui)
从CAD2007之后,Autodesk提供了一个新的程序集AcCui.dll,使用这个程序集,我们可以方便地做一些界面方面的操作,比如创建自定义菜单. 下面介绍一下菜单的创建过程: 1.在项目中添加引 ...
- ROS话题通信中创建自定义数据类型的两种方式
一.在同一个功能包下创建.msg文件 1.在功能包目录下创建msg文件夹 2.在文件夹中创建.msg文件 3.在该功能包的package.xml文件中加入: <build_depend>m ...
- 独家 | 使用TensorFlow 2创建自定义损失函数
作者:Arjun Sarkar 翻译:陈之炎 校对:欧阳锦 本文约1900字,建议阅读8分钟 本文带你学习使用Python中的wrapper函数和OOP来编写自定义损失函数. 标签:TensorFlo ...
- 使用tolua++编译pkg,从而创建自定义类让Lua脚本使用
2019独角兽企业重金招聘Python工程师标准>>> 在Lua第三篇中介绍了,如何在cocos2dx中使用Lua创建自定义类供Lua脚本调用使用,当时出于Himi对Lua研究不够深 ...
- Docker的使用初探(二):Docker与.NET Core的结合
目录 Docker的使用初探(二):Docker与.NET Core的结合 添加Dockefile 1. 在创建项目时添加 2. 手动添加 3. 容器业务流程协调控制程序支持 Dockefile语法 ...
最新文章
- 全职院士32人!这些大学,正创造奇迹!
- Socket网络通讯_TCP协议
- 赠书:聊聊「分布式架构」那些事儿
- 【Flutter】Flutter 开源项目参考
- windows IIS权限经典设置教程
- 3.2 matlab用if语句实现选择结构
- 异想-天开 python---while、for、if-else 循环学习
- 用Thread实现socket多线通讯
- vmware 搭建k8s无法ping通子节点_一波四折 —— 记一次K8S集群应用故障排查
- carplant_mxnet
- Mysql原理+ 多实例 +表损坏
- 2014年12月21号面试
- 值得收藏的微软Windows系统蓝屏代码大全以及修复方法
- Javaweb面试题整理
- 如何创建GOOGLE ADS的MCC经理账户,有什么好处?
- Python上位机与C51单片机串口通信
- Win系统 - 如何查看电脑开机了多长时间?
- ZXR10 1809 路由器 1800开启WEB配置界面调试方法
- 【研究生】横扫13项中文NLP任务:香侬科技提出汉语字形表征向量Glyce+田字格CNN...
- composer类库—汉字转拼音