YAPG - Jeu de plateformes

Yet Another Platformer Game - Jeu de plateformes

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

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

YAPG

C'est en fait un jeu de plateformes (comme le nom l'indique :-p ) 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

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. :-)

Édité par victorlevasseur

+7 -0
Auteur du sujet

Intéressant ton projet, je le testerai dès que possible. :-)

Sauf que moi j'utilise des fichiers JSON pour tous les paramètres et des fichiers Lua pour leur comportement

J'avais pensé à faire une partie en YAML et une autre en Lua mais finalement, j'ai préféré tout mettre en Lua. Cela va me permettre de faire de l'"héritage" d'objet/bloc : Par exemple, un modèle qui décrit un "bloc" et d'autres fichiers qui décrivent des "bloc en herbe", "bloc en terre" et "bloc en pierre" où seule la texture est changée (mais la hitbox, la taille et tout le reste est identique). On aurait par exemple :

Fichier block.lua

 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
block = {
    name = "block",
    friendlyname = "Block",

    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,
            width = 64,
            height = 64
        },
        ["Platform"] = {

        },
        ["Hitbox"] = {
            polygon = {
                points = {
                    {
                        x = 0,
                        y = 0
                    },
                    {
                        x = 64,
                        y = 0
                    },
                    {
                        x = 64,
                        y = 64
                    },
                    {
                        x = 0,
                        y = 64
                    },
                }
            }
        },
        ["Render"] = {
            texture = "spritesheet_complete.png",
            current_animation = "default",
            animations = {
                ["default"] = {
                    total_duration = 1,
                    frames = {
                        {
                            rect = { left = 1560, top = 390, width = 128, height = 128},
                            relative_duration = 1,
                        },
                    },
                },
            },
        }
    }
}

Fichier grass_block.lua

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
grass_block = template.inherits(block, { 
    --La fonction template.inherits fusionnera les deux tables Lua (ici, blocks et la table "anonyme" ci-dessous)
    name = "grass_block",
    friendlyname = "Grass block",

    components = {
        ["Render"] = {
            animations = {
                ["default"] = {
                    total_duration = 1,
                    frames = {
                        {
                            rect = { left = 1400, top = 390, width = 128, height = 128}, --On change uniquement juste la portion de l'image utilisée pour ce bloc
                            relative_duration = 1,
                        },
                    },
                },
            },
        }
    }
})

Et ainsi de suite pour les autres blocs très similaires.

Édité par victorlevasseur

+2 -0

Tient on ne peux pas aller en arrière ni sauter ? … Après quelque seconde je me suis dit: Essaye les touche Q et Z … hm tout ça pour dire que c'est gentil pour les monsieurs qui utilise comme moi du qwertz :p

Sinon je trouve que ça à l'air sympas, à voir quand ça aura un peu plus évolué.

+1 -0
Auteur du sujet

Après quelque seconde je me suis dit: Essaye les touche Q et Z … hm tout ça pour dire que c'est gentil pour les monsieurs qui utilise comme moi du qwertz :p

Ne t'inquiète pas pour l'azerty, c'est temporaire (c'est codé en dur dans le système qui gère la physique et les déplacements du personnage). Pour la prochaine version alpha, je re-basculerai sur le flèches (toujours temporairement), comme ça, pas de jaloux. :-p

Je vais également améliorer le spawn du personnage (qui est actuellement placé dans le niveau comme un objet lambda, on peut faire un niveau sans joueur du coup) et je vais tenter de construire un niveau plus long (avec de nouveaux objets) pour montrer les capacités du jeu car le niveau d'exemple ne donne pas franchement envie… (mais c'est pas pratique sans éditeur de niveau pour le moment.)

Dans son état actuel, le jeu permet déjà de construire des choses sympas mais le manque de contenu/d'exemples vient ternir cette impression.

+0 -0
Auteur du sujet

Mise à jour 0.1.1-alpha

Je viens de publier une nouvelle version alpha qui modifie les choses suivantes : - plus besoin de placer une instance de joueur sur le niveau, le jeu les instance lui-même ; - ajout de quelques nouveaux blocs (jumpthru, pentes) - amélioration du niveau d'exemple - le jeu utilise les touches fléchées

==> Lien de téléchargement pour Windows x86

Attention à bien supprimer les fichiers de l'ancienne version.

Le niveau d'exemple.

Édité par victorlevasseur

+3 -0
Auteur du sujet

Je viens de finir de coder la possibilité de customizer les touches utilisées par le joueur. Le mouvement est maintenant géré par un système séparé du système qui gère la physique. Les deux communiquent via des booléens mis à vrai ou faux suivant si le joueur veut aller à droite, à gauche ou veut sauter. La configuration se fait pour le moment directement dans un fichier xml.

