Catégories
LEGO Microgame Unity

Les variables • LEGO Microgame

Parmi les nouveautés que l’on trouve dans le template, il y a un compteur qui va modifier une variable et un déclencheur qui se base sur la valeur d’une variable. Voyons ce qu’il est possible d’en faire…

Le compteur doit être actionné par une autre brique, et sera configuré pour une certaine opération tel que l’addition, la soustraction, etc… Le déclencheur lui se voit configuré une condition, par exemple « si le score égale 3 ».

Si vous n’êtes pas familier avec la programmation informatique, une variable est un symbole que associe un nom à une valeur. Généralement la valeur est amenée à changer au cours du temps. Par exemple: on peut avoir la vie du joueur (nom) qui est à 42 (valeur). Si il mange un donut, on ajoutera +8 à la vie du joueur qui vaudra maintenant 50.

Ces deux briques ouvrent beaucoup de possibilités, à noter que l’on peut désormais ajouter des conditions sur tous les déclencheurs. On peut donc combiner des interactions et l’état d’une variable. Cependant si l’on change de scène la variable est réinitialisée, or j’aurais besoin de valeurs persistées entres les différences salles de mon jeu.

Comment ça marche ?

Ouvrons les scripts pour comprendre le fonctionnement qui a été choisi pour ces briques de comportement. On trouve 4 Scripts relatifs à au système:

  • CounterAction.cs : LEGOBehaviour d’action, c’est lui qui déclare la variable au lancement de la scène, puis demande à la modifier lorsqu’il est activé.
  • CounterTrigger.cs : LEGOBehaviour de déclenchement, il lit à chaque rafraichissement la valeur de la variable qu’il suit et test sa ou ses conditions. Je note aussi qu’il ne détient aucune logique spécifique, mais se base entièrement sur les conditions propres aux déclencheurs.
  • Variable.cs : ScriptableObject servant à la configuration de la variable. Avec un nom pour l’identifier, une valeur initiale, mais aussi l’interface utilisateur à utiliser pour afficher la variable en jeu si voulu.
  • VariableManager.cs : La classe statique qui garde en mémoire les valeurs des variables, utilisé par les briques de comportement pour lire et modifier les variables.

En l’état mes variables ne devraient s’initialiser qu’à l’apparition d’une brique d’action comportant une nouvelle variable, lors du chargement d’une scène. Le script statique garde les valeurs durant toute l’éxécution du jeu. La raison pour laquelle mes valeurs sont perdues entre les scènes est en fait extérieur. C’est le manager de jeu qui commande la réinitialisation au lancement d’une scène :

public class GameFlowManager : MonoBehaviour
{
  void Start()
  {
    // ...
    VariableManager.Reset();
  }
}

On va donc pouvoir obtenir des variables partagées entre les scènes sans trop de difficultés. Il me suffit de commencer mon script de gestion de partie personnalisé.

Let’s code !

Première mésaventure, je ne peux pas étendre la classe GameFlowManager fournie. Trop d’éléments sont déclarés privé et m’empêche de reprendre le code existant correctement. Je part donc sur une copie brute de la classe. De toute façon j’ai d’autres modifications en tête, elle était vouée à changer…

Je n’exclue pas la possibilité de réinitialiser les variables au lancement d’une scène, j’ajoute simplement un champ me permettant d’activer ou non cette fonction :

public class MyGameFlowManager : MonoBehaviour
{
  [SerializeField, Tooltip("Does the variables must be reset when the scene is loaded ?")]
  bool m_resetVariablesOnStart = true;
  protected void Start()
  {
    // ...
    if (m_resetVariablesOnStart)
      VariableManager.Reset();
  }
  // ...
}

Je modifie le prefab Game Manager avec mon script. J’oublie une première fois de décocher ma variable, classique ! Et finalement je peux voir que cela fonctionne, à un détail prêt, l’affichage des variables ne se fait plus après un changement de scène.

On y était presque ! Malheureusement le code fourni se montre toujours compliqué à étendre. Le manager de variable statique est appelé directement par les briques, et je n’ai pas envie de modifier un script susceptible d’être mis à jour par Unity. Or l’interface attend de ce manager l’indication qu’une variable a été ajouté pour afficher cette dernière.

Je suis obligé de ruser pour arranger ce comportement. Le manager de variable ne propose pas de méthode donnant les variables enregistrées. J’ajoute donc un tableau de variable à surveiller dans mon manager de jeu, et quelques ruses :

public class MyGameFlowManager : MonoBehaviour
{
  [SerializeField, Tooltip("Does the variables must be reset when the scene is loaded ?")]
  bool m_resetVariablesOnStart = true;
  [SerializeField, Tooltip("The variables to watch when the scene load.")]
  Variable[] m_VariablesToWatchAtStart = new Variable[0];
  protected void Start()
  {
    // ...
    HandleVariableManagement();
  }
  protected void HandleVariableManagement()
  {
    if (m_resetVariablesOnStart) {
      VariableManager.Reset();
      return;
    }
    foreach (Variable variable in m_VariablesToWatchAtStart) {
      if (!VariableManager.IsVariableRegistered(variable)) {
        continue;
      }
      VariableAdded evt = Events.VariableAddedEvent;
      evt.Variable = variable;
      EventManager.Broadcast(evt);
      // Force value update, as HUD init with initialValue
      VariableManager.SetValue(variable, VariableManager.GetValue(variable));
    }
  }
}

Ça y est ! Les valeurs de mes variables sont gardées d’une scène à l’autres comme escompté. Je regrette tout de même que les scripts ne soient pas plus ouverts.

Je réarrange mon code pour simplifier l’utilisation. Je considère désormais une liste de variable à conserver entre les scènes, au lieu du couple garder les variable + variables à vérifier :

public class MyGameFlowManager : MonoBehaviour
{
  [SerializeField, Tooltip("The variables to keep when the scene load.")]
  Variable[] m_VariablesToKeepBetweenScenes = new Variable[0];
  protected void HandleVariableManagement()
  {
    // Keep value before reset
    Dictionary<Variable, int> currentValues = m_VariablesToKeepBetweenScenes
      .Where(VariableManager.IsVariableRegistered)
      .ToDictionary(_v => _v, VariableManager.GetValue);
    // Reset all variables
    VariableManager.Reset();
    // Set the variable we want to keep between scenes
    foreach (KeyValuePair<Variable,int> value in currentValues) {
      VariableManager.SetValue(value.Key, value.Value);
    }
  }
  // ...
}

Où est mon record ?

Pour aller plus loin sur les variables je souhaite ajouter un comportement qui me permette de sauvegarder des scores sur plusieurs sessions. Pour ça je vais utiliser la class PlayerPref de Unity, qui se charge de gérer la méthode de sauvegarde selon la plateforme. Je pars du script existant que je duplique faute d’attributs accessibles, auquel j’ajoute la sauvegarde persistante. Et voilà, prêt à battre des records !

Vous pouvez retrouver les modification cités dans cette article sur mon repository GitLab.

À suivre

Les comportements manquants étant résolus, nous allons pouvoir nous atteler au level design. À très vite pour de nouveaux défis !