Partager via


Instructions de codage

Ce document décrit les recommandations de codage avec World Locking Tools pour Unity. La plupart de ces suggestions suivent les normes recommandées par MSDN.


En-têtes d’informations de licence de script

Un en-tête de licence standard doit être attaché à tous les scripts publiés dans World Locking Tools pour Unity, exactement comme indiqué ci-dessous :

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

Tous les fichiers de script soumis sans l’en-tête de licence seront rejetés.

En-têtes de résumé de fonction/méthode

Vous devez spécifier l’objectif et l’utilisation de la totalité des classes publiques, structs, enums, fonctions, propriétés et champs publiés, exactement comme indiqué ci-dessous :

    /// <summary>
    /// The Controller definition defines the Controller as defined by the SDK / Unity.
    /// </summary>
    public struct Controller
    {
        /// <summary>
        /// The ID assigned to the Controller
        /// </summary>
        public string ID;
    }

Cette règle garantit que la documentation est correctement générée et diffusée pour toutes les classes, méthodes et propriétés.

Tous les fichiers de script soumis sans les balises de résumé appropriées seront rejetés.

Règles d’espace de noms

Toutes les classes et extensions doivent être délimitées par un espace de noms, choisi correctement à partir des espaces de noms suivants.

Microsoft.MixedReality.WorldLocking.Core - Code de fondation remplissant le service de base de World Locking Tools.

Microsoft.MixedReality.WorldLocking.Tools - Fonctionnalités optionnelles complétant le développement de World Locking Tools. Exemples : visualisations de diagnostic et implémentations de référence des gestionnaires d’événements d’application.

Microsoft.MixedReality.WorldLocking.Examples - Implémentations spécifiques illustrant l’utilisation des fonctionnalités World Locking Tools et les bénéfices obtenus.

Les fonctionnalités associées dans l’un des espaces de noms ci-dessus peuvent être regroupées en les étendant à un nouveau sous-espace de noms.

À faire

namespace Microsoft.MixedReality.WorldLocking.Examples.Placement
{
    // Interface, class or data type definition.
}

L’omission de l’espace de noms pour une interface, une classe ou un type de données entraîne le blocage de votre modification.

Espaces et tabulations

Veillez à utiliser quatre espaces à la place d’onglets lors de la contribution à ce projet.

En outre, assurez-vous que des espaces sont ajoutés pour les fonctions conditionnelles/de boucle comme if/while/for

À ne pas faire

private Foo () // < - space between Foo and ()
{
    if(Bar==null) // <- no space between if and ()
    {
        DoThing();
    }
    
    while(true) // <- no space between while and ()
    {
        Do();
    }
}

À faire

private Foo()
{
   if (Bar==null)
   {
       DoThing();
   }
   
   while (true)
   {
       Do();
   }
}

Espacement

N’ajoutez pas d’espaces supplémentaires entre les crochets et les parenthèses :

À ne pas faire

private Foo()
{
    int[ ] var = new int [ 9 ];
    Vector2 vector = new Vector2 ( 0f, 10f );
}

À faire

private Foo()
{
    int[] var = new int[9];
    Vector2 vector = new Vector2(0f, 10f);
}

Conventions d'affectation de noms

Utilisez toujours PascalCase pour les propriétés publiques / protégées / virtuelles, et camelCase pour les propriétés et les champs privés.

La seule exception concerne les structures de données qui requièrent la sérialisation des champs par JsonUtility.

À ne pas faire

public string myProperty; // <- Starts with a lower case letter
private string MyProperty; // <- Starts with an uppercase case letter

À faire

public string MyProperty;
protected string MyProperty;
private string myProperty;

Modificateurs d’accès

Déclarez toujours un modificateur d’accès pour l’ensemble des champs, propriétés et méthodes.

Toutes les méthodes de l’API Unity doivent être private par défaut, sauf si vous devez les substituer dans une classe dérivée. Dans ce cas, vous devez utiliser protected.

Les champs doivent toujours être private, avec les accesseurs de propriété public ou protected.

À ne pas faire

// protected field should be private
protected int myVariable = 0;

// property should have protected setter
public int MyVariable { get { return myVariable; } }

// No public / private access modifiers
void Foo() { }
void Bar() { }

À faire

public int MyVariable { get; protected set; } = 0;

private void Foo() { }
public void Bar() { }
protected virtual void FooBar() { }

Utiliser des accolades

Utilisez toujours des accolades après chaque bloc d’instructions et placez-les à la ligne suivante.

À ne pas faire

private Foo()
{
    if (Bar==null) // <- missing braces surrounding if action
        DoThing();
    else
        DoTheOtherThing();
}

À ne pas faire

private Foo() { // <- Open bracket on same line
    if (Bar==null) DoThing(); <- if action on same line with no surrounding brackets 
    else DoTheOtherThing();
}

À faire

