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 utiliserprotected
.
Les champs doivent toujours être
private
, avec les accesseurs de propriétépublic
ouprotected
.
À 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 nomsUnityEngine.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.