J'ai du mal à implémenter les scripts dans mon moteur de jeu. Je n'ai que quelques exigences: cela devrait être intuitif, je ne veux pas écrire un langage personnalisé, un analyseur et un interprète, et je ne veux pas utiliser le filetage. (Je suis certain qu'il existe une solution plus simple; je n'ai pas besoin des tracas de plusieurs threads de logique de jeu.) Voici un exemple de script, en Python (alias pseudocode):
def dramatic_scene(actors):
alice = actors["alice"]
bob = actors["bob"]
alice.walk_to(bob)
if bob.can_see(alice):
bob.say("Hello again!")
else:
alice.say("Excuse me, Bob?")
Ce morceau épique de narration pose des problèmes de mise en œuvre. Je ne peux pas simplement évaluer toute la méthode à la fois, car cela walk_to
prend du temps de jeu. Si cela revient tout de suite, Alice commencerait à marcher vers Bob et (dans le même cadre) dire bonjour (ou être salué). Mais s'il walk_to
y a un appel de blocage qui revient quand elle atteint Bob, alors mon jeu est bloqué, car il bloque le même fil d'exécution qui ferait marcher Alice.
J'ai envisagé de faire de chaque fonction la mise en file d'attente d'une action - alice.walk_to(bob)
pousserait un objet dans une file d'attente, qui serait sauté après qu'Alice ait atteint Bob, où qu'il soit. C'est plus subtilement cassé: la if
branche est évaluée immédiatement, donc Bob pourrait saluer Alice même si son dos lui est tourné.
Comment les autres moteurs / personnes gèrent-ils les scripts sans créer de threads? Je commence à chercher des idées dans des domaines autres que les développeurs de jeux, comme les chaînes d'animation jQuery. Il semble qu'il devrait y avoir de bons modèles pour ce genre de problème.
Réponses:
La façon dont quelque chose comme Panda fait cela est avec les rappels. Au lieu de bloquer, ce serait quelque chose comme
Un rappel complet vous permet d'enchaîner ce type d'événements aussi profondément que vous le souhaitez.
EDIT: exemple JavaScript car qui a une meilleure syntaxe pour ce style:
la source
Le terme que vous souhaitez rechercher ici est " coroutines " (et généralement le mot-clé de la langue ou le nom de la fonction est
yield
).L'implémentation dépendra tout d'abord de votre langue. Pour un jeu, vous voulez que la mise en œuvre soit aussi légère que possible (plus légère que les fils ou même les fibres). La page Wikipedia (liée) contient des liens vers diverses implémentations spécifiques au langage.
J'ai entendu dire que Lua avait un support intégré pour les coroutines. GameMonkey aussi.
UnrealScript implémente cela avec ce qu'il appelle des "états" et des "fonctions latentes".
Si vous utilisez C #, vous pouvez consulter ce billet de blog de Nick Gravelyn.
De plus, l'idée de "chaînes d'animation", bien que n'étant pas la même chose, est une solution pratique au même problème. Nick Gravelyn a également une implémentation C # de cela .
la source
yield return walk_to();
dans votre script.ne pas fileter est intelligent.
La plupart des moteurs de jeu fonctionnent comme une série d'étapes modulaires avec des éléments en mémoire qui guident chaque étape. Pour votre `` marche vers l'exemple '', vous avez généralement une étape IA où vos personnages marchés sont dans un état où ils ne devraient pas chercher d'ennemis à tuer, une étape d'animation où ils devraient exécuter l'animation X, une étape physique (ou étape de simulation) où leur position réelle est mise à jour, etc.
dans votre exemple ci-dessus, 'alice' est un acteur composé de morceaux qui vivent dans plusieurs de ces étapes, donc un acteur bloquant.walk_to call (ou une coroutine que vous appelez next () une fois par image) n'aurait probablement pas le contexte approprié pour prendre beaucoup de décisions.
Au lieu de cela, une fonction 'start_walk_to' ferait probablement quelque chose comme:
Ensuite, votre boucle principale exécute son tick ai, son tick physique, son tick d'animation et son cutscene, et la cutscene met à jour l'état de chacun des sous-systèmes de votre moteur. Le système de cinématiques doit définitivement suivre ce que fait chacune de ses cinématiques, et un système basé sur la coroutine pour quelque chose de linéaire et déterministe comme un scéne pourrait avoir du sens.
La raison de cette modularité est qu'elle garde les choses simples et agréables, et pour certains systèmes (comme la physique et l'IA), vous devez connaître l'état de tout en même temps pour résoudre les choses correctement et garder le jeu dans un état cohérent .
J'espère que cela t'aides!
la source