Les temps d’itération rapides sont la clé du développement de jeux, bien plus que des graphismes sophistiqués et des moteurs dotés de nombreuses fonctionnalités à mon avis. Pas étonnant que beaucoup de petits développeurs choisissent des langages de script.
La manière d'Unity 3D de pouvoir mettre en pause un jeu et de modifier des ressources ET du code, puis de continuer et de faire en sorte que les modifications prennent effet immédiatement est absolument géniale pour cela. Ma question est la suivante: quelqu'un a-t-il mis en œuvre un système similaire sur les moteurs de jeu C ++?
J'ai entendu dire que certains moteurs très haut de gamme le font, mais je suis plus intéressé par savoir s'il existe un moyen de le faire dans un moteur ou un jeu développé localement.
De toute évidence, il y aurait des compromis, et je ne peux pas imaginer que vous puissiez simplement recompiler votre code pendant que le jeu est en pause, le recharger et le faire fonctionner dans toutes les circonstances ou sur toutes les plateformes.
Mais peut-être est-il possible pour la programmation AI et les modules logiques à niveau simple. Ce n'est pas comme si je voulais faire ça dans n'importe quel projet à court (ou long terme), mais j'étais curieux.
la source
Le remplacement à chaud est un problème très, très difficile à résoudre avec un code binaire. Même si votre code est placé dans des bibliothèques de liens dynamiques distinctes, votre code doit garantir qu'il n'y a pas de références directes à des fonctions en mémoire (car l'adresse des fonctions peut changer avec une recompilation). Cela signifie essentiellement que vous n'utilisez pas de fonctions virtuelles, et tout ce qui utilise des pointeurs de fonction le fait via une sorte de tableau de répartition.
Des trucs poilus et contraignants. Il est préférable d'utiliser un langage de script pour le code qui nécessite une itération rapide.
Au travail, notre base de code actuelle est un mélange de C ++ et de Lua. Lua n'est pas non plus une petite partie de notre projet, c'est presque 50/50. Nous avons mis en place un rechargement à la volée de Lua, qui vous permet de modifier une ligne de code, de recharger et de continuer. En fait, vous pouvez le faire afin de corriger les bugs qui se produisent dans le code Lua, sans avoir à redémarrer le jeu!
la source
(Vous voudrez peut-être connaître le terme "correction de singe" ou "frappe de canard" si ce n'est que pour l'image mentale humoristique.)
Cela dit: si votre objectif est de réduire le temps d’itération pour les changements de «comportement», essayez des approches qui vous mèneront presque jusqu'au bout, et combinez-les pour en permettre davantage à l’avenir.
(Cela sera un peu tangent, mais je vous promets que ça reviendra!)
Bon nombre de ces points sont utiles même si vous ne parvenez pas à recharger des données ou du code.
Anecdotes à l'appui:
Sur un grand PC RTS (environ 120 personnes, principalement C ++), il existait un système extrêmement puissant de sauvegarde d'état, utilisé à au moins trois fins:
Depuis, j'ai utilisé l'enregistrement / la lecture déterministe sur un jeu de cartes C ++ et Lua pour la DS. Nous nous sommes connectés à l'API que nous avons conçue pour l'IA (côté C ++) et avons enregistré toutes les actions de l'utilisateur et de l'IA. Nous avons utilisé cette fonctionnalité dans le jeu (pour rediffuser le joueur), mais aussi pour diagnostiquer les problèmes: en cas de crash ou de comportement étrange, il suffisait de récupérer le fichier de sauvegarde et de le lire dans une version de débogage.
Depuis lors, j'ai également utilisé plus de quelques fois les incrustations, et nous les avons combinées avec notre système de "parcourir automatiquement ce répertoire et de télécharger du nouveau contenu sur le terminal". Tout ce que nous aurions à faire, c’est de quitter la cinématique / niveau / peu importe et de revenir, et non seulement les nouvelles données (images-objets, agencement de niveaux, etc.) seraient chargées, mais également tout nouveau code inséré dans la superposition. Malheureusement, cela devient beaucoup plus difficile avec les ordinateurs de poche les plus récents en raison de la protection contre la copie et des mécanismes anti-piratage qui traitent spécialement le code. Nous le faisons toujours pour les scripts Lua cependant.
Dernier point mais non le moindre: vous pouvez (et j'ai, dans de très petites circonstances spécifiques) faire un peu de frappe de canard en corrigeant directement les codes d'opération d'instructions. Cela fonctionne mieux si vous êtes sur une plate-forme fixe et un compilateur, et parce que c'est presque impossible à maintenir, très sujet aux bogues et limité dans ce que vous pouvez accomplir rapidement, je ne l'utilise surtout que pour réacheminer le code pendant le débogage. Il ne vous enseigner un enfer beaucoup sur votre architecture de jeu d'instructions pressé, cependant.
la source
Vous pouvez faire quelque chose comme des modules remplaçants à chaud au moment de l'exécution, les implémentant en tant que bibliothèques de liens dynamiques (ou bibliothèques partagées sous UNIX), et en utilisant dlopen () et dlsym () pour charger des fonctions de manière dynamique à partir de la bibliothèque.
Pour Windows, les équivalents sont LoadLibrary et GetProcAddress.
Ceci est une méthode C, et présente quelques pièges en l’utilisant en C ++, vous pouvez en savoir plus ici .
la source
Si vous utilisez Visual Studio C ++, vous pouvez en fait mettre en pause et recompiler votre code dans certaines circonstances. Visual Studio prend en charge Modifier et continuer . Attachez-vous à votre jeu dans le débogueur, arrêtez-le à un point d'arrêt, puis modifiez le code après votre point d'arrêt. Si vous devez enregistrer puis continuer, Visual Studio tentera de recompiler le code, de le réinsérer dans l'exécutable en cours d'exécution et de continuer. Si tout se passe bien, les modifications apportées s'appliqueront au jeu en cours d'exécution sans qu'il soit nécessaire d'effectuer un cycle complet de compilation-test-construction. Cependant, ceci empêchera ceci de fonctionner:
J'ai utilisé Edit and Continue pour améliorer considérablement la vitesse d’itérations telles que l’itération d’UI. Supposons que votre interface utilisateur fonctionne entièrement en code, sauf que vous avez accidentellement inversé l'ordre de tirage de deux boîtes afin que vous ne puissiez rien voir. En modifiant 2 lignes de code en direct, vous pouvez économiser un cycle de compilation / compilation / test de 20 minutes pour vérifier un correctif d'interface utilisateur trivial.
Ce n'est PAS une solution complète pour un environnement de production, et j'ai trouvé la meilleure solution pour transférer autant de votre logique que possible dans des fichiers de données, puis pour rendre ces fichiers rechargeables.
la source
Comme d'autres l'ont dit, c'est un problème difficile, car lier dynamiquement le C ++. Mais c'est un problème résolu - vous avez peut-être entendu parler de COM ou de l'un des noms marketing qui lui ont été appliqués au fil des ans: ActiveX.
COM est un peu un mauvais nom du point de vue du développeur car il peut être très difficile d'implémenter des composants C ++ exposant leurs fonctionnalités (bien que cela soit simplifié avec ATL - la bibliothèque de modèles ActiveX). Du point de vue du consommateur, il a mauvaise réputation car les applications qui l'utilisent, par exemple pour incorporer une feuille de calcul Excel dans un document Word ou un diagramme Visio dans une feuille de calcul Excel, avaient tendance à se bloquer un peu plus tôt dans la journée. Et cela revient aux mêmes problèmes - même avec toutes les indications fournies par Microsoft, COM / ActiveX / OLE était / est difficile à obtenir correctement.
Je soulignerai que la technologie de COM n'est pas intrinsèquement mauvaise. Tout d'abord, DirectX utilise des interfaces COM pour exposer ses fonctionnalités, ce qui fonctionne assez bien, de même qu'une multitude d'applications intégrant Internet Explorer à l'aide de son contrôle ActiveX. Deuxièmement, c’est l’un des moyens les plus simples de lier dynamiquement du code C ++: une interface COM est essentiellement une simple classe virtuelle. Bien qu'il ait un IDL comme CORBA, vous n'êtes pas obligé de l'utiliser, surtout si les interfaces que vous définissez ne sont utilisées que dans votre projet.
Si vous n'écrivez pas pour Windows, ne pensez pas que COM ne mérite pas d'être envisagé. Mozilla l'a ré-implémenté dans sa base de code (utilisée dans le navigateur Firefox) car ils avaient besoin d'un moyen de composant leur code C ++.
la source
Il existe une implémentation de C ++ compilé à l'exécution pour le code de jeu ici . De plus, je sais qu’au moins un moteur de jeu propriétaire fait de même. C'est délicat, mais faisable.
la source