DonerComponents ist ein C ++ 14-Komponenten-basierte Spielobjektmodells-Framework für die Entwicklung von Videospielen.
Wenn Sie mit diesem Konzept nicht vertraut sind, empfehle ich Ihnen, einen Blick auf das Beispielprojekt oder das Tutorial zu werfen und es auszuprobieren!
Hier können Sie stabile Veröffentlichungen erwerben.
Alternativ können Sie die aktuelle Entwicklungsversion mit:
git clone https://github.com/Donerkebap13/DonerComponents.git
Denken Sie daran, git submodule update --init --recursive danach auszuführen.
Sie können mich direkt per E -Mail kontaktieren. Wenn Sie einen Vorschlag haben oder Fehler finden, zögern Sie bitte nicht, ein neues Problem zu erstellen.
DonerComponents_asteroids_example ist ein Beispielprojekt, das ich erstellt habe, um zu zeigen, wie DonerComponents verwendet werden. Es ist ein wirklich einfacher Asteroiden-Warm-Klon. Meine Absicht mit diesem Projekt ist es, alle Funktionen des Rahmens zu zeigen, die derzeit unterstützt wird, z. B.:
Hier werde ich versuchen, die grundlegende Verwendung der Hauptsysteme von DonerComponents zu veranschaulichen. Nachdem Sie dies gelesen haben, haben Sie das Grundkenntnis darüber, wie die Dinge organisiert sind und wie sie verwendet werden können. Für ein tieferes Verständnis empfehle ich Ihnen, einen Blick auf das Beispielprojekt zu werfen.
CDonerComponentsSystems ist ein Singleton , das alle verschiedenen Systeme initialisiert und auf alle DonerComponents zugreifen kann. Es sollte initialisiert werden:
# include < DonerComponents/CDonerComponentsSystems.h >
DonerComponents::CDonerComponentsSystems::CreateInstance ();
DonerComponents::CDonerComponentsSystems::Get ()->Init();Aktualisiert:
float elapsed = ...;
DonerComponents::CDonerComponentsSystems::Get ()->Update(elapsed);Und zerstört:
DonerComponents::CDonerComponentsSystems::DestroyInstance (); DonerComponents::CGameObject ist DonerComponents Hauptdarsteller. Diese Klasse kann verschiedene DonerComponents::CComponent , das ihr Verhalten definiert. Es hat auch Informationen über seine Eltern und seine Kinder. Es kann auch POD -Nachrichten empfangen und an seine Komponenten und Kinder weiterleiten. Zu guter Letzt kann es auch markiert werden.
Das Erstellen eines neuen GameObject ist so einfach wie:
# include < DonerComponents/gameObject/CGameObject.h >
DonerComponents::CGameObjectManager* gameObjectManager = DonerComponents::CDonerComponentsSystems::Get()-> GetGameObjectManager ();
DonerComponents::CGameObject *gameObject = gameObjectManager-> GetNewElement (); GetNewElement(); Gibt einen gültigen DonerComponents::CGameObject zurück, solange es keine GameObjects mehr hat, um zu generieren. Standardmäßig können DonerComponents gleichzeitig 4096 GameObjects lebendig haben. Dieser Wert wird über den Compiler -Flag -DMAX_GAME_OBJECTS=4096 mit einem Maximum von 8,192 GameObjects verändert.
DonerComponents unterstützt die Definition von Vorfabern, sodass der Benutzer eine bestimmte GameObject -Hierarchie definieren kann, um sie wiederzuverwenden, wo es benötigt wird:
# include < DonerComponents/gameObject/CPrefabManager.h >
DonerComponents::CGameObjectManager* prefabManager = DonerComponents::CDonerComponentsSystems::Get()-> GetPrefabManager ();
prefabManager-> RegisterPrefab ( " prefabName " , anyGameObjectCreatedPreviously);Prefabs könnten ebenfalls aus einer JSON -Datei geladen werden.
DonerComponents::CComponent ist die Basisklasse für eine beliebige Komponente in DonerComponents. Komponenten definieren das Verhalten des GameObject durch Aggregation. Sie können bestimmte Nachrichten anhören und Aktionen entsprechend ausführen. Jede neue Komponente sollte aus dieser Klasse erben und kann bei Bedarf einige grundlegende Methoden implementieren. Der Benutzer kann bis zu 512 verschiedene Komponenten registrieren.
Hier ist ein Beispiel für eine neue Komponentenerstellung. Die Implementierung aller Methoden ist optional :
# 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;
};Um diese Komponente im System zu registrieren, sodass jeder GameObject es verwenden kann, müssen wir Folgendes tun:
# include < DonerComponents/component/CComponentFactoryManager.h >
static constexpr int amountOfFooComponentsAvailable = 512 ;
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, amountOfFooComponentsAvailable); Nach der Initialisierung der DonerComponents::CDonerComponentsSystems können wir unsere Komponenten mithilfe des Makro ADD_COMPONENT_FACTORY in das System registrieren. Die von ihm empfangene Zeichenfolge besteht darin, die Komponente zu identifizieren und gleichzeitig unsere GameObjects aus einer JSON -Datei zu analysieren. Die letzten Parameter sind, wie viele Komponenten verfügbar sein werden. Wie bei den GameObjects gibt es maximal 8,192 Komponenten derselben Art, die gleichzeitig lebendig ist.
Sobald ein Komponet in das System registriert ist, kann er auf zwei verschiedene Arten einem GameObject hinzugefügt werden:
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 " );In DonerComponents werden Komponenten nach Typ, jeweils ein Typ, in der Reihenfolge, in der sie in das System registriert wurden, aktualisiert. Also im Beispiel:
ADD_COMPONENT_FACTORY ( " foo " , CCompFoo, 128 );
ADD_COMPONENT_FACTORY ( " bar " , CCompBar, 128 ); Alle vorhandenen CCompFoo werden nacheinander aktualisiert, bevor alle vorhandenen CCompBar -Komponenten aktualisiert werden.
Sie können definieren, welche Daten mithilfe von Donersserializer in JSON geändert werden. Sie können hier überprüfen, wie Sie es verwenden. Hier werde ich nur ein Beispiel zeigen.
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 " )
) Nachdem m_dummy1 und m_dummy2 freigelegt haben, können wir ihre Werte in JSON so definieren:
{
"root" : {
"name" : " test1 " ,
"components" : [
{
"name" : " foo " ,
"dummy1" : 1.0 ,
"dummy2" : [ " Test1 " , " Test2 " , " Test3 " ]
}
]
}
}Für einen detaillierteren Blick darauf, wie Sie von JSON lesen können, überprüfen Sie dies .
DonerComponents unterstützt ein Nachrichtensystem, um zwischen verschiedenen GameObjects und Komponenten zu interagieren. Eine Nachricht kann jede vom Benutzer definierte Struktur/Klasse sein. Normalerweise enthält es nur Daten, keine Logik, aber es gibt keine Einschränkung dafür. So kann ein DonerComponents::CComponent eine bestimmte Nachricht anhören:
// 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) {
// ...
}Nachdem Sie die gewünschten Nachrichten registriert haben, können Sie mit dem Senden von Nachrichten wie folgt beginnen:
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 sendet die Nachricht sofort im selben Frame. Wenn Sie das Senden der Nachricht bis zum Ende des Frame verzögern möchten, verwenden Sie stattdessen PostMessage .
Wenn Sie eine Nachricht an alle lebenden GameObjects senden möchten, können Sie BroadcastMessage verwenden:
SDummyMessage message ( 2 , 3 );
// This will propagate the message to all GameObjects alive.
gameObjectManager-> BroadcastMessage (message); DonerComponents::CHandle sind eine Art einzelne Thread -Smart -Zeiger . Sie verweisen auf eine bestimmte DonerComponents::CGameObject oder DonerComponents::CComponent , in allen Momenten wissen, ob sie noch gültig sind oder nicht oder nicht, mit anderen Worten, ob sie woanders an einem anderen Ort im Code zerstört wurden. Die Größe eines DonerComponents::CHandle beträgt 32 Bit. Die Art und Weise, wie wir in DonerComponents arbeiten DonerComponents::CHandle besteht darin, dass wir nie Rohzeiger von DonerComponents::CGameObject oder DonerComponents::CComponent aufbewahren. Alle DonerComponents::CHandle können an einen DonerComponents::CGameObject oder DonerComponents::CComponent übertragen werden. Wenn die Besetzung gültig ist und das Element noch existiert, wird ein gültiger Zeiger auf das Element zurückgegeben. Ansonsten wird es nullptr zurückgeben. Hier ist ein Beispiel:
# 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.Außerdem können Sie Nachrichten über Handles senden. Wenn das Handle gültig ist, wird die Nachricht ordnungsgemäß ausgegeben. Andernfalls wird die Nachricht ignoriert:
DonerComponents::CHandle handle = gameObject;
SDummyMessage message ( 2 , 3 );
handle.SendMessage(message);
Tags sind eine Möglichkeit, Ihren GameObjects weitere Informationen hinzuzufügen. Daher können Sie sie filtern, Nachrichten nur an GameObjects mit bestimmten Tags usw. senden. Es gibt zwei Möglichkeiten, dem System Tags hinzuzufügen, sodass Sie sie später verwenden können. Erstens, die sie direkt im Code deklarieren:
# include < DonerComponents/tags/CTagsManager.h >
DonerComponents::CTagsManager* tagsManager = DonerComponents::CDonerComponentsSystems::Get()-> GetTagsManager ();
tagsManager-> RegisterTag ( " Tag1 " );
tagsManager-> RegisterTag ( " TagN " );Die zweite, die sie aus einer JSON -Datei analysiert:
tagsManager-> ParseTagsFromFile ( " path/to/your/tags.json " );Das Format der Datei tags.json ist etwas ähnlich wie folgt:
{ "tags" : [ " Tag1 " , " tag2 " , " tagN " ] }DonerComponents unterstützt dank DonerSerializer das Laden von der Festplatte mit JSON. Die grundlegende Verwendung ist wie folgt:
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParseSceneFromFile( " path/to/your/scene.json " );Das Format einer Szene.json -Datei ähnelt dies:
{
"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
}
]
}
} Wenn wir anstatt eine Szene zu analysieren, möchten wir einen Vorfeld analysieren, um ihn automatisch in DonerComponents::CPrefabManager zu registrieren, müssen wir nur ParsePrefabFromFile anrufen:
# include < DonerComponents/GameObjects/CGameObjectParser.h >
DonerComponents::CGameObjectParser parser;
CGameObject* gameObject = parser.ParsePrefabFromFile( " path/to/your/prefab.json " );Danach ist der Prefab für jede neue Parsen -Szene zur Verfügung.
DonerComponents unterstützt Prefabs, die andere Vorbereitungen enthält und die Informationen seiner Komponenten überschreiben können:
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 "
}
]
}
}Szene.json
{
"root" : {
"name" : " root " ,
"prefab" : " Prefab2 " ,
"components" : [
{
"name" : " comp_location " ,
"x" : 0 ,
"y" : 0 ,
"z" : 0
}
]
}
}