Comment Lua fonctionne-t-il comme langage de script dans les jeux?

67

Je suis un peu confus sur ce qu'est exactement Lua et comment un jeu programmé en C ++ l'utilisera. Je demande principalement comment il est compilé et exécuté.

Par exemple, lorsque vous utilisez un programme écrit en C ++ qui utilise des scripts Lua: le code en Lua appelle-t-il simplement des fonctions du programme principal écrit en C ++ et agit-il comme une classe non compilée en attente de compilation et ajoutée au segment de mémoire du C ++? programme?

Ou agit-il comme un script bash sous Linux où il exécute des programmes complètement distincts du programme principal?

XSoloDolo
la source

Réponses:

90

Le script est une abstraction de programmation dans laquelle vous avez (conceptuellement) un programme (le script) exécuté dans un autre programme (l'hôte). Dans la plupart des cas, la langue dans laquelle vous écrivez le script est différente de celle dans laquelle l'hôte est écrit, mais toute abstraction de programme dans un programme peut être considérée comme un script.

Sur le plan conceptuel, les étapes habituelles pour activer les scripts sont les suivantes (j'utiliserai pseudo-c pour l'hôte et pseudo-lua pour le script. Il ne s'agit pas d'étapes exactes, mais plutôt du flux général dans lequel vous activez les scripts).

  1. Créez une machine virtuelle dans le programme hôte:

    VM m_vm = createVM();
  2. Créez une fonction et exposez-la à la machine virtuelle:

    void scriptPrintMessage(VM vm)
    {
        const char* message = getParameter(vm, 0); // first parameter
        printf(message);
    }
    
    //...
    
    createSymbol(m_vm, "print", scriptPrintMessage);
    

    Notez que le nom dans lequel nous avons exposé la fonction ( print) ne doit pas nécessairement correspondre au nom interne de la fonction elle-même ( scriptPrintMessage)

  3. Exécutez un code de script qui utilise la fonction:

    const char* scriptCode = "print(\"Hello world!\")"; // Could also be loaded from a file though
    doText(m_vm, scriptCode);
    

C'est tout ce qu'on peut en dire. Le programme se déroule ensuite de la manière suivante:

  1. Vous appelez doText(). Le contrôle est ensuite transféré à la machine virtuelle, qui exécutera le texte à l'intérieur scriptCode.

  2. Le code de script trouve un symbole précédemment exporté print. Il transférera ensuite le contrôle à la fonction scriptPrintMessage().

  3. Une fois scriptPrintMessage()terminé, le contrôle sera transféré vers la machine virtuelle.

  4. Lorsque tout le texte de scriptCodea été exécuté, doText()se terminera et le contrôle sera transféré à votre programme sur la ligne suivante doText().

Donc, en général, tout ce que vous faites est d’exécuter un programme dans un autre programme. Théoriquement, vous ne pouvez rien faire avec les scripts sans eux, mais cette abstraction vous permet de faire des choses intéressantes très facilement. Certains d'entre eux sont:

  • Séparation des problèmes: il est courant d’écrire un moteur de jeu en C / C ++, puis le jeu dans un langage de script tel que Lua. Si vous le faites bien, le code du jeu peut être développé complètement indépendamment du moteur lui-même.

  • Flexibilité: les langages de script sont couramment interprétés et, en tant que tels, une modification d'un script ne nécessite pas nécessairement une reconstruction du projet entier. Bien fait, vous pouvez même changer un script et voir les résultats sans même un redémarrage du programme!

  • Stabilité et sécurité: étant donné que le script est exécuté sur une machine virtuelle, un script qui ne fonctionne pas correctement ne fera pas planter le programme hôte. Ceci est particulièrement important lorsque vous autorisez vos utilisateurs à écrire leurs propres scripts dans votre jeu. N'oubliez pas que vous pouvez créer autant de machines virtuelles indépendantes que vous le souhaitez! (J'ai déjà créé un serveur MMO dans lequel chaque correspondance était exécutée sur une machine virtuelle lua distincte)

  • Fonctionnalités linguistiques: lorsque vous utilisez des langages de script, en fonction de votre choix pour les langages hôte et de script, vous pouvez utiliser les meilleures fonctionnalités offertes par chaque langage. En particulier, les coroutines de lua sont une fonctionnalité très intéressante et très difficile à implémenter en C ou C ++.

