Monday, September 10, 2012

Scripting

Introduction


Aujourd'hui nous allons parler de scripting.
Ce que j'entends par scripting, c'est toutes les fonctionnalités liées aux déclenchements événements ponctuels influant sur le déroulement du jeu.

C'est la première fois que Maxence et moi (les programmeurs) sommes confrontés à la mise en place de la gestion des scripts dans un jeu. Nous en connaissons cependant les enjeux et la théorie mais nous sommes des amateurs en la matière.
La solution que nous employons n'est donc sans doute pas la meilleure mais nous avons quand même décidé de la créer de nos mains.

Implémenter la gestion des scripts est primordiale pour le jeu. Cela nous servira en premier lieu à mettre en scène des événements scriptés qui nous permettront d'étoffer l'histoire.
Cela nous permettra aussi de gérer la progression du joueur, avec tout un système d'interrupteurs conditionnels.


Notre langage de scripting : le C#


Pour commencer, nous avons dû faire le choix d'un langage de scripting. La plupart du temps, un langage auxiliaire simple à utiliser est prescrit. Cela permet aux personnes travaillant sur le contenu du jeu et n'ayant pas d'implication réelle dans la programmation du moteur de facilement créer des événements.

Dans notre cas, nous avons choisi de garder le C#, qui est notre langage moteur, comme langage de scripting. Il y a trois raisons à cela :

1- En tant que game designer et scénariste, je serais le seul à utiliser les scripts. Étant donné que je travaille aussi sur le moteur du jeu, je peux parfaitement coder les scripts en C#.

2- L'un des intérêts de la mise en place d'un langage de scripting annexe est la facilité de modification. En effet, en ayant les scripts dans des fichiers annexes, vous pouvez plus facilement procéder à de petites modifications rapidement, sans recompiler le jeu à chaque fois.
Cependant, étant donné que nous souhaitons développer le jeu pour Xbox, nous sommes contraints de le déployer sur le projet complet sur la console à chaque fois que nous souhaitons le tester. Nous ne gagnons donc pas de temps durant les phases de test.

3- La dernière raison est que le C# est un langage adapté au scripting. Il fournit un ensemble d'outils qui vont nous faciliter la tâche comme nous le verrons par la suite.

Intégration dans le moteur


Nous avons profité de l'utilisation du C# pour mettre en place une architecture orientée objet pour la gestion des scripts.

Pour commencer, voilà un petit schéma que nous allons expliquer au fur et à mesure :


ScriptManager

Gère tous les scripts du niveau, que se soit par le chargement de nouveaux scripts ou le rafraîchissement de ceux en place.
Cela nous offre la possibilité de faire fonctionner plusieurs scripts en parallèle.

Script

Contient une liste de ScriptLine qu'il utilise une par une.
Au chargement du script, les différentes commandes sont chargées de la façon suivante :

lines.Add(new ScriptLine(Cmd.fonction, new List<object> {...}));

Par exemple, si je souhaite déplacer le joueur jusqu'au point de coordonnées (2000, 650), je procède de la façon suivante :

lines.Add(new ScriptLine(Cmd.destinationGameObject, new List<object> { game.Joueur, new Point(2000, 650) }));

Durant le rafraîchissement du script et à partir de son initialisation, celui-ci va traiter une par une ces lignes. Lorsque qu'il a terminé une instruction, il passe à la suivante.
Une fois tout le script défilé, le script se désactive et peut être rappelé au besoin.

ScriptLine

Structure contenant la référence vers une méthode statique de Commande et une liste d'object sur lequel agir. Pour garder en mémoire la référence vers une fonction, nous utilisons un delegate.
En voici la déclaration :

public delegate bool Commande(List<object> arg);

Commande

Structure statique contenant des fonctions prédéfinies permettant d'agir sur le jeu.
Elle possède une référence vers le temps de jeu afin de gérer les événements temporels.

Toutes les méthodes sont de la forme :

static public bool nomFonction(List<object> arg)
{
     Travail sur les arguments envoyé à la fonction
     ...

     return condition de réussite;
}

La liste d'objects est nécessaire à l'utilisation d'un delegate mais nous contraint à caster systématiquement les arguments utilisés. (caster = indiquer la véritable nature d'un objet au programme)

Tant que la commande n'a pas atteint son objectif, elle renverra faux et sera rappelée par le script au prochain rafraîchissement.

SwitchManager

Gère tous les interrupteurs conditionnels du niveau. S'occupe surtout de leur chargement/libération.

Switch

Structure lié à certains scripts, décidant de leur activation selon une ou plusieurs conditions qui lui sont liées.

Conditionel

Structure vérifiant l'état d'une variable de jeu et regardant si elle remplit une condition d'activation.
Cela peut être la position du joueur par exemple.

Simplification de l'édition des scripts


Afin de simplifier la création des scripts, nous allons passer par un pseudo-code.
Notre éditeur de niveau contiendra un éditeur de texte qui nous permettra de créer les scripts liés à celui-ci.

Au lieu d'écrire de façon brute les lignes au format :

lines.Add(new ScriptLine(Cmd.fonction, new List<object> {arg1, arg2, arg3}));

... les lignes de script seront simplifiées dans l'éditeur de la façon suivante :

fonction arg1 arg2 arg3

L'éditeur créera à partir de ça la page de code C# faisant office de script. De la même façon, il interprétera les fichiers C# que nous chargerons.

Conclusion


Nous avons mis en place un système de scripting pour le moment fonctionnel. Il possède une forte orientation objet qui se lie plutôt bien au moteur du jeu.
Cependant, il n'est pas exempt de défauts et ne respecte pas quelques notions liées au scripting.

À l'heure actuelle, seules la gestion des scripts et quelques commandes de base ont été implémentées. La suite nous dira si nous avons fait les bons choix.

No comments:

Post a Comment