Je suis un programmeur inexpérimenté qui crée un jeu "roguelike-like" dans la veine de FTL , en utilisant Python (pas encore PyGame pour l'instant car je ne m'intéresse qu'au texte).
Mon jeu contiendra un grand nombre d'armes (environ 50 pour les débutants) qui donnent des capacités uniques. J'ai du mal à comprendre comment structurer le code objet d'une manière à la fois puissante (en termes de permettre aux armes d'avoir des effets radicalement différents) et extensible (afin que je puisse facilement ajouter plus d'armes plus tard, par exemple en les déposant dans un dossier ).
Mon premier réflexe a été d'avoir une classe BasicWeapon et d'avoir différentes armes héritées de cette classe. Cependant, cela me semble problématique: soit je dois rendre la classe BasicWeapon si simple qu'elle est fondamentalement inutile (les seules caractéristiques que toutes les armes ont en commun sont le nom et le type (pistolet, hache, etc.)), ou je dois prédire chaque effet unique que je trouverai et coderai dans BasicWeapon.
Ce dernier est clairement impossible, mais le premier peut encore être travaillé. Cependant, cela me laisse avec la question: où dois-je mettre le code pour les armes individuelles?
Dois-je créer plasmarifle.py, rocketlauncher.py, swarmofbees.py, etc etc, et les déposer tous dans un dossier d'où le jeu peut les importer?
Ou existe-t-il un moyen d'avoir un fichier de style base de données (peut-être quelque chose d'aussi simple qu'une feuille de calcul Excel) qui contient en quelque sorte un code unique pour chaque arme - sans avoir besoin de recourir à eval / exec?
En ce qui concerne cette dernière solution (base de données), je pense que le problème fondamental avec lequel je me bats est que même si je comprends qu'il est souhaitable de maintenir la séparation entre le code et les données, j'ai l'impression que les armes brouillent la ligne entre le "code" et "données" un peu; ils représentent la grande variété de choses similaires qui peuvent être trouvées dans le jeu, dans ce sens, elles sont comme des données, mais la plupart d'entre elles nécessiteront au moins un code unique qui ne sera partagé avec aucun autre élément, dans ce sens, elles sont, naturellement, code.
Une solution partielle que j'ai trouvée ailleurs sur ce site suggère de donner à la classe BasicWeapon un tas de méthodes vides - on_round_start (), on_attack (), on_move () etc. - puis de remplacer ces méthodes pour chaque arme. À la phase pertinente du cycle de combat, le jeu appellera la méthode appropriée pour l'arme de chaque personnage, et seules celles qui ont des méthodes définies feront réellement quelque chose. Cela aide, mais cela ne me dit toujours pas où je dois mettre le code et / ou les données pour chaque arme.
Existe-t-il un langage ou un outil différent que je peux utiliser comme une sorte de chimère mi-données, mi-code? Suis-je en train d'abattre complètement les bonnes pratiques de programmation?
Ma compréhension de la POO est au mieux sommaire, donc j'apprécierais des réponses qui ne sont pas trop informatiques.
EDIT: Vaughan Hilts a clairement indiqué dans son article ci-dessous que ce dont je parle essentiellement est une programmation basée sur les données. L'essence de ma question est la suivante: comment puis-je implémenter une conception basée sur les données de telle manière que les données puissent contenir des scripts, permettant à de nouvelles armes de faire de nouvelles choses sans changer le code du programme principal?
la source
Réponses:
Vous voulez une approche basée sur les données presque certainement, à moins que votre jeu ne soit complètement inattendu et / ou généré de manière procédurale.
Essentiellement, cela implique de stocker des informations sur vos armes dans un langage de balisage ou un format de fichier de votre choix. XML et JSON sont tous deux de bons choix lisibles qui peuvent être utilisés pour rendre l'édition assez simple sans avoir besoin d'éditeurs compliqués si vous essayez simplement de démarrer rapidement. ( Et Python peut également analyser XML assez facilement! ) Vous définiriez des attributs tels que «puissance», «défense», «coût» et «statistiques» qui sont tous pertinents. La façon dont vous structurez vos données dépendra de vous.
Si une arme doit ajouter un effet d'état, attribuez-lui un nœud d'effet d'état, puis spécifiez les effets d'un effet d'état via un autre objet piloté par les données. Cela rendra votre code moins dépendant du jeu spécifique et rendra l'édition et le test de votre jeu triviaux. Ne pas avoir à recompiler tout le temps est également un bonus.
Une lecture supplémentaire est disponible ci-dessous:
la source
(Je suis désolé de soumettre la réponse au lieu d'un commentaire, mais je n'ai pas encore de représentant.)
La réponse de Vaughan est excellente, mais j'aimerais ajouter mes deux cents.
L'une des principales raisons pour lesquelles vous souhaitez utiliser XML ou JSON et l'analyser lors de l'exécution est de modifier et d'expérimenter de nouvelles valeurs sans avoir à recompiler le code. Comme Python est interprété et, à mon avis, assez lisible, vous pourriez avoir les données brutes dans un fichier avec un dictionnaire et tout organisé:
De cette façon, il vous suffit d'importer le fichier / module et de l'utiliser comme un dictionnaire normal.
Si vous souhaitez ajouter des scripts, vous pouvez utiliser la nature dynamique de Python et des fonctions de 1ère classe. Vous pouvez faire quelque chose comme ça:
Bien que je pense que ce serait contraire à la conception basée sur les données. Pour être 100% DDD, vous auriez des informations (données) spécifiant quelles seraient les fonctions et le code qu'une arme spécifique utiliserait. De cette façon, vous ne cassez pas DDD, car vous ne mélangez pas les données avec les fonctionnalités.
la source
Conception basée sur les données
J'ai récemment soumis quelque chose comme cette question à la révision du code .
Après quelques suggestions et améliorations, le résultat a été un code simple qui permettrait une certaine flexibilité relative sur la création d'armes basée sur un dictionnaire (ou JSON). Les données sont interprétées au moment de l'exécution et de simples vérifications sont effectuées par la
Weapon
classe elle-même, sans avoir besoin de s'appuyer sur un interpréteur de script complet.La conception pilotée par les données, bien que Python soit un langage interprété (les fichiers source et de données peuvent être modifiés sans avoir besoin de les recompiler), semble être la bonne chose à faire dans des cas tels que celui que vous avez présenté. Cette question va plus en détail sur le concept, ses avantages et ses inconvénients. Il y a aussi une belle présentation sur Cornell University à ce sujet.
Par rapport à d'autres langages, tels que C ++, qui utiliseraient probablement un langage de script (tel que LUA) pour gérer l'interaction data x engine et les scripts en général, et un certain format de données (comme XML) pour stocker les données, Python peut réellement faire le tout seul (compte tenu de la norme
dict
mais aussiweakref
, cette dernière spécifiquement pour le chargement et la mise en cache des ressources).Un développeur indépendant, cependant, peut ne pas pousser à l'extrême l'approche basée sur les données comme suggéré dans cet article :
Peut-être qu'avec Python, on pourrait bénéficier du meilleur de l'approche orientée objet et orientée données, visant à la fois la productivité et l'extensibilité.
Traitement simple des échantillons
Dans le cas spécifique discuté lors de la révision du code, un dictionnaire stockerait à la fois les "attributs statiques" et la logique à interpréter - si l'arme avait un comportement conditionnel.
Dans l'exemple ci-dessous, une épée devrait avoir des capacités et des statistiques entre les mains des personnages de la classe `` antipaladin '', et aucun effet, avec des statistiques inférieures lorsqu'elles sont utilisées par d'autres personnages):
À des fins de test, j'ai créé des classes simples
Player
etWeapon
: la première pour tenir / équiper l'arme (appelant ainsi son paramètre on_equip conditionnel) et la seconde en tant que classe unique qui récupérerait les données du dictionnaire, en fonction du nom de l'élément passé en tant que lors de l'Weapon
initialisation. Ils ne reflètent pas la conception appropriée des classes de jeu, mais peuvent néanmoins être utiles pour tester les données:Avec quelques améliorations futures, j'espère que cela me permettra même d'avoir un jour un système d'artisanat dynamique, traitant des composants d'armes au lieu d'armes entières ...
Tester
Comme ça:
Il devrait imprimer:
Pour un barde
Pour un antipaladin
la source