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 и перенаправить их на свои компоненты и детей. И последнее, но не менее важное, это также можно пометить.
Создать новый GameObject так же просто, как:
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement(); Вернут действительный DonerComponents::CGameObject , если у него не хватает GameObjects. По умолчанию DonerComponents может одновременно иметь 4096 GameObjects. Это значение модифицируется через флаг компилятора -DMAX_GAME_OBJECTS=4096 с максимумом 8,192 GameObjects.
DonerComponents поддерживает определение Prefabs, поэтому пользователь может определить конкретную иерархию GameObject для повторного использования ее везде, где это необходимо:
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);Префабы также могут быть загружены из файла JSON.
DonerComponents::CComponent является базовым классом для любого компонента в DonerComponents. Компоненты определяют поведение GameObject путем агрегации. Они могут слушать конкретные сообщения и выполнять действия соответственно. Любой новый компонент должен наследовать от этого класса, и он может реализовать некоторые основные методы, если это необходимо. Пользователь может зарегистрировать до 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 . Строка, которую он получает, - это идентифицировать компонент при анализе наших GameObjects из файла JSON . Последние параметры - сколько компонентов будет доступно. Как и в случае с GameObjects, в то же время существует максимум 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 ); Все существующие CCompFoo будут обновляться последовательно перед обновлением всех существующих компонентов CCompBar .
Вы можете определить, какие данные будут подвергаться эксплуатации в JSON с помощью Donerserializer . Вы можете проверить здесь, как его использовать. Здесь я просто собираюсь показать пример.
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 поддерживает систему сообщений для взаимодействия между различными GameObjects и компонентами. Сообщение может быть любым структурой/классом, определенным пользователем. Обычно он будет содержать только данные, без логики, но в этом нет ограничений. Вот как 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 .
И последнее, но не менее важное: если вы хотите отправить сообщение на все живые GameObjects, вы можете использовать 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::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);
Теги - это способ добавить больше информации в ваши GameObjects, поэтому вы можете отфильтровать их, отправлять сообщения только в GameObjects с определенными тегами и т. Д. Есть два способа добавления тегов в систему, поэтому вы можете использовать их позже. Первый, объявляя их непосредственно в коде:
# 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 " );Формат файла Tags.json - это нечто похожее на это:
{ "tags" : [ " Tag1 " , " tag2 " , " tagN " ] }DonerComponents поддерживает загрузку с диска, используя JSON благодаря Donerserializer , поэтому существует способ создания сцена или сцен, которые можно хранить в качестве активов вместо того, чтобы строить их с нуля в коде каждый раз, когда мы запускаем наше приложение. Основное использование заключается в следующем:
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParseSceneFromFile( " path/to/your/scene.json " );Формат файла сцены.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 поддерживает Prefabs, которые включают в себя другие префабов, способные переопределить информацию своего компонента:
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 "
}
]
}
}Scene.json
{
"root" : {
"name" : " root " ,
"prefab" : " Prefab2 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 0 ,
"y" : 0 ,
"z" : 0
}
]
}
}