Le moteur Koala est un moteur de jeu entièrement implémenté en tant que système de composants entités (ECS).
Le moteur est basé sur Entt. L'intégration avec d'autres logiciels utilisant EnTT devrait être simple. Cette documentation suppose au moins la connaissance de base de EnTT et de sa terminologie ( entity , registry , handle ...).

L'exemple de projet présente certaines des fonctionnalités de base. Cela devrait vous donner une idée de ce que le support du moteur pour la réflexion et l'extensibilité d'exécution a à offrir.
Le moteur utilise des sous-modules Git et doit donc être cloné récursivement avec
git clone https://github.com/phisko/kengine --recursive
Le moteur a été testé sous Windows avec MSVC et Mingw.
La compilation Linux fonctionne avec GCC. Au moment de la rédaction du moment de la rédaction, Clang ne prend pas en charge le V ++ 20 constexpr std::string et std::vector .
Le moteur nécessite un compilateur C ++ 20 .
Le moteur a commencé comme un projet passion / étudiant en 2016-2017. Mes amis / collègues et moi nous sommes lancés dans la mise en œuvre d'une ECS à partir de zéro. Nous voulions la sécurité et la clarté de type absolu, et même si nous avions déjà joué avec la métaprogrammation des modèles, c'était une chance pour nous d'en savoir plus à ce sujet.
Une fois que le Core ECS fonctionnait, le moteur s'est transformé en un terrain de jeu pour en savoir plus sur le développement de jeux. J'ai appris le rendu OpenGL, comment utiliser Navmeshes avec refonte / détour, configurer la physique avec des balles ... tout en développant des aides utiles et des installations de réflexion d'exécution compatibles avec un ECS.
Après plus de 5 ans de travail sur le moteur, j'ai réalisé que le Core ECS lui-même n'était plus au centre de ce projet. D'autres bibliothèques, et EnTT en particulier, propose une API très similaire, avec des fonctionnalités beaucoup plus avancées. J'ai donc pris le temps de vider complètement les EC internes et de le remplacer par EnTT . Toutes les fonctionnalités restent les mêmes, ce qui me donnera plus de temps pour travailler sur des aides utiles, ce qui peut fonctionner avec d'autres projets EnTT .
De nombreuses parties du moteur (comme les systèmes de script ou l'éditeur d'entité IMGUI) utilisent l'API de réflexion putils . La plupart des composants dans les échantillons suivants sont donc définis comme réfléchissables.
Le code moteur est organisé en trois catégories:
Notez que systems ne sont pas des objets d'une classe spécifique. Les systèmes sont simplement des entités avec un composant d'exécution (ou tout ce dont ils ont besoin pour faire leur travail). L'entité vit alors dans le registry avec le reste de l'état de jeu. Cela permet aux utilisateurs d'introspecter les systèmes ou à leur ajouter un comportement comme toute autre entité.
Ces trois catégories sont divisées en différentes bibliothèques, par exemple:
Notez que certaines bibliothèques contiennent des sous-bibliothèques, par exemple:
La section CMake explique plus en détail comment travailler avec ces bibliothèques.
Le moteur est livré avec un nombre (assez grand) de composants prédéfinis qui peuvent être utilisés pour bootstrap un jeu, ou simplement comme des exemples sur lesquels vous pouvez baser vos propres implémentations.
Ces composants se situent dans trois catégories:
Les composants de données contiennent des données sur leur entité.
Les composants de données sont ce qui vient d'abord à l'esprit lorsque vous pensez à un composant, comme une transformation ou un nom.
Les composants de données peuvent parfois contenir des fonctions:
Les composants de la fonction maintiennent des fonctions pour interroger, modifier ou informer leur entité.
Les composants de la fonction sont simplement des détenteurs de fonds qui peuvent être attachés en tant que composants aux entités. Ce mécanicien peut être utilisé pour:
Les composants de fonction sont des types qui héritent de Base_Function, ce qui lui donne la signature de fonction comme paramètre de modèle.
Pour appeler un composant de fonction, on peut utiliser son operator() ou sa fonction call .
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); // AlternativelyLes composants méta sont des composants des composants.
Le moteur utilise des «entités de type» pour contenir des informations sur les différents composants utilisés. Chaque entité de type représente un type de composant différent et peut être utilisée pour interroger les propriétés du composant au moment de l'exécution.
Les méta-composants sont attachés à ces "entités de type" et maintiennent l'implémentation d'une fonction générique pour ce type spécifique. Parce qu'ils détiennent des fonctions, ils sont très similaires aux composants de la fonction.
Un exemple le rend plus clair: meta :: imgui :: edit est un composant méta qui, une fois appelé, dessinera les propriétés de son "composant parent" en utilisant IMGUI pour l'entité donnée. Le code suivant affichera une fenêtre pour modifier le composant de nom de e
// 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 ();Si vous généralisez cela, vous pouvez modifier tous les composants d'une entité avec le code suivant:
// 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 ();Voir CMake pour des instructions sur la façon d'activer chaque bibliothèque.
Un script Python généré_type_Registration est fourni, qui peut être utilisé pour générer des fichiers C ++ contenant des fonctions qui enregistreront un ensemble de types donnés avec le moteur.
Ce n'est absolument pas obligatoire .
Le moteur utilise Cmake comme système de construction. Un cadre personnalisé a été mis en place pour simplifier la création de bibliothèques. La racine CMakelists itère sur les sous-répertoires et les ajoute automatiquement en tant que bibliothèques si elles correspondent à quelques conditions.
Une bibliothèque d'interface de base kengine est créée qui se lie à toutes les bibliothèques activées, afin que les clients puissent simplement se lier à cela.
Les options CMake suivantes sont exposées.
KENGINE_TESTSCompile les exécutables de test pour les bibliothèques qui implémentent les tests.
KENGINE_NDEBUGDésactive le code de débogage.
KENGINE_TYPE_REGISTRATIONGénera le code d'enregistrement de type pour les types de moteurs. Ceci est au cœur de nombreuses capacités de réflexion du moteur, car il fournit la mise en œuvre des composants Meta.
KENGINE_GENERATE_REFLECTIONMettra à jour les en-têtes de réflexion pour les types de moteurs. Ceux-ci sont pré-générés, donc à moins que vous ne modifiiez le code source du moteur, vous ne devriez pas avoir besoin d'activer cela.
Toutes les bibliothèques sont désactivées par défaut, pour éviter de construire des dépendances indésirables. Chaque bibliothèque peut être activée individuellement en définissant son option CMake sur ON . Voir la dénomination de la bibliothèque pour le nom de l'option.
Alternativement, toutes les bibliothèques peuvent être activées avec l'option KENGINE_ALL_SYSTEMS .
Notez que les sous-bibliothèques ont besoin que leur bibliothèque parent soit activée: kengine_imgui_entity_editor nécessite kengine_imgui.
Les bibliothèques sont nommées en fonction de leur chemin relatif vers la racine du moteur. Les barres obliques dans le chemin sont simplement remplacées par des soulignements, par exemple:
kengine_corekengine_imgui_toolCes noms sont:
KENGINE_CORE pour kengine_core )KENGINE_CORE_EXPORT pour kengine_core )Il est possible de tester l'existence d'une bibliothèque pendant la compilation grâce à C ++ définir les macros. Ceux-ci ont le même nom que les options CMake, par exemple:
# ifdef KENGINE_CORE
// The kengine_core library exists
# endifCertaines bibliothèques utilisent VCPKG pour la gestion des dépendances.
Étant donné que les bibliothèques sont automatiquement détectées par Root CMakeLists.txt , la création d'une nouvelle bibliothèque est assez facile.
Les bibliothèques sont automatiquement liées à kengine_core , car il fournit des aides qui doivent être utilisées par toutes les bibliothèques (comme le log_helper et le profil_helper).
Les sous-bibliothèques sont automatiquement liées à leur parent. Par exemple, Kengine_Imgui_entity_Editor se relie automatiquement à KEngine_Imgui.
Les fichiers source des helpers et sous-répertoires systems d'une bibliothèque sont automatiquement ajoutés. Si aucune n'est trouvée, la bibliothèque sera une bibliothèque d'interface CMake.
L'enregistrement de type et le code de réflexion peuvent être générés automatiquement pour les composants. Par défaut, tous les en-têtes des sous-répertoires data et functions d'une bibliothèque seront transmis aux scripts de génération.
De façon similaire aux fichiers source, le cas échéant, les fichiers *.tests.cpp se trouvent dans helpers/tests ou sous-répertoires systems/tests d'une bibliothèque, un exécutable Googlest sera ajouté automatiquement.
CMakeLists.txt CUSTO Les bibliothèques de base ne devraient pas avoir besoin de leur propre CMakeLists.txt , car leurs fichiers source seront automatiquement. Cependant, si une bibliothèque a besoin d'un comportement personnalisé (par exemple, pour ajouter des sources supplémentaires ou pour être liées à une bibliothèque tierce), elle peut ajouter son propre CMakeLists.txt . Que CMakeLists.txt sera appelé après l'appel à add_library .
Les variables et fonctions suivantes sont définies avant d'appeler les CMakeLists.txt :
kengine_library_name : le nom de la bibliothèquekengine_library_tests_name : le nom de la cible Googlest de la bibliothèquelink_type : le type de lien de la bibliothèque ( PUBLIC ou INTERFACE , selon que les sources ont été trouvées ou non)kengine_library_link_public_libraries(libraries) : liens contre d'autres bibliothèques (publiquement)kengine_library_link_private_libraries(libraries) : liens contre d'autres bibliothèques (privé)register_types_from_headers(headers) : ajoute des en-têtes pour lesquels les en-têtes d'enregistrement et de réflexion peuvent être généréssubdirectory_is_not_kengine_library(path) : indique à la racine CMakeLists.txt qu'elle ne devrait pas traiter path