private Foo()
{
    if (Bar==true)
    {
        DoThing();
    }
    else
    {
        DoTheOtherThing();
    }
}

Les classes publiques, les structs et les enums doivent tous se trouver dans leurs propres fichiers.

Si la classe, la struct ou l’enum peut être rendue privée, il est possible de l’inclure dans le même fichier. Cette inclusion évite les problèmes de compilation avec Unity et garantit l’abstraction de code appropriée. Elle réduit également les conflits et les changements cassants lorsque le code doit être modifié.

À ne pas faire

public class MyClass
{
    public struct MyStruct() { }
    public enum MyEnumType() { }
    public class MyNestedClass() { }
}

À faire

// Private references for use inside the class only
public class MyClass
{
   private struct MyStruct() { }
   private enum MyEnumType() { }
   private class MyNestedClass() { }
}

À faire

MyStruct.cs

// Public Struct / Enum definitions for use in your class.  Try to make them generic for reuse.
public struct MyStruct
{
   public string Var1;
   public string Var2;
}

MyEnumType.cs

public enum MuEnumType
{
    Value1,
    Value2 // <- note, no "," on last value to denote end of list.
}

MyClass.cs

public class MyClass
{
    private MyStruct myStructreference;
    private MyEnumType myEnumReference;
}

Classer les enums pour l’extension appropriée.

Si une enum est susceptible d’être étendue à l’avenir, il est essentiel de classer les valeurs par défaut en haut de l’enum. Ce classement garantit que les index Enum ne sont pas affectés par de nouveaux ajouts.

À ne pas faire

public enum SDKType
{
    WindowsMR,
    OpenVR,
    OpenXR,
    None, <- default value not at start
    Other <- anonymous value left to end of enum
}

À faire

   /// <summary>
   /// The SDKType lists the VR SDK's that are supported by the MRTK
   /// Initially, this lists proposed SDK's, not all may be implemented at this time (please see ReleaseNotes for more details)
   /// </summary>
   public enum SDKType
   {
       /// <summary>
       /// No specified type or Standalone / non-VR type
       /// </summary>
       None = 0,
       /// <summary>
       /// Undefined SDK.
       /// </summary>
       Other,
       /// <summary>
       /// The Windows 10 Mixed reality SDK provided by the Universal Windows Platform (UWP), for Immersive MR headsets and HoloLens. 
       /// </summary>
       WindowsMR,
       /// <summary>
       /// The OpenVR platform provided by Unity (does not support the downloadable SteamVR SDK).
       /// </summary>
       OpenVR,
       /// <summary>
       /// The OpenXR platform. SDK to be determined once released.
       /// </summary>
       OpenXR
   }

Terminer les noms d’enum par « Type »

Les noms d’enum doivent indiquer clairement leur nature à l’aide du suffixe Type.

À ne pas faire

public enum Ordering
{
    First,
    Second,
    Third
}
public enum OrderingEnum
{
    First,
    Second,
    Third
}

À faire

public enum OrderingType
{
    First = 0,
    Second,
    Third
}

Examiner l’utilisation de l’enum pour les champs de bits

S’il est possible qu’une énumération exige plusieurs états comme valeur, par exemple, Handedness = Left &Right. Ensuite, l’enum doit être complétée par des indicateurs binaires pour permettre son utilisation correcte

Le fichier Handedness.cs propose une implémentation concrète pour ce cas

À ne pas faire

public enum Handedness
{
    None,
    Left,
    Right
}

À faire

[flags]
public enum HandednessType
{
   None = 0 << 0,
   Left = 1 << 0,
   Right = 1 << 1,
   Both = Left | Right
}

Meilleures pratiques, y compris les recommandations Unity

Certaines des plates-formes cibles de ce projet nécessitent de prendre en compte les performances. En tenant compte de ces éléments, soyez toujours prudent lorsque vous allouez de la mémoire dans du code fréquemment appelé dans des boucles ou des algorithmes de mise à jour serrés.

Encapsulation

Utilisez toujours des champs privés et des propriétés publiques si l’accès au champ est nécessaire à l’extérieur de la classe ou de la struct. Veillez à colocaliser le champ privé et la propriété publique. Cet emplacement permet de mieux voir, d’un coup d’œil, ce qui soutient la propriété et de confirmer que le champ est modifiable par le script.

Si vous devez avoir la possibilité de modifier votre champ dans l’inspecteur, il est recommandé de suivre les règles d’encapsulation et de sérialiser votre champ de stockage.

La seule exception concerne les structures de données qui requièrent la sérialisation des champs par JsonUtility, où une classe de données doit posséder tous les champs publics pour que la sérialisation fonctionne.

À ne pas faire

public float MyValue;

À faire

// private field, only accessible within script (field is not serialized in Unity)
private float myValue;

À faire

// Enable private field to be configurable only in editor (field is correctly serialized in Unity)
[SerializeField] 
private float myValue;

