DonerComponents é uma estrutura de modelo de objeto de jogo baseado em componentes C ++ 14 para o desenvolvimento de videogames.
Se você não está familiarizado com esse conceito, recomendo que você dê uma olhada no projeto de exemplo ou no tutorial e experimente!
Você pode adquirir lançamentos estáveis aqui.
Como alternativa, você pode conferir a versão atual de desenvolvimento com:
git clone https://github.com/Donerkebap13/DonerComponents.git
Lembre -se de executar git submodule update --init --recursive posteriormente.
Você também pode entrar em contato comigo diretamente por e -mail, se tiver alguma sugestão ou encontrar algum bug, não hesite em criar um novo problema.
DonerComponents_astroids_example é um projeto de exemplo que eu criei para mostrar como usar o donercomponentes . É um clone de asteróides realmente simples. Minha intenção com esse projeto é mostrar todos os recursos que a estrutura suporta agora, como:
Aqui vou tentar ilustrar o uso básico dos principais sistemas dos donercomponentes. Depois de ler isso, você terá o conhecimento básico sobre como as coisas são organizadas e como elas podem ser usadas. Para um entendimento mais profundo, recomendo que você dê uma olhada no projeto de exemplo.
CDonerComponentsSystems é um singleton que inicializa e fornece acesso a todos os sistemas diferentes donerConents . Deve ser inicializado:
# include < DonerComponents/CDonerComponentsSystems.h >
DonerComponents::CDonerComponentsSystems::CreateInstance ();
DonerComponents::CDonerComponentsSystems::Get ()->Init();Atualizado:
float elapsed = ...;
DonerComponents::CDonerComponentsSystems::Get ()->Update(elapsed);E destruído:
DonerComponents::CDonerComponentsSystems::DestroyInstance (); DonerComponents::CGameObject é o principal ator do donercomponents. Esta classe pode conter diferentes DonerComponents::CComponent que define seu comportamento. Ele também tem informações sobre seus pais e seus filhos. Também pode receber mensagens de pod e encaminhá -las para seus componentes e seus filhos. Por último, mas não menos importante, também pode ser marcado.
Criar um novo GameObject é tão simples quanto:
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement(); Retornará um DonerComponents::CGameObject , desde que não fique sem GameObjects para gerar. Por padrão, os donercomponentes podem ter 4096 GameObjects vivos ao mesmo tempo. Este valor é modificável através do sinalizador do compilador -DMAX_GAME_OBJECTS=4096 com um máximo de 8,192 GameObjects.
DonerComponents suporta a definição de pré -fabricados, para que o usuário possa definir uma hierarquia específica do GameObject para reutilizá -la onde for necessário:
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);Os pré -fabricados também podem ser carregados de um arquivo JSON.
DonerComponents::CComponent é a classe base para qualquer componente no donerComponents. Os componentes definem o comportamento do GameObject por agregação. Eles podem ouvir mensagens específicas e executar ações de acordo. Qualquer novo componente deve herdar desta classe e pode implementar alguns métodos básicos, se necessário. O usuário pode registrar até 512 componentes diferentes.
Aqui está um exemplo de uma nova criação de componentes. A implementação de todos os seus métodos é opcional :
# 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;
};Para registrar este componente no sistema, para que qualquer objeto GameObject possa usá -lo, precisamos fazer o seguinte:
# include < DonerComponents/component/CComponentFactoryManager.h >
static constexpr int amountOfFooComponentsAvailable = 512 ;
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, amountOfFooComponentsAvailable); Depois de inicializar os DonerComponents::CDonerComponentsSystems podemos começar a registrar nossos componentes no sistema usando o macro ADD_COMPONENT_FACTORY . A string que recebe é identificar o componente enquanto analisa nossos objetos de gameObjects a partir de um arquivo json . Os últimos parâmetros são quantos componentes estarão disponíveis. Assim como nos GameObjects, há um máximo de 8,192 componentes do mesmo tipo vivo ao mesmo tempo.
Depois que uma componente é registrada no sistema, ele pode ser adicionado a um GameObject de duas maneiras diferentes:
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 " );Nos donerComponents , os componentes são atualizados por tipo, um tipo de cada vez, na ordem em que foram registrados no sistema. Então, no exemplo:
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, 128 );
ADD_COMPONENT_FACTORY ( " bar " , CCompBar, 128 ); Todo CCompFoo existente será atualizado sequencialmente antes de atualizar todos os componentes CCompBar existentes.
Você pode definir quais dados serão expostos para serem modificados no JSON usando donerserializador . Você pode verificar aqui como usá -lo. Aqui, eu só vou mostrar um exemplo.
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 " )
) Depois de expor m_dummy1 e m_dummy2 , podemos definir seus valores em json como este:
{
"root" : {
"name" : " test1 " ,
"components" : [
{
"name" : " foo " ,
"dummy1" : 1.0 ,
"dummy2" : [ " Test1 " , " Test2 " , " Test3 " ]
}
]
}
}Para uma análise mais aprofundada de como ler do JSON , verifique isso .
O donerComponents suporta um sistema de mensagens para interagir entre diferentes objectos e componentes. Uma mensagem pode ser qualquer estrutura/classe definida pelo usuário. Geralmente, ele contém apenas dados, sem lógica, mas não há limitação para isso. É assim que um DonerComponents::CComponent pode ouvir uma mensagem específica:
// 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) {
// ...
}Depois de registrar as mensagens que você deseja, você pode começar a enviar mensagens como esta:
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 envia a mensagem imediatamente, no mesmo quadro. Se você deseja adiar o envio da mensagem até o final do quadro, use PostMessage .
Por último, mas não menos importante, se você deseja enviar uma mensagem a todos os objetos de jogo vivos, poderá usar BroadcastMessage :
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all GameObjects alive.
gameObjectManager-> BroadcastMessage (message); DonerComponents::CHandle é uma espécie de ponteiros inteligentes de threads . Eles apontam para um DonerComponents::CGameObject ou DonerComponents::CComponent , sabendo em todos os momentos se ainda são válidos ou não ou, em outras palavras, se tiverem sido destruídos em outro lugar do código. O tamanho de um DonerComponents::CHandle é de 32 bits. A maneira de trabalhar no donerComponents é que nunca armazenamos ponteiros crus de DonerComponents::CGameObject ou DonerComponents::CComponent , sempre armazenamos DonerComponents::CHandle , para que possamos verificar se o elemento para o qual eles apontam ainda é válido, por isso não acessamos os ponteiros. Qualquer DonerComponents::CHandle pode ser lançado para um DonerComponents::CGameObject ou DonerComponents::CComponent . Se o elenco for válido e o elemento ainda existir, ele retornará um ponteiro válido para o elemento. Caso contrário, ele retornará nullptr . Aqui está um exemplo:
# 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.Além disso, você pode enviar mensagens através de alças. Se a alça for válida, a mensagem será propagada corretamente. Caso contrário, a mensagem será ignorada:
DonerComponents::CHandle handle = gameObject;
SDummyMessage message ( 2 , 3 );
handle.SendMessage(message);
As tags são uma maneira de adicionar mais informações aos seus objetos de jogo, para que você possa filtrá -los, enviar mensagens apenas para objeto de gameObjects com tags específicas etc. Existem duas maneiras de adicionar tags ao sistema, para que você possa usá -las posteriormente. Primeiro, declarando -os diretamente no código:
# include < DonerComponents/tags/CTagsManager.h >
DonerComponents::CTagsManager* tagsManager = DonerComponents::CDonerComponentsSystems::Get()-> GetTagsManager ();
tagsManager-> RegisterTag ( " Tag1 " );
tagsManager-> RegisterTag ( " TagN " );O segundo, analisando -os de um arquivo json:
tagsManager-> ParseTagsFromFile ( " path/to/your/tags.json " );O formato do arquivo tags.json é algo semelhante a isso:
{ "tags" : [ " Tag1 " , " tag2 " , " tagN " ] }O donerComponents suporta o carregamento do disco usando o JSON, graças ao DonerSerializer ; portanto, há uma maneira de criar prefabs ou cenas que podem ser armazenados como ativos em vez de construí -los a partir do zero no código toda vez que executamos nosso aplicativo. O uso básico é o seguinte:
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParseSceneFromFile( " path/to/your/scene.json " );O formato de um arquivo cenário.json é algo semelhante a isso:
{
"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
}
]
}
} Se, em vez de analisar uma cena, queremos analisar um pré -fabricação para registrá -lo automaticamente em DonerComponents::CPrefabManager , só precisamos ligar para ParsePrefabFromFile :
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParsePrefabFromFile( " path/to/your/prefab.json " );Depois de fazer isso, o Prefab está disponível para qualquer nova cena analisada usar.
O donerComponents suporta pré -fabricados que incluem outros pré -fabricados, sendo capaz de substituir as informações de seu componente:
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 "
}
]
}
}Cena.json
{
"root" : {
"name" : " root " ,
"prefab" : " Prefab2 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 0 ,
"y" : 0 ,
"z" : 0
}
]
}
}