Eugola

Terraria-like

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

Bonjour,

Introduction en 3 étapes

Courte présentation

Je m’appelle Guillaume, j’ai 17 ans et je suis en terminale S-SI. Je touche un petit peu à tout en programmation depuis que j’ai 10 ans, avec souvent de grandes pauses entre différent projets. (Je ne suis spécialiste en rien)

Génèse du projet

Ce projet a initialement débuté en 2011-2012 (je ne m’en souviens plus du tout). En regardant les projets sur zeste de savoir, et après avoir fini de réaliser un site internet pour notre classe, je ne voulais pas m’arrêter là. Mais, faire des sites tout seul dans son coin, c’est pas très gratifiant et puis ça ne m’amuse pas du tout. Hors, comme c’est sur mon temps libre que je développe, pourquoi perdre du temps avec des choses que je n’aime pas ? Je me suis dit « mais mince, ils ont tous leur projet, il est passé où mon esprit qui inventait tout le temps ?? ».

Alors j’ai relancé VS 2015, j’ai téléchargé la SFML et c’est reparti pour ce projet qu’il me tient à cœur de finir un jour.

Le but du projet

Je souhaite réapprendre le C++, non pas pour arriver à un niveau professionnel mais pour m’imposer une rigueur dans la vie de tous les jours. Je trouve que c’est un bon moyen pour y arriver (en plus du travail personnel !).

De plus, j’aimerai beaucoup faire évoluer ce projet, dans un sens qui me plaît. C’est-à-dire : y ajouter des choses que je trouve amusante, mais le fin but de ce projet est un jour d’avoir un joueur qui pourrait y jouer un petit peu en y prenant du plaisir.

Pour cela, j’ai mis le projet sur github dans le but qu’une personne, ou plus, dans le meilleur des cas, puisse m’apporter des remarques sur mon code (qu’est-ce qui est bien/pas bien, comment je pourrai améliorer tout ça…).

Eugola

Il s’agit de ce que je définirai comme un terraria-like (bien que je n’ai joué que quelques minutes à ce jeu), ce qui signifie : terrains aléatoires, biome, personnage, classes, monstres de toute sortes, etc. Pleins de choses durs à programmer pour moi en somme.

Ce qui a déjà été fait

J’avais dans le passé déjà entreprit de reprendre ce projet, j’avais donc réécrit super proprement toutes les caractéristiques (j’avais rassemblé des tonnes de liens sur de nombreux sujets : la création d’un format de sauvegarde, l’intégration de l’eau, les collisions, la génération aléatoire, les shaders pour la lumière, le scrolling, le multijoueur etc..). Au final je n’ai presque rien fait de tout ça, puisque j’avais bloqué sur l’affichage des tuiles en fonction des tuiles qui sont à côté (et oui, cela semble facile mais pas tant que ça au final !), de plus que je me rends compte aujourd’hui que je vais devoir réécrire en partie ce que j’ai fait car l’orientation de chaque bloc sera intégré dans le fichier de sauvegarde, enfin bref, un problème passager en quelque sorte.