Les scripts ne sont pas parfaits cependant. L'utilisation de scripts présente certains inconvénients courants:

  • Le débogage devient très difficile: en général, les débogueurs inclus dans les IDE communs ne sont pas conçus pour déboguer le code dans les scripts. De ce fait, le débogage de trace de la console est beaucoup plus courant que je ne le souhaiterais.

    Certains langages de script tels que lua disposent de fonctionnalités de débogage dont certains IDE comme Eclipse peuvent tirer parti. Faire cela est très difficile, et honnêtement, je n'ai jamais vu le débogage de script fonctionner aussi bien que le débogage natif.

    En passant, l'extrême manque d'intérêt des développeurs d'Unity à cet égard est la principale critique que j'ai faite à leur moteur de jeu et la principale raison pour laquelle je ne l'utilise plus, mais je m'éloigne du sujet.

  • Intégration de l'EDI: il est peu probable que votre IDE sache quelles fonctions sont exportées de votre programme. Il est donc peu probable que des fonctionnalités telles qu'IntelliSense, etc., fonctionnent avec vos scripts.

  • Performances: Il s'agit généralement de programmes interprétés et destinés à une machine virtuelle abstraite dont l'architecture peut être différente du matériel réel. Par conséquent, l'exécution des scripts est généralement plus lente que celle du code natif. Certaines machines virtuelles telles que luaJIT et V8 font un très bon travail. Cela peut être perceptible si vous utilisez beaucoup de scripts.

    En règle générale, les modifications de contexte (hôte à script et script à hôte) sont très coûteuses. Vous pouvez donc les minimiser si vous rencontrez des problèmes de performances.

Comment vous utilisez vos scripts est à vous. J'ai vu des scripts utilisés pour des tâches aussi simples que le chargement des paramètres, aussi complexes que la création de jeux entiers dans le langage de script avec un moteur de jeu très fin. Une fois, j'ai même vu un moteur de jeu très exotique qui mélangeait les scripts lua et JavaScript (via V8).

L'écriture de script n'est qu'un outil. La façon dont vous l'utilisez pour créer des jeux géniaux dépend entièrement de vous.

Pyjama panda
la source
Je suis vraiment nouveau dans ce domaine, mais j'utilise Unity. Vous avez mentionné quelque chose à propos d'Unity, pourriez-vous élaborer? Devrais-je utiliser autre chose?
Tokamocha
1
Je ne voudrais pas utiliser printfdans votre exemple (ou au moins utiliser printf("%s", message);)
Ratchet Freak
1
@ratchetfreak: Le point-virgule à la fin de votre message suivi de la parenthèse me fait un clin d'œil ...
Panda Pyjama
1
Une des meilleures réponses que j'ai vues sur ce site depuis longtemps; il mérite tous les votes positifs qu'il reçoit. Très bien fait.
Steven Stadnicki
1
World of Warcraft est l’affiche de Lua dans les jeux. La plupart de l’UI est écrite en Lua et peut être remplacée par le joueur. Je suis surpris que vous n'en ayez pas parlé.
Michael Hampton
7

En règle générale, vous liez ou exposez certaines fonctions natives à Lua (vous utilisez souvent une bibliothèque d’utilitaires , bien que vous puissiez le faire à la main). Cela permet au code Lua d’appeler votre code C ++ natif lorsque votre jeu exécute ce code Lua. En ce sens, votre hypothèse selon laquelle le code Lua appelle uniquement le code natif est vraie (bien que Lua dispose de sa propre bibliothèque de fonctionnalités standard disponible, vous n'avez pas besoin d'appeler votre code natif pour tout).

Le code Lua lui-même est interprété par le moteur d'exécution Lua, qui est un code C que vous liez comme une bibliothèque (généralement) dans votre propre programme. Vous pouvez en savoir plus sur le fonctionnement de Lua sur la page d'accueil de Lua . En particulier, Lua n'est pas "une classe non compilée" comme vous le supposez, en particulier si vous songez à une classe C ++, car le C ++ n'est presque jamais compilé de manière dynamique dans la pratique. Cependant, le runtime Lua et les objets créés par les scripts Lua exécutés par votre jeu consomment de l'espace dans la réserve de mémoire système de votre jeu.

Josh
la source
5

