Bonjour à tous,
Je viens vous présenter le début d'un projet que j'ai débuté il y a un mois : YAPG, Yet Another Platformer Game (nom temporaire car je ne suis pas très inspiré sur le nom…).
Présentation
C'est en fait un jeu de plateformes (comme le nom l'indique ) qui a pour but d'être le plus moddable possible dans le sens où il est/sera possible de créer nos propres objets. Il sera donc possible de créer ses propres niveaux mais aussi ses propres environnements sans avoir à modifier le code source du jeu. Un éditeur de niveau sera inclu afin de permettre au plus grand nombre de créer ses niveaux. Par contre, la création des blocs/objets devra se faire à la main (donc réservé au plus persévérants).
Le jeu est codé en C++ et utilise SFML pour la gestion de la fenêtre et l'affichage, Entityx comme moteur d'ECS (Entity-Components-Systems), Boost.FileSystem, SFGUI ImGui pour les éléments d'interface graphique et Sol (un binding Lua en C++).
Le jeu est libre et sous licence GNU GPLv2 sur le dépôt Github YAPG. Les instructions pour compiler le jeu sont dans le wiki mais une version alpha de test est disponible pour Windows dans la section « Release » du dépôt.
==> YAPG compilé pour Windows x86 : Version 0.4.2-alpha pour Windows x86
Pour l'instant, 3 objets sont codés (dossier templates
de l'archive du jeu compilé) et un niveau de test est automatiquement chargé par le jeu au lancement. Vous pouvez ajouter d'autres objets en prenant pour modèle les objets déjà existants.
Mises à jour
- Version 0.1.1-alpha : Voir le message de la mise à jour
- Version 0.2.1-alpha : Voir le message de la mise à jour
- Version 0.3.0-alpha : Voir le message de la mise à jour
- Version 0.4.2-alpha : Voir le message de la mise à jour
Côté technique
Pour les curieux, je vais présenter le fonctionnement de la déclaration des objets et des métadatas.
Pour permettre la création d'objets/blocs customizés, les blocs sont décrits par des fichiers en Lua (dans le dossier template
). Par exemple, le joueur (pas encore complet) est décrit par le fichier suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | player = { name = "player", friendlyname = "Player", parameters = { x = { name = "X position", component = "Position", type = "number", attribute = "x", }, y = { name = "Y position", component = "Position", type = "number", attribute = "y", }, }, components = { ["Position"] = { x = 0, y = 0, z = 100, width = 64, height = 128 }, ["Platformer"] = { onIdle = function(entity) entity:set_string_attribute("Render", "current_animation", "idle") end, onStartWalking = function(entity) entity:set_string_attribute("Render", "current_animation", "walking") end, onStartJumping = function(entity) entity:set_string_attribute("Render", "current_animation", "jump") end, onStartFalling = function(entity) entity:set_string_attribute("Render", "current_animation", "jump") end, onTurnRight = function(entity) entity:set_bool_attribute("Render", "flipped", false) end, onTurnLeft = function(entity) entity:set_bool_attribute("Render", "flipped", true) end, }, ["Hitbox"] = { polygon = { points = { { x = 12, y = 0 }, { x = 52, y = 0 }, { x = 52, y = 128 }, { x = 12, y = 128 }, } } }, ["Render"] = { texture = "spritesheet_complete.png", current_animation = "idle", animations = { ["idle"] = { total_duration = 1, frames = { { rect = { left = 390, top = 1290, width = 128, height = 256}, relative_duration = 1, }, }, }, ["walking"] = { total_duration = 0.2, frames = { { rect = { left = 390, top = 516, width = 128, height = 256}, relative_duration = 1, }, { rect = { left = 390, top = 258, width = 128, height = 256}, relative_duration = 1, }, }, }, ["jump"] = { total_duration = 1, frames = { { rect = { left = 390, top = 1548, width = 128, height = 256}, relative_duration = 1, }, }, }, }, } } } |
L'utilisation de Lua permet de décrire des structures complexes et d'utiliser des fonctions de callback pour réagir à certains événements du jeu. La modification des attributs des composants est aisée depuis le code Lua en utilisant les méthodes set_string_attribute
, set_number_attribute
, … Dans l'exemple ci-dessus, lorsque le personnage saute, son animation est mise à "jump".
Du côté du code C++, il est facile pour les composants de déclarer des attributs qui peuvent être chargés, récupérer et modifié depuis le lua grâce à un système de Metadata. Par exemple, pour déclarer les attributs d'une animation (et comment charger une animation depuis le Lua) :
1 2 3 4 5 6 7 8 9 10 11 12 13 | //Déclaration de la classe Animation pour qu'elle puisse être chargés depuis le lua (et accédée/modifiés via les méthodes set_XXX_attribute...) meta::MetadataStore::registerClass<Animation>() .declareAttribute<float>("total_duration", &Animation::m_totalDuration) .declareAttribute<std::vector<Frame>>("frames", &Animation::m_frames) .setExtraLoadFunction([](Animation* anim, const sol::object& luaObject) { anim->normalizeFramesDurations(); //Doit être exécuté après le chargement de l'objet depuis lua }); //Déclaration de Frame meta::MetadataStore::registerClass<Frame>() .declareAttribute<sf::IntRect>("rect", &Frame::rect) .declareAttribute<float>("relative_duration", &Frame::relativeDuration); |
Voilà pour le moment. Si vous avez des avis/des conseils pour le jeu et son code source, je les attends avec impatience.