经历了一个月的考试,终于又有机会抱着书啃啃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文件,我们会发现一些问题,比如           

再比如
相同点,他们都继承自同一个类模板:CGameObjectExtensionHelper,实现了里面所有纯虚函数,下面就是他们的基类,里面的每一个函数都有注释
2.好了,看完了提供给我们的资料,我们就可以自己动手写了。首先可以在GameFiles的Environment里面创建一个筛选器,把我们以后要写的都放进去,我这里起名     MyTestEntity,然后在里面添加一个类,我这边:类名:MyTestEntity2,它的父类填写: CGameObjectExtensionHelper < CProximityMine, IGameObjectExtension >。
3.完成后,我们需要转到IGameObjectExtensions类,找到里面的所有纯虚函数,实现他们。这里有一个简单的操作,直接把这些纯虚函数拷贝到.h和.cpp里面就行。或者直接拷贝我下面的代码:
//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++的类向导的纯虚函数这一栏怎么什么也没有?要怎么写才能让系统帮忙生成默认的代码呢?还望知道的朋友能不吝赐教。

4.接着就是去填写这些函数了。这里我这把要我们写的函数列出来,其他不需要我们操作的函数的具体含义,在这里就不一一列出了,一来我现在也是一知半解,怕误导大家,二来,基类里面有非常多的注释,大家不妨看一下。
5.第一个要写的函数是:GetMemoryUsage(),从字面意思上相比也能明白他的意思,其实就是获取内存的使用情况,这里我们直接填写pSizer->Add(this);即可。
第二个要写的函数是:Init()和postInity函数。这两个函数都是做的初始化,我们要注意他们的顺序关系,首先执行init函数,接下来再执行postinit函数。函数内容如下:

bool CMyTestEntity2::Init(IGameObject * pGameObject)
{SetGameObject(pGameObject);return true;
}
void CMyTestEntity2::PostInit(IGameObject * pGameObject)
{pGameObject->EnableUpdateSlot(this, 0);
}

这一部分内容,在官网的文档上有中文说明,链接如下:

http://docs.cryengine.com/display/SDKDOC4/GameObject
第三个要写的函数ReloadExtension()
只用填写:
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不用管,指针返回空即可。

到了这一步,.cpp文件的内容应该大致如下:
#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///
6.注册entity
然后我们需要在GameFactory的InitGameFactory函数里面注册entity。找到Custome GameObject,添加下面的语句
   REGISTER_GAME_OBJECT(pFramework, MyTestEntity2, "");
     这个语句共有三个参数,第二个参数是注册的类名,第三个是用到的lua文件路径,我们这里还没有写,所有为空
好了,现在应该可以启动调试了,如果没有其他问题的话,应该能在编辑器中找到entity,并且可以顺利的拖动添加到场景中,只是它是空的,没有任何(Properties)特性,也没有代表的图标,或者三维模型。
7.接下来,我们让这个entity拥有一个静态网格模型cgf。其实这一步很简单,只用在PostInit函数里面添加这么一句就可以了
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文件,然后你就会得到这些静态网格物体。关于导入模型之类的,网上的教程太多了,我这里不赘述应该用不了多久,我这边就会更新到跟深入一点的内容了。

重新编译,你可以看到,再次拖放出来,已经有我们自己给他添加的物体了。
8.接下来,我们继续深入,认识里面的Update函数,这个Update函数和Unity的Update函数差不多,都是逐帧刷新的。我们通过尝试使用以下物理引擎来体验它!首先申明,这一部分内容在这里我们只要体验一下怎么用就行,暂时不做深究,会在以后的文章中接触到,现在只要跟着我做即可。
首先,在Update函数里面添加下面的代码:  
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。这时候我们调试,进入编辑器会没有任何反应。

9. 接着继续添加:
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。

10.然后继续添加:当人物走到这个区域的时候,做出一些反应,比如减少他的health值
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测试,具体如图:

FG(这部分具体可见以前的文章)

编辑器运行截图
运行的结果应该是这样的:当玩家走进我们设定的矩形区域,首先玩家生命值减少,然后同时玩家会被一股无形的力弹开
11.我们接着完善:玩家可以走进模型不被弹开,但是其他具有物理效果的entity走进可以被弹开。这里只用增加if语句里面的判断条件即可。if语句:    
    if (pQueryPhysEnt && (pQueryEnt != pEnt)&&(pQueryEnt != gEnv->pGame->GetIGameFramework()->GetClientActor()->GetEntity())&&(pQueryEnt->GetClass()!=gEnv->pEntitySystem->GetClassRegistry()->FindClass("MyTestEntity2")))

接着F5调试,我们发现玩家不会被弹开,走进区域内的玩家的生命值一直在减少。

接下来我们还可以更改代码里面的内容:比如扩大矩形区域的范围,让玩家的生命值减少的更快....这些都是些小玩意儿了,我就不多说了。
12.我把最终的CPP文件贴上来,供大家参考    
#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///
(二)接下来我还要说下一在本文开头提到的遇到的书中的问题。
其实看过书的朋友都知道,书中在用lua创建entity之后,讲解的是用C++直接创建entity,而不是我们这样建立GameObject Extensions的方法。但是书中描述的实在是太过苍白,我手边也没有源码,实在搞不清楚一些框架上的问题。这边我先把我做到的部分,和书中提到的部分写出来,如果可能的话希望有大婶们可以帮我们解惑。
1.在GameSDK项目文件或者任意它的子筛选器内建立文件MyEntityClass.h(这部分我就不明白书中的用意,好歹建一个筛选器管理哈o(╯□╰)o)
在.h文件中建立CMyEnityClass类,它的父类是IEntityClass
书中这里是直接建立的.h文件,然后写下的如下的一段代码    
#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();}
从这里我们都可以看出include后面不应该是尖括号,这里我们可以直接把""里面的内容删掉,只用留下IEntityClass.h即可
2.同样的,需要实现IEntityClass里面的所有纯虚函数。书上说,大部分的纯虚函数可以返回一个默认的空啊,1啊,什么的,有四个函数需要注意一下
(1).Realse函数,里面应该写delete this
(2)GetName函数,应该返回类名
(3)GetEditorClassInfo函数,这个函数要返回ClassInfo的结构体包括对应于编辑器的entity所在目录,helper(?不知道是什么),还有icor,这个好说是图标。
(4)SetEditorClassInfo函数。这个函数在编辑器更新的时候有时候会调用。
然后,书中到这里关于MyClassEntity类的内容就完全没有了。我们继续看书中的内容,再看我碰到的问题。
3.实现了上面的纯虚函数,还需要在GameFactory的InitGameFactory函数里面注册类。然后就直接给了我们这么一段代码    
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了。

这里遇到第一个问题就是,new一个CMyEntityClass的对象,居然给构造函数里面传了个IEntityClassRegistry::SEntityClassDesc类型的参数,而这个参数还居然是个结构体?那么问题就来了,这个结构体和我们的类是什么关系?难道要在我们的类里面实例化一个它的成员变量吗?........
这样吧,我先把内容跟新到这里,大家不妨先按着书中的方法做一下,看看是否会出现问题,或者出现怎样的问题,下次更博的时候,我再把我的解决方法和遇到的新问题列出来。
新年新气象~祝大家在新的一年开开心心~  