Les langages de script comme Lua peuvent être utilisés de plusieurs manières. Comme vous l'avez dit, vous pouvez utiliser Lua pour appeler des fonctions dans le programme principal, mais vous pouvez également appeler des fonctions Lua du côté C ++ si vous le souhaitez. Généralement, vous construisez une interface pour permettre une certaine flexibilité avec le langage de script de votre choix, de sorte que vous puissiez utiliser le langage de script dans une série de scénarios. Le grand avantage des langages de script tels que Lua est qu’ils sont interprétés au lieu de les compiler, ce qui vous permet de modifier les scripts Lua à la volée, de sorte que vous n’ayez pas à attendre que votre jeu soit compilé pour que vous puissiez le recompiler. avez fait ne convient pas à vos goûts.

Les commandes Lua ne sont appelées que lorsque le programme C ++ veut les exécuter. En tant que tel, le code ne sera interprété que lors de son appel. Je suppose que vous pouvez considérer Lua comme un script bash s'exécutant séparément du programme principal.

Généralement, vous souhaitez utiliser les langages de script pour les éléments que vous souhaitez mettre à jour ou effectuer une itération ultérieure. J'ai vu de nombreuses entreprises l'utiliser comme interface graphique, ce qui permet une grande personnalisation de l'interface. Si vous savez comment ils ont créé leur interface Lua, vous pouvez également modifier l'interface graphique vous-même. Mais vous pouvez emprunter de nombreuses autres voies pour utiliser Lua, telles que la logique de l'intelligence artificielle, des informations sur les armes, le dialogue de personnages, etc.

M Davies
la source
3

La plupart des langages de script, y compris Lua, fonctionnent sur une machine virtuelle ( VM ), qui est essentiellement un système permettant de mapper une instruction de script sur une "vraie" instruction de processeur ou un appel de fonction. La VM Lua s'exécute normalement dans le même processus que l'application principale. Ceci est particulièrement vrai pour les jeux qui l'utilisent. L'API Lua vous fournit plusieurs fonctions que vous appelez dans l'application native pour charger et compiler des fichiers de script. Par exemple, luaL_dofile()compile le script donné en bytecode Lua puis l’exécute. Ce bytecode sera ensuite mappé par la machine virtuelle s'exécutant à l'intérieur de l'API dans des instructions d'ordinateur natives et des appels de fonction.

Le processus de connexion d'un langage natif, tel que C ++, à un langage de script s'appelle une liaison . Dans le cas de Lua, son API fournit des fonctions qui vous aident à exposer des fonctions natives au code de script. Ainsi, vous pouvez par exemple définir une fonction C ++ say_hello()et la rendre appelable à partir d’un script Lua. L'API Lua fournit également des méthodes de création de variables et de tables via du code C ++ qui seront visibles pour les scripts lors de leur exécution. En combinant ces fonctionnalités, vous pouvez exposer des classes entières de C ++ à Lua. Le contraire est également possible, l'API Lua permet à l'utilisateur de modifier les variables Lua et d'appeler des fonctions Lua à partir du code C ++ natif.

La plupart des langages de script, sinon tous, fournissent des API pour faciliter la liaison du code de script avec du code natif. La plupart sont également compilés en bytecode et exécutés dans une machine virtuelle, mais certains peuvent être interprétés ligne par ligne.

J'espère que cela vous aidera à clarifier certaines de vos questions.

Glampert
la source
3

Étant donné que personne n'en a parlé, je vais l'ajouter ici aux personnes intéressées. Il existe tout un livre sur le sujet intitulé Game Scripting Mastery . C'est un texte fantastique qui a été écrit il y a longtemps, mais qui reste tout à fait pertinent aujourd'hui.

Ce livre ne vous montrera pas seulement comment les langages de script s'intègrent au code natif, il vous apprend également comment implémenter votre propre langage de script. Bien que ce soit excessif pour 99% des utilisateurs, il n’existe pas de meilleur moyen de comprendre quelque chose que de l’appliquer réellement (même sous une forme très basique).

Si vous souhaitez un jour écrire un moteur de jeu (ou si vous travaillez uniquement avec un moteur de rendu), ce texte est précieux pour comprendre comment un langage de script peut être intégré au mieux à votre moteur / jeu.

Et si vous voulez jamais créer votre propre langage de script, c'est l'un des meilleurs endroits pour commencer (à ma connaissance).

free3dom
la source
2

Premièrement, les langages de script ne sont généralement pas compilés . C'est une grande partie de ce qui les définit généralement comme des langages de script. Au lieu de cela, ils sont souvent "interprétés". Ce que cela signifie essentiellement qu'il existe une autre langue (qui est compilé, le plus souvent) qui est en train de lire dans le texte, en temps réel, et effectuer des opérations, ligne par ligne.