Prochaines étapes du développement :

  • Centrer la caméra sur le joueur
  • Possibilité d'avoir plusieurs joueurs en même temps (peu de choses à modifier car la plupart du code ne dépend pas du nombre de joueur, il faut juste modifier la partie qui charge un niveau)
  • Un menu où il sera dans un premier temps possible de choisir un niveau à jouer (en tapant son nom)
  • Un menu de configuration pour éditer les touches

En parallèle, je vais continuer le développement d'une GUI simple pour SFML qui s'appelle SimplGUI (sans "e", rien à voir avec le générateur de GUI Python, SimpleGUI). Pour le moment, elle ne contient qu'un widget "zone de texte" mais je compte bien y en rajouter d'autres pour les utiliser dans YAPG. (Lien Github, elle nécessite un compilo C++11 et SFML 2.1 au minimum).

Une zone de texte de SimplGUI.

Édité par victorlevasseur

+2 -0
Auteur du sujet

Mise à jour 0.2.1-alpha

Une nouvelle version est disponible. Elle propose un menu principal et un menu de configuration où il est possible de configurer les touches (par défaut en QWERTY). Les widgets des menu sont réalisé avec SFGUI (et pas SimplGUI comme cela avait été prévu), une bibliothèque assez complète pour réaliser des GUI dans SFML.

=> Lien de téléchargement pour Windows x86

Le menu principal et de configuration de YAPG.

Il y a une petite surprise sur le menu principal :

Indice : c'est en rapport avec le personnage qui court sur ce dernier.

+6 -0

Il y a une petite surprise sur le menu principal :

La possibilité de déplacer le perso en cliquant dessus :p
et accessoirement la possibilité de le perdre…

Je trouve super ! ça évolue bien.

Concernant le menu, si on redimensionne la fenêtre pour la mettre en grand, la partie de menu ne reste pas au centre et le logo devient un peu pixelisé.

Sinon depuis le level il serait bien de pouvoir revenir au menu principale (Mais je pense qu'une interface de "pause" est prévu).

[edit]
Si on écrit un nom de niveau inexistant, le programme plante.

Édité par WinXaito

+2 -0
Auteur du sujet

Concernant le menu, si on redimensionne la fenêtre pour la mettre en grand, la partie de menu ne reste pas au centre et le logo devient un peu pixelisé.

Il semblerait que la bibliothèque SFGUI (pour les éléments de GUI) s'adapte bien au changement de dimensions de la fenêtre, alors que le jeu en lui-même ne prend pas en compte cela pour le moment.

Si on écrit un nom de niveau inexistant, le programme plante.

En effet, je m'occuperai de la gestion des erreurs plus tard.

j'ai vu qu'on pouvait tomber à l'infini

Je vais aussi m'atteler à cela plus tard car je voudrais créer l'éditeur de niveau (en tout cas, une version basique de celui-ci) avant de continuer à améliorer le moteur de jeu. Cela permettra de créer plus facilement des niveaux de test.

Édité par victorlevasseur

+2 -0
Auteur du sujet

Voilà une première image de l'éditeur de niveau. Il n'est pas du tout fonctionnel pour le moment (impossible de charger un autre niveau, de placer des blocs).

L'éditeur de niveau (début).

Pour le moment, il y a 3 outils : ajouter des blocs, modifier des blocs existants (modifier leurs propriétés, leur position et les supprimer) et bouger la vue. Le panneau en dessus des 3 outils contient la liste des templates (modèles de blocs/d'objets) quand l'outil sélectionné est "Insert new entities" sinon les propriétés du bloc sélectionné quand l'outil "Modify placed entities" est sélectionné.

+6 -0
Auteur du sujet

Bonjour tout le monde,

J'ai pu bien avancer le projet ces derniers temps. L'éditeur de niveau approche désormais d'un stade que je qualifierai d' « utilisable ».

L'utilisateur peut ajouter des blocs sur le niveau, les déplacer et même modifier leur propriétés à la main : ce sont le plus souvent les position X et Y, mais cela peut être n'importe quel « paramètre » déclaré dans les modèles de blocs (on peut imaginer un objet « interrupteur » qui puisse avoir en paramètre une liste de blocs à détruire lors de son activation, etc.).

Voici une petite animation pour voir l'état actuel de l'éditeur :

L'éditeur de niveau à l'action !

J'ai pu aussi en parallèle écrire une première version de la documentation de modding du jeu. Elle est disponible sur ReadTheDocs (cela permet sa recompilation automatique à chaque modif sur Github) : http://yapg.readthedocs.org

Édité par victorlevasseur

+4 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte