L’article de blog suivant, sauf indication contraire, a été écrit par un membre de la communauté Gamasutras.
Les pensées et les opinions exprimées sont celles de l’écrivain et non de Gamasutra ou de sa société mère.
Partie 1 – Messagerie
Partie 2 – Mémoire
Partie 3 – Cache &de données
Partie 4 – Bibliothèques graphiques
Nous vivons une époque formidable pour être développeurs. Avec une telle quantité de grands moteurs AAA-grade disponibles pour tout le monde, faire des jeux simples peut être aussi facile que de glisser-déposer. Il semble qu’il n’y ait plus aucune raison d’écrire un moteur de nos jours. Et avec le sentiment commun, « Écrire des jeux, pas des moteurs », pourquoi devriez-vous ?
Cet article est principalement destiné aux développeurs solos et aux petites équipes. Je suppose une certaine familiarité avec la programmation orientée objet.
Je veux vous donner un aperçu de l’approche du développement d’un moteur et j’utiliserai un simple moteur fictif pour illustrer cela.
Pourquoi écrire un moteur ?
La réponse courte est : Ne le faites pas, si vous pouvez l’éviter.
La vie est trop courte pour écrire un moteur pour chaque jeu (Tiré du livre 3D Graphics Programming de Sergei Savchenko)
La sélection actuelle d’excellents moteurs tels que Unity, Unreal ou CryEngine sont aussi flexibles qu’on peut l’espérer et peuvent être utilisés pour faire à peu près n’importe quel jeu. Pour des tâches plus spécialisées, il existe bien sûr des solutions plus pointues comme Adventure Game Studio ou RPG Maker, pour n’en citer que quelques-unes. Même le coût des moteurs de qualité commerciale n’est plus un argument.
Il ne reste que quelques raisons de niche pour écrire votre propre moteur :
- Vous voulez apprendre comment fonctionne un moteur
- Vous avez besoin de certaines fonctionnalités qui ne sont pas disponibles ou les solutions disponibles sont instables
- Vous croyez que vous pouvez le faire mieux / plus rapidement
- Vous voulez garder le contrôle du développement
Toutes ces raisons sont parfaitement valables et si vous lisez ceci, vous appartenez probablement à l’un de ces camps. Mon objectif n’est pas d’entrer dans un long débat « Quel moteur dois-je utiliser ? » ou « Dois-je écrire un moteur ? » ici et je vais sauter directement dedans. Donc, commençons.
Comment échouer à écrire un moteur
Attendez. D’abord je vous dis de ne pas en écrire un, ensuite j’explique comment échouer ? Grande introduction…
En tout cas, il y a beaucoup de choses à concidérer avant même d’écrire une seule ligne de code. Le premier et le plus grand problème de tous ceux qui commencent à écrire un moteur de jeu peut se résumer à ceci :
Je veux voir du gameplay aussi vite que possible !
Cependant, plus vite vous réalisez que cela prendra beaucoup de temps jusqu’à ce que vous voyiez réellement quelque chose d’intéressant se produire, mieux vous serez en train d’écrire votre moteur.
Forcer votre code à montrer une forme de graphique ou de gameplay aussi vite que possible, juste pour avoir une confirmation visuelle de « progrès », est votre plus grand ennemi à ce stade. Prenez. votre. Temps!
Ne pensez même pas à commencer par les graphiques. Vous avez probablement lu beaucoup de tutoriels et de livres OpenGL / DirectX et vous savez comment rendre un simple triangle ou sprite. Vous pourriez penser qu’un court extrait de code de rendu d’un petit maillage à l’écran est un bon point de départ. Ce n’est pas le cas.
Oui, vos premiers progrès seront étonnants. Heck, vous pourriez courir autour d’un petit niveau en First-Person en seulement un jour copier-coller des extraits de code de divers tutoriels et Stack Overflow. Mais je vous garantis que vous effacerez chaque ligne de ce code deux jours plus tard. Pire encore, vous pourriez même être découragé d’écrire un moteur, car il n’est pas motivant de voir de moins en moins.
Le deuxième grand problème auquel les développeurs sont confrontés en écrivant des moteurs est le feature creep. Tout le monde aimerait écrire le saint graal des moteurs. Tout le monde veut ce moteur parfait qui peut tout faire. Les jeux de tir à la première personne, les jeux de rôle tactiques, tout ce que vous voulez. Mais le fait est que nous ne pouvons pas le faire. Pas encore. Il suffit de regarder les grands noms. Même Unity ne peut pas vraiment répondre parfaitement à tous les genres de jeux.
Ne pensez même pas à écrire un moteur qui peut faire plus d’un genre du premier coup. Ne le faites pas !
Où commencer réellement lors de l’écriture d’un moteur
Écrire un moteur est comme l’ingénierie d’un vrai moteur pour une voiture. Les étapes sont en fait assez évidentes, en supposant que vous savez sur quel jeu (ou voiture) vous travaillez. Les voici :
- Pointer exactement ce dont votre moteur doit être capable ET ce dont votre moteur n’a pas besoin d’être capable.
- Organiser les besoins en Systèmes dont votre moteur aura besoin.
- Concevoir votre Architecture parfaite qui lie tous ces Systèmes ensemble.
- Répétez les étapes 1. – 3. aussi souvent que possible.
- Codez.
Si (= si et seulement si) vous consacrez suffisamment de temps et d’efforts aux étapes 1. – 4. et que le Game Design ne change pas soudainement d’un jeu d’horreur à une machine à sous (lire : Silent Hill), le codage sera une entreprise très agréable. Le codage sera toujours loin d’être facile, mais parfaitement gérable, même par les développeurs solos.
C’est la raison pour laquelle cet article porte principalement sur les étapes 1. – 4. Pensez à l’étape 5. comme « Remplir les blancs. 50.000 LOC de blancs ».
La partie la plus cruciale de tout cela est l’étape 3. Nous allons concentrer la plupart de nos efforts ici!
Etape 1. Déterminer les besoins et les non-besoins
Toutes ces étapes peuvent sembler plutôt triviales au premier abord. Mais elles ne le sont pas vraiment. Vous pourriez penser que l’étape 1 du processus de développement d’un moteur de jeu de tir à la première personne peut se résumer à ceci :
J’ai besoin de charger un niveau, l’arme des joueurs, quelques ennemis avec une IA. C’est fait, on passe à l’étape 2.
Si seulement c’était aussi facile. La meilleure façon d’aborder l’étape 1 est de parcourir l’ensemble du jeu, clic par clic, action par action, depuis le clic sur l’icône sur votre bureau, jusqu’à l’appui sur la touche de sortie après avoir lancé les crédits. Faites une liste, une grande liste de ce dont vous avez besoin. Faites une Liste de ce dont vous n’avez absolument pas besoin.
Cela va probablement se passer comme ceci:
Je démarre le jeu et il va directement au Menu principal. Le menu utilisera-t-il une image statique ? Une scène coupée ? Comment puis-je contrôler le menu principal, souris ? Le clavier ? De quel type d’éléments d’interface graphique ai-je besoin pour le menu principal ? Boutons, formulaires, barres de défilement ? Et la musique ?
Et ce ne sont que des macro-concidérations. Allez-y de manière aussi détaillée que possible. Décider que vous avez besoin de Boutons est bien et bon, mais aussi concidérer ce qu’un bouton peut faire.
Je veux que les boutons aient 4 états, Up, Hover, Down, Disabled. Aurai-je besoin de son pour les boutons ? Et des effets spéciaux ? Sont-ils animés dans l’état de repos ?
Si votre liste de besoins et de non besoins ne contient qu’environ 10 éléments à la fin du menu principal, vous avez fait quelque chose de mal.
À ce stade, ce que vous faites est de simuler le moteur dans votre cerveau et d’écrire ce qui doit être fait. L’étape 1 deviendra plus claire à chaque itération, ne vous inquiétez pas de manquer quelque chose la première fois.
Etape 2. Organiser les besoins en systèmes
Donc, vous avez vos listes de choses dont vous avez besoin et dont vous n’avez pas besoin. Il est temps de les organiser. Évidemment, les choses liées à l’interface graphique comme les boutons iront dans une sorte de système d’interface graphique. Les éléments liés au rendu vont dans le système / moteur graphique.
Encore, comme pour l’étape 1, décider de ce qui va où sera plus évident lors de votre deuxième itération, après l’étape 3. Pour la première passe, regroupez-les logiquement comme dans l’exemple ci-dessus.
La meilleure référence sur « ce qui va où » et « ce qui fait quoi » est sans aucun doute le livre Game Engine Architecture de Jason Gregory.
Commencez à regrouper les fonctionnalités. Commencez à penser à des façons de les combiner. Vous n’avez pas besoin de Camera->rotateYaw(float yaw)
et Camera->rotatePitch(float pitch)
si vous pouvez les combiner en Camera->rotate(float yaw, float pitch)
. Restez simple. Trop de fonctionnalités (rappelez-vous, le feature creep) vous fera mal plus tard.
Pensez à la fonctionnalité qui doit être exposée publiquement et à celle qui doit seulement résider dans le système lui-même. Par exemple, votre Renderer a besoin de trier tous les Sprites transparents avant de les dessiner. La fonction permettant de trier ces sprites n’a cependant pas besoin d’être exposée. Vous savez que vous devez trier les Sprites transparents avant de les dessiner, vous n’avez pas besoin d’un Système externe pour vous le dire.
Etape 3. L’architecture (ou, l’article proprement dit)
Nous aurions aussi bien pu commencer l’article ici. C’est la partie intéressante et importante.
L’une des plus simples architectures possibles que votre moteur peut avoir est de mettre chaque système dans une classe et d’avoir la boucle principale du jeu qui appelle leurs sous-routines. Cela pourrait ressembler à quelque chose comme ceci :
while(isRunning)
{
Input->readInput();
isRunning = GameLogic->doLogic();
Camera->update();
World->update();
GUI->update();
AI->update();
Audio->play();
Render->draw();
}
Cela semble parfaitement raisonnable au début. Vous avez toutes vos bases couvertes, Entrée -> traitement de l’Entrée -> Sortie.
Et en effet, cela suffira pour un Jeu simple. Mais ce sera une douleur à maintenir. La raison pour cela devrait être évidente : les dépendances.
Chaque système doit communiquer avec d’autres systèmes d’une manière ou d’une autre. Nous n’avons aucun moyen de le faire dans notre boucle de jeu ci-dessus. Par conséquent, l’exemple indique clairement que chaque système doit avoir une certaine référence des autres systèmes afin de faire quelque chose de significatif. Notre interface graphique et notre logique de jeu doivent savoir quelque chose sur nos entrées. Notre Renderer doit savoir quelque chose sur notre Logique de Jeu afin d’afficher quoi que ce soit de significatif.
Cela conduira à cette merveille architecturale:
Si ça sent les spaghettis, ce sont des spaghettis. Définitivement pas ce que nous voulons. Oui, c’est facile et rapide à coder. Oui, nous aurons des résultats acceptables. Mais ce n’est pas maintenable. Changez un petit morceau de code quelque part et cela pourrait avoir des effets dévastateurs sur tous les autres systèmes sans que nous le sachions.
De plus, il y aura toujours du code auquel de nombreux Systèmes devront avoir accès. Le GUI et le Renderer doivent faire des appels de Draw ou au moins avoir accès à une sorte d’interface pour gérer cela pour nous. Oui, nous pourrions simplement donner à chaque Système le pouvoir d’appeler directement les fonctions OpenGL / DirectX, mais nous nous retrouverons avec beaucoup de redondances.
Nous pourrions résoudre ce problème en rassemblant toutes les fonctions de dessin à l’intérieur du Système de Rendu et appeler celles-ci depuis le système GUI. Mais alors le système de rendu aura des fonctions spécifiques pour l’interface graphique. Celles-ci n’ont pas leur place dans le Renderer et est donc contraire à l’étape 1 et 2. Décisions, Décisions.
Donc la première chose que nous devrions concidérer est de diviser notre moteur en couches.
Lasagne de moteur
Lasagne est meilleure que Spaghetti. Du moins en ce qui concerne la programmation. En restant sur notre exemple de Renderer, ce que nous voulons est d’appeler les fonctions OpenGL / DirectX sans les appeler directement dans le système. Cela ressemble à un Wrapper. Et pour l’essentiel, c’est le cas. Nous rassemblons toutes les fonctions de dessin dans une autre classe. Ces classes sont encore plus basiques que nos systèmes. Appelons ces nouvelles classes le Framework.
L’idée derrière cela est d’abstraire beaucoup d’appels d’API de bas niveau et de les former en quelque chose d’adapté à notre jeu. Nous ne voulons pas définir le tampon de sommets, définir le tampon d’index, définir les textures, activer ceci, désactiver cela juste pour faire un simple appel de dessin dans notre système de rendu. Mettons tous ces trucs de bas niveau dans notre Framework. Et j’appellerai cette partie du Framework « Draw ». Pourquoi ? Eh bien, tout ce qu’elle fait, c’est de tout préparer pour le dessin, puis de le dessiner. Il ne se soucie pas de ce qu’il dessine, où il le fait, pourquoi il le fait. Cela est laissé au Renderer System.
Cela peut sembler bizarre, nous voulons de la vitesse dans notre moteur, non ? Plus de couches d’abstraction = moins de vitesse.
Et vous auriez raison, si c’était les années 90. Mais nous avons besoin de la maintenabilité et pouvons vivre avec la perte de vitesse à peine perceptible pour la plupart des parties.
Comment alors notre Draw Framework devrait-il être conçu ? En termes simples, comme notre propre petite API. SFML est un excellent exemple de cela.
Des choses importantes à garder à l’esprit :
- Gardez-le bien documenté. Quelles sont les fonctions que nous avons ? Quand peuvent-elles être appelées ? Comment sont-elles appelées ?
- Gardez-le simple. Des fonctions faciles comme drawMesh(Mesh* oMesh) ou loadShader(String sPath) vous rendront heureux à long terme.
- Gardez-le fonctionnel. Ne soyez pas trop spécifique. au lieu de
drawButtonSprite
, ayez unedrawSprite
fonction et laissez l’appelant s’occuper du reste.
Que gagnons-nous ? Beaucoup:
- Nous n’avons besoin de configurer notre Framework qu’une seule fois et nous pouvons l’utiliser dans tous les systèmes dont nous avons besoin (GUI, Renderer….)
- Nous pouvons facilement changer les API sous-jacentes si nous choisissons, sans réécrire chaque système. Passer d’OpenGL à DirectX ? Pas de problème, il suffit de réécrire la classe du Framework.
- Il garde le code dans nos systèmes propres et serrés.
- Avoir une interface bien documentée signifie qu’une personne peut travailler sur le Framework, tandis qu’une personne travaille dans la couche système.
Nous finirons probablement avec quelque chose comme ceci:
Ma règle du pouce de ce qui va dans le Framework est assez simple. Si j’ai besoin d’appeler une bibliothèque externe (OpenGL, OpenAL, SFML…) ou d’avoir des structures de données / algorithmes dont chaque système a besoin, je devrais le faire dans le Framework.
Nous avons maintenant notre première couche de lasagne faite. Mais nous avons encore cette énorme boule de spaghetti au-dessus. Attaquons-nous à cela ensuite.
Messagerie
Le grand problème demeure cependant. Nos systèmes sont encore tous interconnectés. Nous ne voulons pas cela. Il existe une multitude de façons de traiter ce problème. Événements, messages, classes abstraites avec des pointeurs de fonction (quel ésotérisme)…
Restons-en aux messages. C’est un concept simple qui est encore très populaire dans la programmation des interfaces graphiques. Il est également bien adapté comme exemple facile pour notre moteur.
Il fonctionne comme un service postal. L’entreprise A envoie un message à l’entreprise B et demande que quelque chose soit fait. Ces entreprises n’ont pas besoin de connexion physique. L’entreprise A suppose simplement que l’entreprise B le fera à un moment donné. Mais, pour l’instant, l’entreprise A ne se soucie pas vraiment de savoir quand ou comment l’entreprise B le fera. Il suffit de le faire. Heck, l’entreprise B pourrait même décider de réacheminer le message à l’entreprise C et D et de les laisser s’en occuper.
Nous pouvons aller plus loin, l’entreprise A n’a même pas besoin de l’envoyer à quelqu’un de spécifique. L’entreprise A poste simplement la lettre et toute personne qui se sent responsable la traitera. De cette façon, les entreprises C et D peuvent directement traiter la demande.
De toute évidence, les entreprises égalent nos Systèmes. Prenons un exemple simple :
- La trame informe le système d’entrée que « A » a été pressé
- L’entrée traduit que la frappe « A » signifie « Ouvrir l’inventaire » et envoie un message contenant « Ouvrir l’inventaire »
- L’interface graphique traite le message et ouvre la fenêtre de l’inventaire
- La logique du jeu traite le message et met le jeu en pause
.
L’entrée ne se soucie même pas de ce qui est fait à son message. L’interface graphique ne se soucie pas que la logique du jeu traite également le même message. S’ils étaient tous couplés, Input devrait appeler une fonction dans le système GUI et une fonction dans Game Logic. Mais ce n’est plus nécessaire. Nous avons pu découpler cela avec succès en utilisant les Messages.
À quoi ressemble un Message ? Il devrait au moins avoir un certain type. Par exemple, l’ouverture de l’inventaire pourrait être un certain enum appelé OPEN_INVENTORY
. Cela suffit pour les messages simples comme celui-ci. Les messages plus avancés qui doivent inclure des données auront besoin d’un moyen de stocker ces données. Il existe une multitude de façons d’accomplir cela. La plus simple à mettre en œuvre est l’utilisation d’une structure de carte simple.
Mais comment envoyer des Messages ? Via un bus de messages, bien sûr !
Ce n’est pas beau ? Plus de spaghetti, juste de bonnes vieilles lasagnes. J’ai délibérément placé notre logique de jeu de l’autre côté du bus de messages. Comme vous pouvez le voir, elle n’a aucune connexion avec la couche Framework. C’est important pour éviter toute tentation de « n’appeler que cette seule fonction ». Croyez-moi, vous en aurez envie tôt ou tard, mais cela casserait notre conception. Nous avons suffisamment de systèmes qui traitent avec le Framework, pas besoin de le faire dans notre logique de jeu.
Le bus de messages est une simple classe avec des références à chaque système. S’il a un Message en file d’attente, le Bus à Messages le poste à chaque Système via un simple appel handleMessage(Msg msg)
. En retour, chaque système dispose d’une référence au bus de messages afin de poster des messages. Cela peut évidemment être stocké en interne ou passé en tant qu’argument de fonction.
Tous nos Systèmes doivent donc hériter ou être de la forme suivante :
class System
{
public:
void handleMessage(Msg *msg);
{
switch(msg->type)
{
//// Example
//case Msg::OPEN_INVENTORY:
// break;
}
}
private:
MessageBus *msgBus;
//// Usage: msgBus->postMessage(msg);
}
(Oui, Oui, des pointeurs bruts…)
Soudainement, notre Boucle de Jeu change pour simplement laisser le Bus de Messages envoyer autour des Messages. Nous aurons toujours besoin de mettre à jour périodiquement chaque système via une forme d’appel update()
. Mais la communication sera gérée différemment.
Cependant, comme avec nos Frameworks, l’utilisation des Messages crée des surcharges. Cela va ralentir un peu le moteur, ne nous voilons pas la face. Mais nous nous en moquons ! Nous voulons un Design propre et simple. Une architecture propre et simple !
Et la partie la plus cool ? Nous obtenons des choses étonnantes gratuitement !
La Console
Chaque Message est à peu près un appel de fonction. Et chaque message est envoyé à peu près partout ! Et si nous avions un Système qui imprime simplement chaque Message qui est envoyé dans une certaine fenêtre de sortie ? Et si ce Système pouvait aussi envoyer les Messages que nous tapons dans cette fenêtre ?
Oui, nous venons de donner naissance à une Console. Et tout ce qu’il nous a fallu, c’est quelques lignes de code. Mon esprit a été soufflé à l’époque où j’ai vu cela en action pour la première fois. Elle n’est même pas liée à quoi que ce soit, elle existe simplement.
Une console est évidemment très utile pendant le développement du jeu et nous pouvons simplement la retirer dans la Release, si nous ne voulons pas que le joueur ait ce genre d’accès.
Cinématiques en jeu, Replays & Débogage
Et si nous falsifions les messages ? Et si nous créions un nouveau système qui envoie simplement des messages à un certain moment ? Imaginez qu’il envoie quelque chose comme MOVE_CAMERA
, suivi de ROTATE_OBJECT
.
Et voilà, nous avons des cinématiques en jeu.
Et si nous enregistrions simplement les messages d’entrée qui ont été envoyés pendant le jeu et les sauvegardions dans un fichier ?
Et voilà, nous avons des Replays.
Et si nous enregistrions simplement tout ce que le joueur fait, et quand le jeu se plante, qu’il nous envoie ces fichiers de données ?
Et voilà, nous avons une copie exacte des actions des joueurs qui ont conduit au crash.
Multi-Threading
Multi-Threading ? Oui, Multi-Threading. Nous avons découplé tous nos systèmes. Cela signifie qu’ils peuvent traiter leurs messages quand ils veulent, comme ils veulent et surtout, où ils veulent. Nous pouvons demander à notre bus de messages de décider sur quel thread chaque système doit traiter un message -> Multi-Threading
Frame Rate Fixing
Nous avons trop de messages à traiter dans cette trame ? Pas de problème, gardons-les dans la file d’attente du bus de messages et envoyons-les à la trame suivante. Cela nous donnera l’opportunité de nous assurer que notre jeu fonctionne à 60 FPS. Les joueurs ne remarqueront pas que l’IA prend quelques images de plus pour « réfléchir ». Ils remarqueront cependant des baisses de Frame Rate.
Les messages sont cool.
Il est important que nous documentions méticuleusement chaque message et ses paramètres. Traitez-le comme une API. Si vous faites cela correctement, chaque développeur peut travailler sur différents Systèmes sans rien casser. Même si un système doit être hors ligne ou en construction, le jeu fonctionnera quand même et pourra être testé. Pas de système audio ? Ce n’est pas grave, nous avons toujours les visuels. Pas de Renderer, c’est bien, nous pouvons utiliser la Console…
Mais les Messages ne sont pas parfaits. Malheureusement.
Parfois, nous VOULONS connaître le résultat d’un message. Parfois, nous avons VRAIMENT besoin qu’ils soient traités immédiatement. Nous devons trouver des options viables. Une solution à cela est d’avoir un Speedway. En dehors d’une simple postMessage
fonction, nous pouvons implémenter une postImmediateMessage
fonction qui est traitée immédiatement. La gestion des messages de retour est beaucoup plus facile. Ceux-ci sont envoyés à notre handleMessage
fonction tôt ou tard. Nous devons juste nous en souvenir lorsque nous envoyons un message.
Les messages immédiats cassent évidemment le multithreading et le Frame Rate Fixing s’ils sont faits en excès. Il est donc vital de se restreindre pour limiter leur utilisation.
Mais le plus gros problème de ce système est la latence. Ce n’est pas l’architecture la plus rapide. Si vous travaillez sur un First Person Shooter avec des temps de réponse semblables à ceux de twitch, cela pourrait être un cas de rupture.
Retour à la conception de notre architecture
Nous avons décidé d’utiliser des systèmes et un bus de messages. Nous savons exactement comment nous voulons structurer notre moteur.
Il est temps de passer à l’étape 4 de notre processus de conception. Itération. Certaines fonctions pourraient ne pas s’insérer dans n’importe quel Système, nous devons trouver une solution. Certaines fonctions doivent être appelées de manière extensive et encombreraient le Bus de messages, nous devons trouver une solution.
Cela prend du temps. Mais cela en vaut la peine à long terme.
Il est enfin temps de coder !
Etape 4. Où commencer à coder ?
Avant de commencer à coder, lisez le livre/article Game Programming Patterns de Robert Nystrom.
A part cela, j’ai esquissé une petite feuille de route que vous pourriez suivre. Ce n’est de loin pas la meilleure façon, mais c’est productif.
- Si vous allez avec un type de moteur de bus de messages, concider de coder la console et le bus de messages en premier. Une fois que ceux-ci sont mis en œuvre, vous pouvez simuler l’existence de tout système qui n’a pas encore été codé. Vous aurez un contrôle constant sur l’ensemble du moteur à chaque étape du développement.
- Pensez à passer à l’interface graphique ensuite, ainsi qu’à la fonctionnalité Draw nécessaire à l’intérieur du Framework. Une solide interface graphique associée à la console vous permettra de truquer tous les autres systèmes encore plus facilement. Les tests seront un jeu d’enfant.
- La suite devrait être le Framework, au moins son interface. La fonctionnalité peut suivre plus tard.
- Enfin, passez aux autres systèmes, y compris le Gameplay.
Vous remarquerez, rendre réellement quelque chose lié au Gameplay pourrait être la dernière chose que vous faites. Et c’est une bonne chose ! Il se sentira tellement plus gratifiant et vous gardera motivé pour terminer les touches finales de votre moteur.
Votre concepteur de jeu pourrait vous tirer dessus pendant ce processus cependant. Tester le gameplay à travers les commandes de la console est à peu près aussi amusant que de jouer à Counter Strike via IRC.
Conclusion
Prenez votre temps pour trouver une architecture solide et tenez-vous en à elle ! C’est le conseil que j’espère que vous retiendrez de cet article. Si vous faites cela, vous serez capable de construire un Moteur parfaitement bien et maintenable à la fin de la journée. Ou le siècle.
Personnellement, j’aime plus écrire des moteurs que de faire tous ces trucs de Gameplay. Si vous avez des questions, n’hésitez pas à me contacter via Twitter @Spellwrath. Je suis actuellement en train de terminer un autre Moteur en utilisant les méthodes que j’ai décrites dans cet article.
Vous pouvez trouver la deuxième partie ici.