À ne pas faire

private float myValue1;
private float myValue2;

public float MyValue1
{
    get{ return myValue1; }
    set{ myValue1 = value }
}

public float MyValue2
{
    get{ return myValue2; }
    set{ myValue2 = value }
}

À faire

// Enable field to be configurable in the editor and available externally to other scripts (field is correctly serialized in Unity)
[SerializeField]
[ToolTip("If using a tooltip, the text should match the public property's summary documentation, if appropriate.")]
private float myValue; // <- Notice we co-located the backing field above our corresponding property.

/// <summary>
/// If using a tooltip, the text should match the public property's summary documentation, if appropriate.
/// </summary>
public float MyValue
{
    get{ return myValue; }
    set{ myValue = value }
}

Utiliser for à la place de foreach lorsque cela est possible

Dans certains cas, une instruction foreach est requise, par exemple, lors d’une boucle sur IEnumerable. Mais pour garantir des performances optimales, évitez si possible les instructions foreach.

À ne pas faire

foreach(var item in items)

À faire

int length = items.length; // cache reference to list/array length
for(int i=0; i < length; i++)

Mettez en cache les valeurs et sérialisez-les dans la scène ou le préfabriqué chaque fois que cela est possible.

En gardant HoloLens à l’esprit, il est préférable d’optimiser les performances et les références de cache dans la scène ou le préfabriqué afin de limiter les allocations de mémoire du runtime.

À ne pas faire

void Update()
{
    gameObject.GetComponent<Renderer>().Foo(Bar);
}

À faire

[SerializeField] // To enable setting the reference in the inspector.
private Renderer myRenderer;

private void Awake()
{
   // If you didn't set it in the inspector, then we cache it on awake.
   if (myRenderer == null)
   {
       myRenderer = gameObject.GetComponent<Renderer>();
   }
}

private void Update()
{
   myRenderer.Foo(Bar);
}

Mettez en cache les références aux matériaux, n’appelez pas chaque fois « .material ».

Unity crée un nouveau matériau chaque fois que vous utilisez « .material », ce qui entraîne une fuite de mémoire si elle n’est pas nettoyée correctement.

À ne pas faire

public class MyClass
{
    void Update() 
    {
        Material myMaterial = GetComponent<Renderer>().material;
        myMaterial.SetColor("_Color", Color.White);
    }
}

À faire

// Private references for use inside the class only
public class MyClass
{
   private Material cachedMaterial;

   private void Awake()
   {
       cachedMaterial = GetComponent<Renderer>().material;
   }

   void Update() 
   {
       cachedMaterial.SetColor("_Color", Color.White);
   }
   
   private void OnDestroy()
   {
       Destroy(cachedMaterial);
   }
}

Vous pouvez également utiliser la propriété « SharedMaterial » d’Unity, qui ne crée pas de nouveau matériau chaque fois qu’elle est référencée.

Utilisez la compilation dépendante de la plateforme pour vous assurer que Toolkit n’interrompt pas la génération sur une autre plateforme

  • Utilisez afin WINDOWS_UWP d’exécuter des API non Unity spécifiques à UWP. Cette définition les empêchera d’essayer de s’exécuter dans l’éditeur ou sur des plateformes non prises en charge. Cette définition est équivalente à UNITY_WSA && !UNITY_EDITOR et doit être privilégiée.
  • Utilisez UNITY_WSA pour exécuter des API Unity spécifiques à UWP, par exemple l’espace de noms UnityEngine.XR.WSA. Cette opération s’exécute dans l’éditeur lorsque la plateforme est définie sur UWP et dans les applications UWP intégrées.

Ce graphique peut vous aider à choisir ce qui #if doit être utilisé, en fonction de vos cas d’usage et des paramètres de build attendus.

Définir UWP IL2CPP UWP .NET Éditeur
UNITY_EDITOR False False True
UNITY_WSA True True True
WINDOWS_UWP True True False
UNITY_WSA && !UNITY_EDITOR True True False
ENABLE_WINMD_SUPPORT True True False
NETFX_CORE False True False

Préférez DateTime.UtcNow à DateTime.Now

DateTime.UtcNow est plus rapide que DateTime.Now. Dans les enquêtes de performances précédentes, nous avons découvert que l’utilisation de DateTime.Now ajoute une surcharge significative, en particulier dans la boucle Update(). D’autres ont rencontré ce même problème.

Utilisez de préférence DateTime.UtcNow, sauf si vous avez réellement besoin d’heures localisées (vous souhaitiez par exemple afficher l’heure actuelle dans le fuseau horaire de l’utilisateur). Si vous utilisez des heures relatives (c’est-à-dire le delta entre une dernière mise à jour et maintenant), il est préférable d’utiliser DateTime.UtcNow pour éviter la surcharge liée à la conversion des fuseaux horaires.