La différence entre cela et les autres langages réside dans le fait que les langages de script ont tendance à être plus simples (communément appelés "niveau supérieur"). Cependant, ils ont aussi tendance à être un peu plus lents, car les compilateurs ont tendance à optimiser bon nombre des problèmes liés à "l'élément humain" du codage, et le binaire résultant a tendance à être plus petit et plus rapide à lire, pour la machine. En outre, il existe moins de temps système qu'un autre programme devant être exécuté pour lire le code en cours d'exécution, avec les programmes compilés.

Maintenant, vous pensez peut-être, "Eh bien, je comprends que c'est un peu plus facile, mais pourquoi diable abandonnerait-il toute cette performance pour un peu de facilité d'utilisation?"

Vous ne seriez pas seul dans cette hypothèse; toutefois, le niveau de facilité que vous avez tendance à obtenir avec les langages de script, en fonction de ce que vous en faites, peut valoir le sacrifice sacrifié en termes de performances.

Fondamentalement: dans les cas où la vitesse de développement est plus importante que la vitesse d'exécution du programme, utilisez un langage de script. Il y a beaucoup de situations de ce genre dans le développement de jeux. Surtout lorsqu'il s'agit de choses triviales telles que la gestion d'événements de haut niveau.

Edit: La raison pour laquelle lua a tendance à être plutôt populaire dans le développement de jeux est parce qu’il est sans doute l’un des langages de script les plus rapides (si ce n’est le plus rapide) disponibles au monde sur la Terre. Cependant, avec cette vitesse supplémentaire, il a sacrifié une partie de sa commodité. Cela dit, il est sans doute plus pratique que de travailler directement en C ou C ++.

Important: après de plus amples recherches, j'ai constaté qu'il y avait beaucoup plus de controverse sur la définition d'un langage de script (voir Dichotomie de Ousterhout ). La critique principale de la définition d'un langage en tant que "langage de script" est qu'il n'est pas significatif pour la syntaxe ni pour la sémantique du langage qu'il est interprété ou compilé.

Alors que les langages généralement considérés comme des "langages de script" sont généralement interprétés plutôt que compilés, leur définition en tant que "langages de script" dépend en réalité de la façon dont les gens les voient et comment leurs créateurs les ont définis.

D'une manière générale, une langue peut facilement être considérée comme un langage de script (si vous êtes d'accord avec la dichotomie de Ousterhout) si elle remplit les critères suivants (selon l'article lié ci-dessus):

  • Ils sont dactylographiés dynamiquement
  • Ils ont peu ou pas de dispositions pour les structures de données complexes
  • Les programmes en eux (scripts) sont interprétés

De plus, il est souvent admis qu'un langage est un langage de script s'il est conçu pour interagir et fonctionner aux côtés d'un autre langage de programmation (généralement un langage qui n'est pas considéré comme un langage de script).

Gurgadurgen
la source
1
Je ne suis pas d'accord avec vous sur la première ligne où vous avez écrit "les langages de script ne sont pas compilés". Lua est en fait compilé en code intermédiaire intermédiaire avant son exécution par un compilateur JIT. Certains sont bien sûr interprétés ligne par ligne, mais pas tous.
Glampert
Je ne suis pas d'accord avec votre définition du "langage de script". Il existe des langages à double usage (tels que Lua ou C #) qui sont relativement couramment utilisés sous leur forme compilée, plutôt que sous la forme de script (ou inversement, comme c'est le cas avec C #). Lorsqu'un langage est utilisé comme langage de script, il est strictement défini comme un langage interprété et non compilé.
Gurgadurgen
Assez juste, votre définition est plus en ligne avec Wikipedia: "Un langage de script ou un langage de script est un langage de programmation qui prend en charge les scripts, les programmes écrits pour un environnement d'exécution spécial qui peut interpréter (plutôt que compiler) ...", ça ne fait rien. mon commentaire alors.
Glampert
1
Même si Lua est complètement compilé en bytecode, le compilateur JIT peut même produire du binaire natif. Donc non, Lua n'est pas interprétée.
Oleg V. Volkov
La première partie est douteuse, je suis tenté de voter. Il a en quelque sorte raison en ce sens qu'il est finalement interprété et que la compilation @ OlegV.Volkov JIT ne produit pas quelque chose de compilé. La compilation est définie par le temps de compilation, ce que Lua a, mais pas le code d'octet Lua (JIT ou pas JIT). Ne confondons pas notre mandat.
Alec Teal