Cependant cela m’a laissé je dirai une bonne base pour recommencer ce projet (j’avais en parallèle expérimenté de nouvelles manières de programmer des jeux notamment avec les systèmes d’états que j’avais fait en C#) qui m’a permis de reprendre ce projet sans avoir de problèmes à comprendre pourquoi j’avais fait ça comme ça! De plus j’ai l’impression que maintenant certaines solutions me paraissent évidentes alors que dans le passé, ce n’était pas le cas.

L’état actuel

Pour l’instant, je ne fais que créer un terrain de … 17100 tuiles, qui change à chaque apparition, avec chaque tuile qui s’oriente selon les tuiles qui l’entourent. On peut aussi les générer à la volée, le terrain est donc bien prépare pour l’arrivé du scrolling au sein du projet. J'ai aussi fait d'autres choses (une classe pour écrire en binaire dans les fichiers, pour préparer le format de sauvegarde, etc.. rien de visible en image presque).

On a un système flexible pour ajouter très facilement de nouvelles tuiles dans le futur. Pour illustrer mon propos, voilà comment on communique au jeu l’existence d’un bloc actuellement (l’ajout de propriétés viendra plus tard) :

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<Tileset path="img/tileset.png">
    <path name="img/tileset.png"/>
    <tiles>
        <tile name="grass" id="0" type="1" xoffset="0" yoffset="0" frames="16" />
        <tile name="sand" id="1" type="1" xoffset="16" yoffset="0" frames="16" />
    </tiles>
</Tileset>

Facile, n’est-ce pas ? Mais bon, rien ne vaut une image, alors voilà ce que ça donne en visuel (rien de fou, mais bon):

eugola avancée 1

(sur cette photo la génération se fait à l’aide d’un simple rand, pas de perlin noise ou de choses comme ça)

Ce qui va être fait ensuite

Prochainement, j’ai prévu de travailler sur ces différents points: - Pouvoir permettre le scrolling à l’infini la carte - Ajouter ma génération aléatoire - Travailler un peu les textures - Ajouter un premier personnage dans ce jeu

Bien d’autres.. que je montrerai sur ce topic au fur et à mesure de mes avancées.

Mélange des grosses idées pour le jeu

  • Création d’un mode histoire
  • Ajout d’un mode multijoueur

D’autres remarques

Etant musicien (même si j’ai arrêté il y a deux ans), j’ai toujours eu envie de faire ma propre musique, j’aimerai bien pouvoir un jour y ajouter une musique que j’ai composée moi-même.

Il n’y a aucune pression sur mes épaules : les études étant largement prioritaires, il se peut que je finisse ce projet non pas cette année mais quand je serai en bac + 2,3, ou encore plus, qui sait.

J’aimerai beaucoup me mettre en contact avec un graphiste qui pourrait réaliser quelques images pour le projet (les tilesets, icônes, et pleins d’autres choses qui devraient vite se rajouter dans le futur) au gré de son envie.

De plus rien n’étant figé quant au gameplay, si des gens ont des propositions, ne pas hésiter !

De plus, si vous avez des questions techniques (ou pas !), il ne faut pas hésiter, j’y répondrai avec grand plaisir :)

Des vidéos de l’ancien état (avant de tout reprendre de zéro), si ça intéresse quelqu'un:

Ressources

  • Le github du projet (bien regardé la branche DEV)
  • Cahier des charges actuel (à venir)
  • Anciennes ressources (à venir)

Merci.

Édité par Unknown

Vive la science

+4 -0
Auteur du sujet

Merci !

Journal #1

Côté jeu

  • J'ai maintenant une gestion complète de toutes les erreurs qui peuvent apparaître dans mon code, à la fois prévues et imprévues: en plus de tout ça, un fichier log.txt contient tout les problèmes rencontrés ! que vous pourrez donc m'envoyer quand je distribuerai le jeu, plus facile pour moi de débug comme ça :) au moins c'est fait et dans le futur je n'aurai plus à le faire !
  • On peut maintenant faire défiler le terrain (haut et bas, droite et gauche)
  • J'ai ajouté le petit curseur bleu :P

quelques autres améliorations mais rien d'intéressant du point de vue utilisateur

Maintenant je travail sur un système d'entité pour pouvoir créer le personnage, et après lier la caméra au personnage et entamer les déplacements du personnages. Ensuite je m'attaque à la génération infinie dans tout les sens.

Côté expérimentations

j'ai échoué à implenté le FNV hash pour pouvoir donner un ID unique pour les composants du systèmes d'entité dans une constexpr, alors je l'ai implémenté comme ça:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using uint_64 = unsigned long long int;

uint_64 hash(const char* str) {
    uint_64 hashT = 0xcbf29ce484222325;
    for (char& c : std::string(str)) {
        hashT = hashT ^ c;
        hashT *= 0x100000001b3;
    }
    return hashT;
}


int main()
{
    std::cout << hash("test") << std::endl;
    std::cout << hash("Vitesse") << std::endl;
    std::cout << hash("Texture") << std::endl;
    std::cout << hash("Position") << std::endl;
    std::cout << hash("test") << std::endl;

    system("pause");

    return 0;
}

et en sortie:

1
2
3
4
5
18007334074686647077
10774659384423672060
2829778445906215252
13934449531345340970
18007334074686647077

Encore et toujours, les remarques sur le code sont les bienvenues !!

Édité par Unknown

Vive la science

+0 -0

Cette réponse a aidé l'auteur du sujet

Lu'!

C++ n'est certainement pas le choix le plus malin pour s'imposer de la rigueur.

