DonerComponents是一个基于C ++的14个基于组件的游戏对象模型框架,用于开发电子游戏。
如果您不熟悉这个概念,我建议您查看示例项目或教程,然后尝试一下!
您可以在此处获取稳定的发行版。
另外,您可以通过以下方式查看当前开发版本
git clone https://github.com/Donerkebap13/DonerComponents.git
请记住,以后运行git submodule update --init --recursive 。
您也可以直接通过电子邮件与我联系,如果您有任何建议或发现任何错误,请随时创建一个新问题。
DonerComponents_asteroids_example是我创建的示例项目,以展示如何使用DonerComponents 。这是一个非常简单的小行星 - 北方克隆。我对该项目的目的是显示框架现在支持的所有功能,例如:
在这里,我将尝试说明Donercomponents主要系统的基本用法。阅读本文后,您将获得有关如何组织方式以及如何使用它们的基本知识。要深入了解,我建议您看看示例项目。
CDonerComponentsSystems是一个单身人士,可以初始化并访问所有DonerComponents不同系统。它应该初始化:
# include < DonerComponents/CDonerComponentsSystems.h >
DonerComponents::CDonerComponentsSystems::CreateInstance ();
DonerComponents::CDonerComponentsSystems::Get ()->Init();更新:
float elapsed = ...;
DonerComponents::CDonerComponentsSystems::Get ()->Update(elapsed);并被摧毁:
DonerComponents::CDonerComponentsSystems::DestroyInstance ();DonerComponents::CGameObject是Donercomponents的主要演员。该类可以包含不同的DonerComponents::CComponent ,以定义其行为。它还具有有关其父母和子女的信息。它还可以接收POD消息并将其转发到其组件和孩子。最后但并非最不重要的一点是,它也可以标记。
创建新的游戏对象很简单:
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement();只要它没有用完gameObject,就会返回有效的DonerComponents::CGameObject 。默认情况下,Donercomponents可以同时拥有4096个GameObject。通过编译器标志-DMAX_GAME_OBJECTS=4096可修改此值,最大为8.192 GAMEOBJECTS。
DonerComponents支持预制的定义,因此用户可以定义特定的游戏对象层次结构,以便在需要的任何地方重复使用:
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);预制也可以从JSON文件中加载。
DonerComponents::CComponent是Donercomponents中任何组件的基类。组件通过汇总来定义游戏对象的行为。他们可以收听特定的消息并相应地执行操作。任何新组件都应从此类中继承,并且可以在需要时实现一些基本方法。用户最多可以注册512个不同的组件。
这是新组件创建的示例。其所有方法的实现都是可选的:
# include < DonerComponents/component/CComponent.h >
class CCompFoo : public DonerComponents ::CComponent
{
public:
void DoInit () override { m_foo = 0 . 0f ; }
void DoUpdate ( float dt) override { m_foo += dt; }
void DoDestroy () override { m_foo = 0 . 0f ; }
void DoActivate () override { }
void DoDeactivate () override { }
private:
float m_foo;
};要在系统中注册此组件,因此任何GameObject都可以使用它,我们需要执行以下操作:
# include < DonerComponents/component/CComponentFactoryManager.h >
static constexpr int amountOfFooComponentsAvailable = 512 ;
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, amountOfFooComponentsAvailable);初始化DonerComponents::CDonerComponentsSystems之后,我们可以使用Macro ADD_COMPONENT_FACTORY开始将组件注册到系统中。它收到的字符串是在从JSON文件中解析我们的游戏对象时识别组件。最后一个参数是将有多少个组件可用。与GameObject一样,同一时间最多有8.192个组件。
将组件注册到系统中后,可以通过两种不同的方式将其添加到GameObject中:
DonerComponents::CComponent* component = gameObject->AddComponent<CCompFoo>();
// same as
CCompFoo* component = gameObject->AddComponent<CCompFoo>();
// or
DonerComponents::CComponent* component = gameObject-> AddComponent ( " foo " );
// same as
CCompFoo* component = gameObject-> AddComponent ( " foo " );在Donercomponents中,组件按类型进行更新,一次是一种类型,以将其注册到系统中。因此,在示例中:
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, 128 );
ADD_COMPONENT_FACTORY ( " bar " , CCompBar, 128 );在更新所有现有的CCompBar组件之前,所有现有的CCompFoo都将依次更新。
您可以使用DonerSerializer定义哪些数据将在JSON中进行修改。您可以在这里检查如何使用它。在这里,我将展示一个例子。
class CCompFoo : public DonerComponents ::CComponent
{
DONER_DECLARE_COMPONENT_AS_SERIALIZABLE (CCompFoo)
public:
CCompFoo ();
private:
float m_dummy1;
std::vector<std::string> m_dummy2;
};
DONER_DEFINE_REFLECTION_DATA (CCompFoo,
DONER_ADD_NAMED_VAR_INFO (m_dummy1, " dummy1 " ),
DONER_ADD_NAMED_VAR_INFO(m_dummy2, " dummy2 " )
)公开m_dummy1和m_dummy2之后,我们可以在JSON中定义它们的值:
{
"root" : {
"name" : " test1 " ,
"components" : [
{
"name" : " foo " ,
"dummy1" : 1.0 ,
"dummy2" : [ " Test1 " , " Test2 " , " Test3 " ]
}
]
}
}有关如何从JSON中阅读的更深入的查看。
Donercomponents支持一个消息系统,以在不同的游戏对象和组件之间进行交互。消息可以是用户定义的任何结构/类。通常,它只包含数据,没有逻辑,但是对此没有任何限制。这就是DonerComponents::CComponent可以收听特定消息的方式:
// Somewhere in your code
struct SDummyMessage {
SDummyMessage ( int foo, int bar)
: m_foo(foo), m_bar(bar) {}
int m_foo = 0 ;
int m_bar = 0 ;
}
// Inside your component
CCompFoo::RegisterMessages () {
RegisterMessage (&CCompFoo::OnDummyMessage);
}
void CCompFoo::OnDummyMessage ( const SDummyMessage& message) {
// ...
}注册了所需的消息后,您可以开始发送这样的消息:
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all gameObject's components.
gameObject-> SendMessage (message);
// This will propagate the message to all gameObject's components and its children's components.
gameObject-> SendMessage (message, DonerComponents::ESendMessageType::Recursive);
// This won't send the message to the current gameObject but it's children.
gameObject-> SendMessageToChildren (message);
// Same as before but recursively through all gameObject's children and children's children.
gameObject-> SendMessageToChildren (message, DonerComponents::ESendMessageType::Recursive); SendMessage在同一帧中立即发送消息。如果要延迟发送消息直到框架结束,请改用后PostMessage 。
最后但并非最不重要的一点是,如果您想向所有活着的游戏对象发送消息,则可以使用BroadcastMessage :
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all GameObjects alive.
gameObjectManager-> BroadcastMessage (message); DonerComponents::CHandle是一种单线智能指针。他们指出了特定的DonerComponents::CGameObject或DonerComponents::CComponent ,在任何时刻都知道它们是否仍然有效,或者,换句话说,是否已被代码中的其他地方销毁。 DonerComponents::CHandle的大小为32位。在Donercomponents中工作的方式是,我们从不存储Donercomponents的原始指针DonerComponents::CGameObject或DonerComponents::CComponent ,我们总是存储DonerComponents::CHandle ,因此我们可以检查他们指向的元素是否仍然有效,因此我们不访问悬而未决的指针。任何DonerComponents::CHandle可以铸造给DonerComponents::CGameObject或DonerComponents::CComponent 。如果演员表是有效的并且该元素仍然存在,则它将返回有效的指针到该元素。否则,它将返回nullptr 。这是一个例子:
# include < DonerComponents/handle/CHandle.h >
# include < DonerComponents/gameObject/CGameObjectManager.h >
using namespace DonerComponents ;
CHandle gameObjectHandle = m_gameObjectManager-> GetNewElement ();
if (gameObjectHandle) {
// CGameObjectManager has return a valid CGameObject
} else {
// CGameObjectManager has run out of CGameObjects
}
CGameObject* gameObject = gameObjectHandle;
// gameObject will be valid as gameObjectHandle points to an alive gameObject
m_gameObjectManager-> DestroyGameObject (&gameObject);
// gameObject is nullptr at this point
// gameObjectHandle == false as it points to a destroyed gameObject.另外,您可以通过手柄发送消息。如果手柄有效,则该消息将得到正确传播。否则,该消息将被忽略:
DonerComponents::CHandle handle = gameObject;
SDummyMessage message ( 2 , 3 );
handle.SendMessage(message);
标签是将更多信息添加到游戏对象的一种方式,因此您可以过滤它们,仅将消息发送给具有特定标签的GameObject。首先,用代码直接宣布它们:
# include < DonerComponents/tags/CTagsManager.h >
DonerComponents::CTagsManager* tagsManager = DonerComponents::CDonerComponentsSystems::Get()-> GetTagsManager ();
tagsManager-> RegisterTag ( " Tag1 " );
tagsManager-> RegisterTag ( " TagN " );第二个,从JSON文件中解析它们:
tagsManager-> ParseTagsFromFile ( " path/to/your/tags.json " );标签的格式。JSON文件与此类似:
{ "tags" : [ " Tag1 " , " tag2 " , " tagN " ] }DonerComponents支持DonerSerializer使用JSON从磁盘上加载,因此,有一种创建预制措施或场景的方式可以作为资产存储,而不是每次运行应用程序时都可以在代码中从scratch中构建它们。基本用法如下:
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParseSceneFromFile( " path/to/your/scene.json " );scene.json文件的格式与此类似:
{
"root" : {
"name" : " test1 " ,
"tags" : [ " tag1 " , " tag2 " , " tag3 " ],
"components" : [
{
"name" : " comp_location " ,
"x" : 1 ,
"y" : -3 ,
"z" : 9
},
{
"name" : " comp_rotation " ,
"radians" : 0.2
}
],
"children" : [
{
"name" : " test11 " ,
"tags" : [ " tag1 " , " tag3 " ]
},
{
"name" : " test12 " ,
"initiallyActive" : false
}
]
}
}如果没有解析场景,我们想解析预制件将其自动注册到DonerComponents::CPrefabManager ,我们只需要致电ParsePrefabFromFile :
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParsePrefabFromFile( " path/to/your/prefab.json " );执行此操作后,预制可用于使用任何新的解析场景。
Donercomponents支持包括其他预制符号的预制,能够覆盖其组件的信息:
prefab1.json
{
"root" : {
"name" : " Prefab1 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 1 ,
"y" : -3 ,
"z" : 9
},
{
"name" : " comp_rotation " ,
"radians" : 0.2
},
{
"name" : " sprite " ,
"texture" : " res/common/textures/asteroid_med.png "
}
]
}
}prefab2.json
{
"root" : {
"name" : " Prefab2 " ,
"prefab" : " Prefab1 " ,
"components" : [
{
"name" : " sprite " ,
"texture" : " res/common/textures/flower_big.png "
}
]
}
}场景
{
"root" : {
"name" : " root " ,
"prefab" : " Prefab2 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 0 ,
"y" : 0 ,
"z" : 0
}
]
}
}