Die Koala -Engine ist eine Game -Engine, die vollständig als Entity Component System (ECS) implementiert ist.
Der Motor basiert auf Entt. Die Integration mit anderen Software -Teilen, die mit EnTT unkompliziert sein. Diese Dokumentation setzt mindestens grundlegendes Wissen über EnTT und seine Terminologie ( entity , registry , handle ...) voraus.

Das Beispielprojekt zeigt einige der Kernfunktionen. Es sollte Ihnen eine Vorstellung davon geben, was die Unterstützung des Motors für Reflexion und Laufzeit -Erweiterbarkeit zu bieten hat.
Der Motor verwendet Git -Submodule und sollte daher rekursiv mit kloniert werden
git clone https://github.com/phisko/kengine --recursive
Die Motor wurde mit MSVC und Mingw unter Windows getestet.
Linux -Kompilierung funktioniert mit GCC. Zum Zeitpunkt des Schreibens unterstützt Clang C ++ 20's constexpr std::string und std::vector nicht.
Der Motor benötigt einen C ++ 20 Compiler.
Der Motor begann 2016-17 als Leidenschaft/Studentenprojekt. Meine Freunde/Kollegen und ich haben versucht, ein ECS von Grund auf umzusetzen. Wir wollten absolute Sicherheit und Klarheit, und obwohl wir bereits mit Vorlagen -Metaprogrammierungen gespielt hatten, war dies eine Chance für uns, mehr darüber zu erfahren.
Sobald der Kern -ECS arbeitete, verwandelte sich der Motor in einen Spielplatz, um mehr über die Spieleentwicklung zu erfahren. Ich habe OpenGL -Rendering gelernt, wie man Navi mit Neuaufbau/Umweg, Einrichtung der Physik mit Kugel verwendet ... während ich nützliche Helfer und Laufzeitreflexionsanlagen entwickelte, die mit einem ECS kompatibel sind.
Nachdem ich über 5 Jahre am Motor gearbeitet hatte, stellte ich fest, dass die Kern -ECS selbst nicht mehr im Mittelpunkt dieses Projekts sind. Andere Bibliotheken und insbesondere EnTT bieten eine sehr ähnliche API mit viel fortgeschritteneren Funktionen. Also nahm ich mir die Zeit, um die internen ECs vollständig auszuweichen und sie durch EnTT zu ersetzen. Alle Funktionen bleiben gleich, und dies gibt mir mehr Zeit, um an nützlichen Helfern zu arbeiten, was mit anderen EnTT -Projekten zusammenarbeiten kann.
Viele Teile des Motors (wie die Skriptsysteme oder der IMGUI -Entitätseditor) nutzen die Reflexions -API von putils . Die meisten Komponenten in den folgenden Stichproben werden somit als reflektierbar definiert.
Der Motorcode ist in drei Kategorien organisiert:
Beachten Sie, dass systems keine Objekte einer bestimmten Klasse sind. Systeme sind einfach Entitäten mit einer ausführenden Komponente (oder alles andere, was sie für ihre Arbeit benötigen). Das Unternehmen lebt dann im registry mit dem Rest des Spielstaates. Dadurch können Benutzer Systeme introspect Systems oder Verhaltensweisen genau wie jede andere Einheit hinzufügen.
Diese drei Kategorien werden in verschiedene Bibliotheken aufgeteilt, z. B.:
Beachten Sie, dass einige Bibliotheken Unterbriebraren enthalten, z. B.:
Der CMAKE -Abschnitt geht detaillierter aus, wie man mit diesen Bibliotheken arbeitet.
Die Engine verfügt über eine (ziemlich große) Anzahl von vorgefertigten Komponenten, mit denen ein Spiel gestartet werden kann, oder einfach als Beispiele, auf denen Sie Ihre eigenen Implementierungen stützen können.
Diese Komponenten passen in drei Kategorien:
Datenkomponenten enthalten Daten über ihre Entität.
Datenkomponenten fällt dem zuerst in den Sinn, wenn man an eine Komponente wie eine Transformation oder einen Namen denkt.
Datenkomponenten können manchmal Funktionen enthalten:
Funktionskomponenten halten Funktionen zur Abfrage, Änderung oder Benachrichtigung ihrer Entität.
Funktionskomponenten sind einfach Inhaber für Funktoren, die als Komponenten an Entitäten angeschlossen werden können. Dieser Mechaniker kann verwendet werden, um:
Funktionskomponenten sind Typen, die von Base_function erben und die Funktionssignatur als Vorlagenparameter geben.
Um eine Funktionskomponente aufzurufen, kann man seinen operator() oder seine call verwenden.
entt::registry r;
const auto e = r.create();
r.emplace<main_loop::execute>(e,
[]( float delta_time) { std::cout << " Yay! " << std::endl; }
);
const auto & execute = r.get<main_loop::execute>(e); // Get the function
execute ( 0 .f); // Call it with its parameters
execute.call( 42 .f); // AlternativelyMeta -Komponenten sind Komponenten für Komponenten.
Die Engine verwendet "Typentitäten", um Informationen über die verschiedenen verwendeten Komponenten zu enthalten. Jede Typentität stellt einen anderen Komponententyp dar und kann zur Abfrage der Eigenschaften der Komponente zur Laufzeit verwendet werden.
Meta -Komponenten werden an diese "Typentitäten" angeschlossen und halten die Implementierung einer generischen Funktion für diesen spezifischen Typ. Da sie Funktionen halten, sind sie den Funktionskomponenten sehr ähnlich.
Ein Beispiel macht dies klarer: meta :: iMgui :: bearbeiten ist eine Meta -Komponente, die beim Aufrufen seine Eigenschaften von "Elternkomponente" mit IMGUI für die angegebene Entität zeichnet. Der folgende Code zeigt ein Fenster an, um die Namenskomponente von e zu bearbeiten.
// r is a registry with the "type entity" for `name` already setup
const auto e = r.create();
r.emplace<core::name>(e);
const auto type_entity = type_helper::get_type_entity<core::name>(r);
const auto & edit = r.get<meta::imgui::edit>(type_entity);
if (ImGui::Begin( " Edit name " ))
edit ({ r, e });
ImGui::End ();Wenn Sie dies verallgemeinern, können Sie alle Komponenten für eine Entität mit dem folgenden Code bearbeiten:
// r is a registry with the "type entities" for all used components already setup
// e is an entity with an unknown set of components
if (ImGui::Begin( " Edit entity " ))
for ( const auto & [type_entity, edit] : r.view<meta::imgui::edit>()) {
edit ({ r, e });
}
ImGui::End ();In CMake finden Sie Anweisungen zum Aktivieren jeder Bibliothek.
Es wird ein Python -Skript für generate_type_registration bereitgestellt, mit dem C ++ - Dateien generiert werden können, die Funktionen enthalten, die eine Reihe von bestimmten Typen mit der Engine registrieren.
Dies ist absolut nicht obligatorisch .
Der Motor verwendet CMake als Build -System. Ein benutzerdefiniertes Framework wurde eingerichtet, um die Erstellung von Bibliotheken zu vereinfachen. Die Root Cmakelists iteriert über Unterverzeichnisse und fügt sie automatisch als Bibliotheken hinzu, wenn sie einigen Bedingungen entsprechen.
Es wird eine Basis kengine -Schnittstellenbibliothek erstellt, die mit allen aktivierten Bibliotheken verlinkt, sodass Clients einfach dagegen verknüpfen können.
Die folgenden CMAKE -Optionen sind freigelegt.
KENGINE_TESTSKompiliert Test ausführbare Ausführungen für die Bibliotheken, die Tests implementieren.
KENGINE_NDEBUGDeaktiviert Debuggcode.
KENGINE_TYPE_REGISTRATIONGenerieren Sie Typ -Registrierungscode für Motortypen. Dies ist von zentraler Bedeutung für viele Reflexionsfunktionen des Motors, da sie die Implementierung für Meta -Komponenten bietet.
KENGINE_GENERATE_REFLECTIONAktualisieren Sie die Reflexionsheader für Motortypen. Diese sind vorgeneriert. Wenn Sie den Quellcode der Engine nicht ändern, sollten Sie dies nicht aktivieren.
Alle Bibliotheken sind standardmäßig deaktiviert, um zu vermeiden, dass unerwünschte Abhängigkeiten aufgebaut werden. Jede Bibliothek kann individuell aktiviert werden, indem ihre CMAKE -Option ON wird. Siehe Bibliotheksnamen für den Optionsnamen.
Alternativ können alle Bibliotheken mit der Option KENGINE_ALL_SYSTEMS aktiviert werden.
Beachten Sie, dass Unterbibliotheken ihre übergeordnete Bibliothek aktiviert werden müssen: Kengine_imgui_entity_editor benötigt Kengine_imgui.
Bibliotheken werden je nach relativem Weg zur Motorwurzel benannt. Die Schrägstriche im Pfad werden einfach durch Unterstriche ersetzt, z. B. durch Unterstriche:
kengine_corekengine_imgui_toolDiese Namen sind:
KENGINE_CORE für kengine_core )KENGINE_CORE_EXPORT für kengine_core )Es ist möglich, auf die Existenz einer Bibliothek während der Zusammenstellung zu testen, dank C ++ Define Makros. Diese haben den gleichen Namen wie die CMAKE -Optionen, z. B. den gleichen Namen: z.
# ifdef KENGINE_CORE
// The kengine_core library exists
# endifEinige Bibliotheken verwenden VCPKG für das Abhängigkeitsmanagement.
Da Bibliotheken automatisch von den root CMakeLists.txt erkannt werden, ist das Erstellen einer neuen Bibliothek ziemlich einfach.
Bibliotheken verknüpfen automatisch mit kengine_core , da sie Helfer bietet, die von allen Bibliotheken verwendet werden sollten (z. B. log_helper und Profiling_helper).
Unterbibliotheken verknüpfen automatisch mit ihren Eltern. Zum Beispiel verbindet Kengine_imgui_entity_editor automatisch mit Kengine_imgui.
Quelldateien aus den helpers und systems einer Bibliothek werden automatisch hinzugefügt. Wenn keine gefunden wird, handelt es sich bei der Bibliothek um eine CMAKE -Schnittstellenbibliothek.
Typregistrierungs- und Reflexionscode kann automatisch für Komponenten generiert werden. Standardmäßig werden alle Header in data und functions einer Bibliothek an die Generierungsskripte übergeben.
Ähnlich wie bei *.tests.cpp finden Sie in den helpers/tests oder systems/tests einer Bibliothek Subditionorien, falls vorhanden.
CMakeLists.txt Grundlegende Bibliotheken sollten nicht ihre eigenen CMakeLists.txt benötigen, da ihre Quelldateien automatisch erfolgen. Wenn eine Bibliothek jedoch ein individuelles Verhalten benötigt (z. B. um zusätzliche Quellen hinzuzufügen oder sich mit einer Bibliothek von Drittanbietern zu verbinden), kann sie seine eigenen CMakeLists.txt hinzufügen. Das CMakeLists.txt wird nach dem Anruf bei add_library aufgerufen.
Die folgenden Variablen und Funktionen werden vor dem Aufrufen der CMakeLists.txt definiert:
kengine_library_name : Der Name der Bibliothekkengine_library_tests_name : Der Name des Googletest -Ziels der Bibliotheklink_type : Der Linktyp der Bibliothek ( PUBLIC oder INTERFACE , je nachdem, ob Quellen gefunden wurden oder nicht)kengine_library_link_public_libraries(libraries) : Links gegen andere Bibliotheken (öffentlich)kengine_library_link_private_libraries(libraries) : Links gegen andere Bibliotheken (privat)register_types_from_headers(headers) : Fügt Header hinzu, für die Registrierungs- und Reflexionsheader generiert werden könnensubdirectory_is_not_kengine_library(path) : Zeigt den root CMakeLists.txt an, dass es keinen path als Kengine -Bibliothek verarbeiten sollte