Quelques astuces incontournables pour Unity 3D

Cet article contient une compilation de petites astuces glanées au fur et à mesure de mon utilisation d’Unity 3D, et qui se sont montrées très utiles au fil du temps. À utiliser sans modération !

Afficher ou cacher des champs d’un composant (classe C#) dans l’inspecteur Unity

L’Inspecteur d’Unity est une manière pratique de rendre le paramétrage accessible dans l’éditeur, à la volée. Ça permet de tester plusieurs valeurs à la volée, de faire du paramétrage fin, de faciliter le débuggage…

Par défaut, le comportement est le suivant : une variable publique est exposée dans l’Inspecteur, et une variable privée n’est pas visible. Cela passe par la Serialization (transformation des données dans un format directement exploitable par l’Editeur Unity et donc son inspecteur).

N.B. : les propriétés et les champs statiques ne peuvent jamais être Serialized, donc ils ne peuvent pas apparaître dans l’inspecteur.

Le code suivant :

public class ClubController : MonoBehaviour
{
    public string PublicMessage = "Hello world";
    private int secretValue = 42;
}

Exposera les variables ainsi :

Il existe deux attributs qui permettent de affiner ce comportement :

  • [HideInInspector] permet de cacher un champ public (il reste quand même Serialized par Unity)
  • [SerializeField] permet de serializer un champ private, ce qui l’expose automatiquement dans l’inspecteur. Ça fait plus que cela,

Le code suivant illustre l’utilisation de ces attributs, ainsi que les types de champ qui ne peuvent pas apparaître dans l’inspecteur.

// Un champ public apparaît dans l'inspecteur, sauf avec [HideInInspector]
public string PublicMessage = "Hello world";
[HideInInspector] public string NotSoPublicMessage = "The truth is out there";

// Un champ privé n'apparaît pas dans l'inspecteur, 
// sauf s'il est Serialized manuellement avec [SerializeField]
private int secretValue = 42;
[SerializeField] private int luckyNumber = 777;

// Un champ statique n'apparaît jamais dans l'inspecteur.
public static float EarthGravity = 9.8f;

// Une propriété n'apparaît jamais dans l'inspecteur.
public int GetSecretValue => secretValue;

Au final, ne s’affichent que les champs public mais non cachés (PublicMessage) et les champs private et serialized (LuckyNumber).

Tous les types de champ ne sont pas serializable (par exemple, les Dictionnaires ne le sont pas), mais beaucoup de types de base du C# et d’Unity peuvent l’être, ainsi que les classes héritant de UnityEngine.Object et les listes. Voir la documentation de SerializeField pour plus d’informations.

N.B. : si vous avez juste besoin de voir les champs privé de manière temporaire, pour faire des tests ou du debugging, vous pouvez mettre l’inspecteur en mode Debug :

Passer l’inspecteur en mode Debug

Tous les champs seront affichés, sauf ceux qui ne sont pas Serializable et ceux qui ont l’attribut « HideInInspector ».

Exemple de composant en mode Debug

Personnalisation rapide d’une classe dans l’Inspecteur : en-têtes de section, valeurs limites…

Unity permet de scripter de manière très approfondie l’éditeur, c’est presque une discipline en soi et ce n’est pas l’objet de cet article. Cependant, il existe des attributs qui permettent de personnaliser rapidement l’inspecteur d’une classe que vous créez, avec de simples attributs dans le code.

Ça permet d’obtenir un composant plus simple et pratique à paramétrer, avec par exemple des directives d’utilisation pour ceux qui font le level design sans être au niveau du code.

  • Header("Titre du header") vous permet de créer une en-tête de section avant le champ suivant (typiquement pour séparer différent type de variables).
  • Range(minimum, maximum) applique des limites haut et basse à un champ numérique dans l’inspecteur. Le champ apparaît sous forme d’un curseur à déplacer sur un segment correspondant aux valeurs possibles.
  • Tooltip("Astuce pour le champ") permet l’affichage d’une infobulle lorsque l’utilisateur passe son curseur sur le champ en question. Très utile pour ajouter des précisions sur le fonctionnement ou l’utilité d’une variable.
  • Space(hauteur) ajoute un espace de la hauteur définie dans l’inspecteur (un entier indiquant le nombre de pixels).
  • TextArea présente un champ string sous la forme d’un champ texte de plusieurs ligne. À utiliser pour les descriptions ou les textes de dialogue par exemple.

Les attributs se mettent entre crochets, avant le champ concerné. Il est possible d’ajouter plusieurs attributs à la suite, avec une virgule.

Voici un exemple d’utilisation de ces attributs :

[Header("Parameters")]
[Range(0f, 2.5f), Tooltip("Maximum speed of the ship, verified with the rigidbody speed.")]
[SerializeField] private float maxShipSpeed = 2f;
private float shipSpeed = 0f;
[Tooltip("Any non alphabetic character in the name will be cleaned on runtime.")]
[SerializeField] private string shipName = "Admiral";
[Header("Passengers List")]
[Range(1, 5)]
[SerializeField] private int passengersCapacity = 2;
private int currentPassengers = 0;
[Space(30)] // Espace de 30 pixels ajouté entre passengersCapacity et les Passengers.
public List<string> Passengers;
[Header("Narrative text")]
[SerializeField, TextArea] private string introductionText = "Multi-line \nnarrative \nexposition text";

Voici comment les champs apparaissent dans l’inspecteur (ci-dessous on voit plusieurs Range(), plusieurs Header(), un Space et un TextArea) :

Exemple dans l’inspecteur des attributs Range, Header, Space et TextArea

L’attribut Tooltip se révèle lorsqu’on passe sur un champ :

Exemple d’attribut Tooltip()

Il existe aussi l’attribut [NonReorderable] qui permet d’empêcher la réorganisation d’une collection dans l’inspecteur. En effet, par défaut, toutes les collections (array, list…) peuvent être réorganisées par l’utilisateur. Cet attribut bloque le comportement de reorganisation.

public string[] Passengers = {
	"Aldrin", "Armstrong", "Shepard", "Conrad"
};

[NonReorderable]
public string[] Planets = {
	"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"
};

Voici comment ces tableaux sont présentés dans l’inspecteur, seul le premier peut être réorganisé.

Exemple de l’attribut [NonReorderable]

Accentuer la teinte du mode « Play » pour éviter les erreurs d’édition

Lorsque vous passez en mode « Play » dans Unity pour tester votre application, toutes les modifications que vous faites,dans l’inspecteur et dans la scène, sont perdues lorsque vous quittez ce mode Play.

Par défaut, Unity colorise l’inspecteur de manière très subtile (trop subtile ?) en y passant juste une teinte sombre. Résultat : on se retrouve parfois à modifier plusieurs données, puis on se rend compte qu’on était en mode Play, donc toutes ces modifications sont perdues. Et ça, c’est arrivé plus d’une fois à de nombreux utilisateurs d’Unity !

Une astuce pour éviter de saisir par erreur des données en mode Play, c’est de rendre ce mode beaucoup plus flagrant en utilisant une teinte de couleur.

Rendez-vous dans les préférences d’Unity (Edit > Preferences), section colors, et cherchez Playmode tint. Là, choisissez une couleur bien marquée, sans transparence. Choisissez plutôt une couleur pâle (une couleur avec trop de saturation peut rendre l’interface plus difficilement lisible, surtout si vous utilisez la version Dark de l’interface). Attention, si vous baissez la luminosité, vous risquez de ne plus rien voir du tout.

Changement de la couleur de l’inspecteur en mode Play (Playmode tint)

Avec cela, à chaque fois que vous passez en mode Play, c’est beaucoup plus marqué sur l’interface, suffisamment pour vous freiner si vous étiez tenté de changer des valeurs. Vous pouvez tout de même continuer à y lire les valeurs ou à modifier à la volée, mais là vous êtes sur de ne pas perdre des modifications faites en Play Mode par erreur.

Interface d’Unity avec une couleur d’accentuation pendant le mode Play

N.B. : pour choisir la teinte, n’hésitez pas à passer en mode Play et à directement changer « Playmode tint », vous verrez en temps réel comment la couleur rend !

Sauvegarder des données d’un composant saisies pendant le mode « Play »

S’il y a certaines valeurs que vous ne pouvez récupérer en mode Play, ou que malgré l’astuce ci-dessus vous avez saisi par erreur une valeur, et que vous souhaitez la récupérer et l’enregistrer dans le composant hors du mode Play, de manière persistante.

Une des possibilités, c’est de noter à côté d’Unity les valeurs que vous voulez. Mais il y a une manière plus directe de le faire : en mode Play, cliquez sur le menu du composant dont vous voulez garder les valeurs, et sélectionnez « Copy Component » :

Exemple de « Copy Component »

Une fois le mode « Play » quitté, retournez sur le composant en question, et choisissez « Paste Component Values« . Toutes les valeurs enregistrées en mode « Play » seront ainsi collées dans le composant.

Exemple de « Paste Composant Values »

Cette manière de faire était typiquement utilisée pour les composants de type « Transform » pour sauvegarder une position en mode Play et la sauvegarder hors du mode Play.

Un exemple typique, c’est pour placer dans la scène un élément accessible à une position précise, accessible au joueur. Par exemple, cacher le déclencher d’un succès ou un élément à collectionner à un endroit caché, ou accessible qu’en faisant une cascade spécifique. Dans ce cas, vous pourrez faire la cascade en conditions réelles en mode « Play », mettre le jeu en pause, enregistrer ces valeurs et les coller ensuite hors du mode Play.

Dans les versions les plus récentes d’Unity, il y a des éléments spécifique dans le menu pour sauvegarder un des Vector3 (Position, Rotation ou Scale), et les coller. Attention, vous ne pourrez avoir qu’un seul Vector3 en mémoire, qu’il s’agisse de n’importe lequel de ces 3 Vector3. L’option « Copy/Paste World Placement » permet de copier et coller l’ensemble des 3 Vector3 en une seule fois.

Option de copier/coller du composant propres au composant Transform

Générer à la volée la documentation dans son code C#, pour s’y retrouver

Cette astuce va au-delà d’Unity, mais peut vous apporter beaucoup. Elle consiste à documenter votre code dans un format compréhensible par l’IDE, ce qui fait que si vous utilisez une méthode que vous avez créé il y a un certain temps, ou s’il y a plusieurs développeurs dans l’équipe.

Il faut utiliser 3 symboles /// dans le code, juste avant la méthode, et mettre sa description dans des balises <summary> et </summary>. Concrètement, cela vous permet de générer un fichier de documentation de code au format XML, qui est ensuite affichée par l’IDE. Vous pouvez utiliser des balises <para></para> pour faire des paragraphe et <br/> pour des retours à la ligne.

/// <summary>
/// <para>Multiplies the current speed by the accelerationFactor, respecting the speed boundaries of the object.
/// <br/>If the object is not moving, the accelerationFactor will use a speed of 1f.</para>
/// </summary>
public void Accelerate(float accelerationFactor)
{
    // Code de la méthode Accelerate.
}

Ainsi, dans Visual Studio, si vous commencez à taper la méthode ou si vous passez le curseur sur cette méthode, la documentation sera directement présentée en infobulle, vous aidant à l’utiliser :

Exemple de documentation d’une fonction indiquée dans Visual Studio

Si vous saisissez les caractères /// sur la ligne précédant une méthode, Visual Studio ajoute par défaut code contenant <summary> pour décrire la méthode, ainsi que plusieurs balises <param> où vous pouvez donner une explication pour chacun des paramètres. Si vous remplissez ces balises <param>, l’info correspondante sera indiquée lorsque l’utilisateur remplira le paramètre correspondant dans la méthode. Ainsi, si on documente des paramètres comme suit :

/// <summary>
/// Changes the apparence of the vehicle.
/// </summary>
/// <param name="type">Type of vehicle (car, bike, boat...), amongst possible types</param>
/// <param name="vehicleModel">Integer identifiying the model of said vehicle. Will default to 0 if there is no model for this identifier</param>
/// <param name="mainColor">Sets the main color of the vehicle (roof, sides, doors...)</param>
/// <param name="secondaryColor">Sets the color for secondary elements of the vehicle, if available on the model.</param>
public void SetVehicleRender(VehicleType type, int vehicleModel, Color32 mainColor, Color32 secondaryColor)
{
	// Code de la méthode SetVehicleRender()
}

Lorsqu’on commence à saisir la méthode correspondante dans le code, la définition générale de <summary> sera tout le temps affiché, mais pour chaque paramètre, la valeur attendue sera décrire dans l’infobulle :

Exemple de présentation d’une documentation de <param> dans Visual Studio

Il existe de nombreuses balises pour créer la documentation de vos méthodes, notamment certaines qui permettent de préciser les exceptions de la méthode, indiquer un exemple d’utilisation, etc. Reportez-vous à la section sur la documentation XML pour voir les différentes possibilités.

Attention à ne pas confondre ces deux balises :

  • <para> sert à définir des paragraphes (équivalent de <p> en html), et sert uniquement à de la mise en forme.
  • <param name="myVar"> permet de décrire le paramètre myVar.

Résolution de problèmes occasionnels

Que faire si Visual Studio a perdu la connexion avec Unity ? (méthodes d’Unity soulignées en rouge…)

Il arrive parfois que Visual Studio perde la connexion avec Unity et indique que les appels standards à Unity sont inconnus. Il y a ainsi de nombreuses méthodes soulignées en rouge, et cela affiche de nombreux messages d’erreurs dans l’IDE.

Voici une manière très rapide de réparer la situation sans avoir à fermer et rouvrir le projet Unity :

  1. Aller dans « Project Settings », onglet « Player »
  2. Dans Api Compatibility Level, changer le réglage sur l’autre possibilité (par ex., .NET Standard 2.0) et attendez la fin de « Script compilation » en bas à droite.
  3. Sans retourner dans Visual Studio, retourner au réglage précédent (.NET 4.x) et attendez à nouveau la fin de Script compilation.
  4. Rouvrez votre fenêtre Visual Studio, l’anomalie sera résolue !
Changer le niveau de Compatibilité de l’API C# .NET d’Unity permet de résoudre un problème de connexion entre Visual Studio et le framework Unity.

Que faire si mon application Unity compilée demande une autorisation firewall au démarrage ?

Lorsque l’éditeur d’Unity est mis à jour, le pare-feu Windows vous demandera une exception. C’est tout à fait normal

Cependant, si vous avez ce type de pop-up à l’ouverture de votre build d’application, et que c’est une application hors ligne, c’est assez inquiétant, notamment pour l’utilisateur final. Ce n’est pas le genre de comportement que vous voulez pour un jeu commercialisé.

En fait, cette pop-up apparaît tout simplement si vous avez choisi de faire un build de développement. Les exceptions au firewall permettent au fonction de profiling à distance du player Unity de fonctionner.

Décocher « Development Build » évite à Windows

Pour votre version de l’application dédiée aux utilisateurs finaux, pensez à décocher « Development Build » dans la fenêtre « Build Settings », avant de lancer la création du build.

One thought on “Quelques astuces incontournables pour Unity 3D

Commentaires désactivés