Concernant ton code en lui-même.

  • pour tout ce qui est MachinManager, voir pourquoi c'est probablement une boulette,
  • sémantiques des classes + points suivants,
  • les defines pour les constantes, c'est mal,
  • tu as des pointeurs nus partout,
    • à plein d'endroits, ça devrait être des références,
    • à d'autres ces pointeurs sont responsables des ressources associées (pas bien),
    • conséquence du point précédent : niveau exception safety, c'est cassé,
    • et il y a des fuites,
    • et on peut créer sans problème des double-free,
  • aucun contrat sur les fonctions (typiquement sur les accès de tableaux),
  • for (pugi::xml_node_iterator it = tileset.begin(); it != tileset.end(); ++it){ -> for(auto xml_node : tileset){
  • quand l'implémentation par défaut d'un constructeur/d'un opérateur te convient, pas la peine de faire une implémentation tordu, demande sa génération,
  • quand une valeur n'a pas de raison d'être négative, il n'y a pas de raison d'utiliser un "int",
  • beaucoup de copie en entrée de fonction là où on voudrait des références const,
  • valeurs magiques.

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+2 -0
Auteur du sujet

Merci,

Je prends le temps de comprendre vos remarques avant d'essayer de les appliquer. Je me focus en priorité sur vos remarques avant d'ajouter de nouvelles choses.

Je travaille donc sur les différents points suivants en ce moment:

  • RAII
  • Mise à jour de mon code grâce aux nouveautés du C++ 11 et du C++ 14 (dans la mesure de ce que suis capable d'implémenter quand j'en vois l'utilité)
  • Les pointeurs, références, fuites, etc..
  • int en short et / ou unsigned quand cela est possible

Par contre , j'ai lu ce qu'était la sémantique des classes et le problème des MachinManager, mais je n'ai pas trop compris comment réorganiser mon code pour prendre les prendre en compte?

De plus la sémantique des classes me permet juste de dire "cette classe est une classe d'entité ou de valeur", en plus de me dire quelles opérations je dois pouvoir faire avec ma classe, non ?

Les defines et valeurs magiques, comment pourrait-on les remplacer sans define, faudrait-il mettre les defines en tant que membre const des classes dans lesquelles elles sont définies ? (mais dans ce cas là, je dois rajouter une multitude de get ?)

Merci beaucoup en tout cas, j'apprécie énormément vos remarques :)

Vive la science

+0 -0

Par contre , j'ai lu […] le problème des MachinManager, mais je n'ai pas trop compris comment réorganiser mon code pour prendre les prendre en compte?

Unknown

Quel est le rôle actuel de tes classes MachinManager ? Si tu trouves plusieurs tâches c'est que c'est découpable (et si je me souviens du survol, il y en a bien plusieurs ;) ). Une classe = une responsabilité (SRP).

De plus la sémantique des classes me permet juste de dire "cette classe est une classe d'entité ou de valeur", en plus de me dire quelles opérations je dois pouvoir faire avec ma classe, non ?

Unknown

La sémantique de la classe ne permet pas de dire cela. Elle le désigne juste. Tes classes ont telle ou telle sémantique, c'est juste que comme ça on peut mettre un nom dessus, et prendre connaissance des quelques obligations qu'on doit s'imposer pour les classes.

Ces obligations (qui sont citées dans le lien aussi) t'aident à garantir la robustesse de ta conception, typiquement en excluant les risques de copies/affectation sur des classes potentiellement polymorphe, ou encore dont on veut garantir l'unicité des instances, etc.

Les defines et valeurs magiques, comment pourrait-on les remplacer sans define, faudrait-il mettre les defines en tant que membre const des classes dans lesquelles elles sont définies ? (mais dans ce cas là, je dois rajouter une multitude de get ?)

Unknown

Brrrrrr … ça fait froid dans le dos. Why getters and setters are evil ? Le getter n'est pas nécessairement un problème terrible, mais il est beaucoup moins rarement nécessaire qu'il n'y paraît ;) . Le setter est souvent un odieux fouteur de merde.

Pour les constantes, on définit des valeurs const/constexpr. L'idée est de les mettre dans la portée où elles sont nécessaires (même quand c'est juste une fonction) et surtout de leur donner un nom clair. Après, je ne vois pas vraiment pour quelles valeurs tu aurais besoin de getters pour les sortir des portées où elles seraient déclarées. Mais si tu as un doute, tu peux toujours poser la question.

First : Always RTFM - "Tout devrait être rendu aussi simple que possible, mais pas plus." A.Einstein

+3 -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