CryENGINE3初探Entities (二)----使用C++创建自定义Entity(一)相关推荐

  1. ROS实战篇(二)如何创建自定义的msg文件以及自己编写launch文件?

    一.如何创建自定义的msg文件? msg文件介绍: 1.msg文件是用来描述ROS中自定义的消息类型的,可以被不同语言调用. 2.msg文件以 .msg 结尾,必须存放在软件包的msg文件夹下. 3. ...

  2. CryENGINE3初探Flowgraph(二)----FG简单概念及操作流程

    接着上篇博客,我们通过简单控制TimeOfDay来一步步深入FG. (一)完成对TimeOfDay简单控制的实例 1.在场景中添加FlowgraphEntity. CE3大部分FG都是依托在Entit ...

  3. MATLAB强化学习实战(十二) 创建自定义强化学习算法的智能体

    创建自定义强化学习算法的智能体 创建环境 定义策略 自定义智能体类 智能体属性 构造函数 相关函数 可选功能 创建自定义智能体 训练自定义智能体 自定义智能体仿真 本示例说明如何为您自己的自定义强化学 ...

  4. cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单

    1.在项目中添加引用:AcCui.dll,这个DLL存放在CAD的安装目录中.但是,好像CAD2007之前的版本里没有这个DLL),我电脑上安装了CAD2004.CAD2005.CAD2008.CAD ...

  5. cad自定义菜单cui_AutoCAD.NET二次开发:创建自定义菜单(AcCui)

    从CAD2007之后,Autodesk提供了一个新的程序集AcCui.dll,使用这个程序集,我们可以方便地做一些界面方面的操作,比如创建自定义菜单. 下面介绍一下菜单的创建过程: 1.在项目中添加引 ...

  6. ROS话题通信中创建自定义数据类型的两种方式

    一.在同一个功能包下创建.msg文件 1.在功能包目录下创建msg文件夹 2.在文件夹中创建.msg文件 3.在该功能包的package.xml文件中加入: <build_depend>m ...

  7. 独家 | 使用TensorFlow 2创建自定义损失函数

    作者:Arjun Sarkar 翻译:陈之炎 校对:欧阳锦 本文约1900字,建议阅读8分钟 本文带你学习使用Python中的wrapper函数和OOP来编写自定义损失函数. 标签:TensorFlo ...

  8. 使用tolua++编译pkg,从而创建自定义类让Lua脚本使用

    2019独角兽企业重金招聘Python工程师标准>>> 在Lua第三篇中介绍了,如何在cocos2dx中使用Lua创建自定义类供Lua脚本调用使用,当时出于Himi对Lua研究不够深 ...

  9. Docker的使用初探(二):Docker与.NET Core的结合

    目录 Docker的使用初探(二):Docker与.NET Core的结合 添加Dockefile 1. 在创建项目时添加 2. 手动添加 3. 容器业务流程协调控制程序支持 Dockefile语法 ...

最新文章

  1. 全职院士32人!这些大学,正创造奇迹!
  2. Socket网络通讯_TCP协议
  3. 赠书:聊聊「分布式架构」那些事儿
  4. 【Flutter】Flutter 开源项目参考
  5. windows IIS权限经典设置教程
  6. 3.2 matlab用if语句实现选择结构
  7. 异想-天开 python---while、for、if-else 循环学习
  8. 用Thread实现socket多线通讯
  9. vmware 搭建k8s无法ping通子节点_一波四折 —— 记一次K8S集群应用故障排查
  10. carplant_mxnet
  11. Mysql原理+ 多实例 +表损坏
  12. 2014年12月21号面试
  13. 值得收藏的微软Windows系统蓝屏代码大全以及修复方法
  14. Javaweb面试题整理
  15. 如何创建GOOGLE ADS的MCC经理账户,有什么好处?
  16. Python上位机与C51单片机串口通信
  17. Win系统 - 如何查看电脑开机了多长时间?
  18. ZXR10 1809 路由器 1800开启WEB配置界面调试方法
  19. 【研究生】横扫13项中文NLP任务:香侬科技提出汉语字形表征向量Glyce+田字格CNN...
  20. composer类库—汉字转拼音

热门文章

  1. 无监督学习之层次聚类算法
  2. 四位计算机的原理及其实现
  3. 初级信息处理技术员access教程_软考初级信息处理技术员2018下半年上午试题与答案及解析...
  4. 中考计算机flash试题及答案,2014年中考信息技术题库试题Flash知识点.doc
  5. ffmpeg 音频拼接
  6. thinkphp网站提示缓存文件写入失败
  7. 这群程序员中的「广告狂人」,把抖音广告做成了AR游戏
  8. 飞秋下载2010正式版 飞秋下载
  9. 精密空调维护如何维护
  10. 【原创】用 Python 反编译 Python 软件