J'ai un jeu fini, que je veux décliner dans d'autres versions. Ce seraient des jeux similaires, avec plus ou moins le même type de design, mais pas toujours, fondamentalement, les choses pourraient changer, parfois peu, parfois grandes.
Je voudrais que le code de base soit versionné séparément du jeu, de sorte que si je corrige un bug trouvé dans le jeu A, le correctif sera présent dans le jeu B.
J'essaie de trouver la meilleure façon de gérer cela. Mes premières idées sont les suivantes:
- Créez un
engine
module / dossier / autre, qui contient tout ce qui peut être généralisé et est 100% indépendant du reste du jeu. Cela comprendrait du code, mais aussi des actifs génériques partagés entre les jeux. - Mettez ce moteur dans son propre
git
référentiel, qui sera inclus dans les jeux en tant quegit submodule
La partie avec laquelle je me bats est de savoir comment gérer le reste du code. Disons que vous avez votre scène de menu, ce code est spécifique au jeu, mais la plupart a tendance à être générique et pourrait être réutilisé dans d'autres jeux. Je ne peux pas le mettre dans le engine
, mais le recoder pour chaque jeu serait inefficace.
Peut-être que l'utilisation d'une sorte de variation des branches git pourrait être efficace pour gérer cela, mais je ne pense pas que ce soit la meilleure façon de procéder.
Quelqu'un a-t-il des idées, de l'expérience à partager ou quelque chose à ce sujet?
Réponses:
C'est exactement ce que je fais et cela fonctionne très bien. J'ai un cadre d'application et une bibliothèque de rendu, et chacun d'eux est traité comme des sous-modules de mes projets. Je trouve que SourceTree est utile en ce qui concerne les sous-modules, car il les gère bien et ne vous laissera rien oublier, par exemple si vous avez mis à jour le sous-module du moteur dans le projet A, il vous avertira de retirer les modifications dans le projet B.
Avec l'expérience vient la connaissance de ce que le code devrait être dans le moteur par rapport à ce qui devrait être par projet. Je suggère que si vous êtes même légèrement incertain, vous devez le conserver dans chaque projet pour l'instant. Au fil du temps, vous verrez parmi vos différents projets ce qui reste le même, puis vous pourrez progressivement en tenir compte dans votre code moteur. En d'autres termes: dupliquez du code jusqu'à ce que vous soyez presque sûr à 100% qu'il ne change pas discrètement par projet, puis généralisez-le.
Remarque sur le contrôle de code source et les fichiers binaires
N'oubliez pas que si vous vous attendez à ce que vos ressources binaires changent souvent, vous ne voudrez peut-être pas les mettre en contrôle de code source comme git. Je dis juste ... qu'il y a de meilleures solutions pour les binaires. La chose la plus simple que vous puissiez faire pour l'instant pour aider à garder votre référentiel "moteur-source" propre et performant est d'avoir un référentiel "moteur-binaires" séparé qui ne contient que des binaires, que vous incluez également comme sous-module dans votre projet. De cette façon, vous atténuez les dommages causés aux performances de votre référentiel "moteur-source", qui change tout le temps et sur lesquels vous avez donc besoin d'itérations rapides: commit, push, pull, etc. Les systèmes de gestion du contrôle de source comme git fonctionnent sur des deltas de texte , et dès que vous introduisez des binaires, vous introduisez des deltas massifs du point de vue du texte - ce qui vous coûte finalement du temps de développement.Annexe GitLab . Google est votre ami.
la source
À un moment donné, un moteur DOIT se spécialiser et connaître des choses sur le jeu. Je vais partir sur une tangente ici.
Prenez des ressources dans un RTS. Un jeu peut avoir
Credits
etCrystal
un autreMetal
etPotatoes
Vous devez utiliser correctement les concepts OO et opter pour max. réutilisation de code. Il est clair qu'un concept
Resource
existe ici.Nous décidons donc que les ressources sont les suivantes:
int
)Notez que cette notion d'un
Resource
pourrait représenter des éliminations ou des points dans un jeu! Ce n'est pas très puissant.Réfléchissons maintenant à un jeu. Nous pouvons en quelque sorte avoir de la monnaie en traitant des sous et en ajoutant un point décimal à la sortie. Ce que nous ne pouvons pas faire, ce sont des ressources "instantanées". Comme dire "génération de réseau électrique"
Disons que vous ajoutez une
InstantResource
classe avec des méthodes similaires. Vous êtes maintenant (en train de) polluer votre moteur avec des ressources.Le problème
Reprenons l'exemple RTS. Supposons que le joueur en fasse don
Crystal
à un autre joueur. Vous voulez faire quelque chose comme:Cependant, c'est vraiment assez compliqué. C'est un usage général, mais désordonné. Déjà, bien qu'il impose un
resourceDictionary
qui signifie que maintenant vos ressources doivent avoir des noms! ET c'est par joueur, donc vous ne pouvez plus avoir de ressources d'équipe.C'est "trop" d'abstraction (ce n'est pas un exemple brillant, je l'admets). Au lieu de cela, vous devriez atteindre un point où vous acceptez que votre jeu a des joueurs et du cristal, alors vous pouvez simplement avoir (par exemple)
Avec une classe
Player
et une classeCurrentPlayer
où lCurrentPlayer
'crystal
objet montrera automatiquement les trucs sur le HUD pour le transfert / envoi de dons.Cela pollue le moteur avec du cristal, le don de cristal, les messages sur le HUD pour les joueurs actuels et tout ça. Il est à la fois plus rapide et plus facile à lire / écrire / maintenir (ce qui est plus important, car il n'est pas beaucoup plus rapide)
Remarques finales
Le dossier de ressources n'est pas brillant. J'espère que vous pouvez toujours voir le point. Si quoi que ce soit, j'ai démontré que "les ressources n'appartiennent pas au moteur", car ce dont un jeu spécifique a besoin et ce qui est applicable à toutes les notions de ressources sont des choses TRÈS différentes. Ce que vous trouverez généralement, ce sont 3 (ou 4) "couches"
creature
ouship
ousquad
. En utilisant l' héritage que vous obtiendrez des cours qui couvrent les 3 couches (par exempleCrystal
est unResource
qui est unGameLoopEventListener
exemple)Créer un nouveau jeu à partir d'un ancien moteur
C'est TRÈS courant. La phase 1 consiste à extraire les couches 3 et 4 (et 2 si le jeu est d'un type TOTALEMENT différent) Supposons que nous fabriquons un RTS à partir d'un ancien RTS. Nous avons encore des ressources, juste pas de cristal et d'autres choses - donc les classes de base dans les couches 2 et 1 ont toujours du sens, tout ce cristal référencé en 3 et 4 peut être jeté. C'est ce que nous faisons. Nous pouvons cependant le vérifier comme référence pour ce que nous voulons faire.
Pollution dans la couche 1
Cela peut arriver. L'abstraction et la performance sont des ennemis. UE4, par exemple, fournit de nombreux cas de composition optimisés (donc si vous voulez X et Y, quelqu'un a écrit du code qui fait X et Y ensemble très rapidement - il sait qu'il fait les deux) et, par conséquent, est VRAIMENT assez grand. Ce n'est pas mauvais mais cela prend du temps. La couche 1 décidera des choses comme "comment vous transmettez les données aux shaders" et comment vous animez les choses. Le faire de la meilleure façon pour votre projet est TOUJOURS bon. Essayez simplement de planifier pour l'avenir, la réutilisation du code est votre ami, héritez là où cela a du sens.
Classification des couches
ENFIN (je le promets), n'ayez pas trop peur des couches. Moteur est un terme archaïque de l'ancien temps des pipelines à fonction fixe où les moteurs fonctionnaient à peu près de la même manière graphiquement (et, par conséquent, avaient beaucoup en commun), le pipeline programmable a renversé la situation et en tant que telle, la "couche 1" est devenue polluée. avec tous les effets que les développeurs voulaient atteindre. L'IA était la caractéristique distinctive (en raison de la myriade d'approches) des moteurs, maintenant c'est l'IA et les graphiques.
Votre code ne doit pas être déposé dans ces couches. Même le célèbre moteur Unreal a BEAUCOUP de versions différentes chacune spécifique à un jeu différent. Il y a peu de fichiers (autres que des structures de données similaires peut-être) qui seraient restés inchangés. C'est bon! Si vous voulez créer un nouveau jeu à partir d'un autre, cela prendra plus de 30 minutes. La clé est de planifier, de savoir quels bits copier et coller et quoi laisser derrière.
la source
Ma suggestion personnelle sur la façon de gérer le contenu qui est un mélange de générique et spécifique est de le rendre dynamique. Je vais prendre votre écran de menu comme exemple. Si j'ai mal compris ce que vous demandiez, faites-moi savoir ce que vous vouliez savoir et j'adapterai ma réponse.
Il y a 3 choses qui sont (presque) toujours présentes sur une scène de menu: l'arrière-plan, le logo du jeu et le menu lui-même. Ces choses sont généralement différentes selon le jeu. Ce que vous pouvez faire pour ce contenu est de créer un MenuScreenGenerator dans votre moteur, qui prend 3 paramètres d'objet: BackGround, Logo et Menu. La structure de base de ces 3 parties fait également partie de votre moteur, mais votre moteur ne dit pas réellement comment ces pièces sont générées, mais quels paramètres vous devez leur donner.
Ensuite, dans votre code de jeu réel, vous créez des objets pour un BackGround, un logo et un menu, et vous le transmettez à votre MenuScreenGenerator. Encore une fois, votre jeu lui-même ne gère pas la façon dont le menu est généré, c'est pour le moteur. Votre jeu n'a qu'à dire au moteur à quoi il devrait ressembler et où il devrait être.
Essentiellement, votre moteur doit être une API que le jeu indique quoi afficher. Si cela est fait correctement, votre moteur devrait faire le gros du travail et votre jeu lui-même devrait seulement dire au moteur quels actifs utiliser, quelles actions prendre et à quoi ressemble le monde.
la source