À 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
et Crystal
un autre Metal
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:
- Un crochet dans la boucle principale pour s'incrémenter / décrémenter
- Un moyen d'obtenir le montant actuel (retourne un
int
)
- Une façon de soustraire / ajouter arbitrairement (joueurs transférant des ressources, achats ....)
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:
if(transfer.target == engine.getPlayerId()) {
engine.hud.addIncoming("You got "+transfer.quantity+" of "+
engine.resourceDictionary.getNameOf(transfer.resourceId)+
" from "+engine.getPlayer(transfer.source).name);
}
engine.getPlayer(transfer.target).getResourceById(transfer.resourceId).add(transfer.quantity)
engine.getPlayer(transfer.source).getResourceById(transfer.resourceId).add(-transfer.quantity)
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)
engine.getPlayer(transfer.target).crystal().receiveDonation(transfer)
engine.getPlayer(transfer.source).crystal().sendDonation(transfer)
Avec une classe Player
et une classe CurrentPlayer
où l CurrentPlayer
' 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"
- Le "Core" - c'est la définition classique du moteur, c'est un graphique de scène avec des hooks d'événements, il traite des shaders et des paquets réseau et une notion abstraite de joueurs
- Le "GameCore" - C'est assez générique pour le type de jeu mais pas pour tous les jeux - par exemple les ressources en RTS ou les munitions en FPS. La logique du jeu commence à s'infiltrer ici. C'est là que serait notre notion antérieure de ressources. Nous avons ajouté ces éléments qui ont du sens pour la plupart des ressources RTS.
- "GameLogic" TRÈS spécifique au jeu en cours de réalisation. Vous trouverez des variables avec des noms comme
creature
ou ship
ou squad
. En utilisant l' héritage que vous obtiendrez des cours qui couvrent les 3 couches (par exemple Crystal
est un Resource
qui est un GameLoopEventListener
exemple)
- Les "atouts" sont inutiles à tout autre jeu. Prenez par exemple les scripts AI combinés dans Half Life 2, ils ne seront pas utilisés dans un RTS avec le même moteur.
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.