Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
C# et .NET, les technologies sous-jacentes aux scripts Unity ont continué de recevoir des mises à jour depuis Microsoft les ont publiées à l’origine en 2002. Toutefois, les développeurs Unity ne connaissent peut-être pas le flux stable de nouvelles fonctionnalités ajoutées au langage C# et .NET Framework, car avant Unity 2017.1, Unity utilise un runtime de script équivalent .NET 3.5, il manque des années de mises à jour.
Avec la version d’Unity 2017.1, Unity a introduit une version expérimentale de son runtime de script mis à niveau vers une version compatible .NET 4.6, C# 6.0. Dans Unity 2018.1, le runtime équivalent .NET 4.x n’est plus considéré comme expérimental, tandis que l’ancien runtime équivalent .NET 3.5 est désormais considéré comme la version héritée. Avec la sortie d’Unity 2018.3, Unity prévoit de faire de l’environnement d’exécution de scripts mis à niveau l’option par défaut, et d’aller encore plus loin avec une mise à niveau vers C# 7. Pour plus d’informations et les dernières mises à jour de cette feuille de route, lisez le billet de blog d’Unity ou visitez son forum des préversions de script expérimental. En attendant, consultez les sections ci-dessous pour en savoir plus sur les nouvelles fonctionnalités disponibles maintenant avec le runtime de script .NET 4.x.
Prerequisites
- Unity 2022.2 ou version ultérieure (2022.1.7 recommandé)
- Visual Studio 2019
Activation du runtime de script .NET 4.x dans Unity
Pour activer le runtime de script .NET 4.x, procédez comme suit :
Ouvrez PlayerSettings dans l’Inspector Unity en sélectionnant Edit > Project Settings > Player > Other Settings.
Sous le titre Configuration, cliquez sur la liste déroulante Niveau de compatibilité de l’API, puis sélectionnez .NET Framework. Vous serez invité à redémarrer Unity.
Choix entre .NET profils 4.x et .NET Standard 2.1
Une fois que vous avez basculé vers le runtime de script équivalent à .NET 4.x, vous pouvez spécifier le Niveau de compatibilité de l’API à l’aide du menu déroulant des PlayerSettings (Edit > Project Settings > Player). Nous avons deux options :
.NET Standard 2.1. Ce profil correspond au profil .NET Standard 2.1 publié par la Fondation .NET. Unity recommande .NET Standard 2.1 pour les nouveaux projets. Il est inférieur à .NET 4.x, ce qui est avantageux pour les plateformes contraintes de taille. De plus, Unity s’est engagé à prendre en charge ce profil sur toutes les plateformes prises en charge par Unity.
.NET Framework. Ce profil permet d’accéder à la dernière API .NET 4. Il inclut tout le code disponible dans les bibliothèques de classes .NET Framework et prend également en charge .NET profils Standard 2.1. Utilisez le profil .NET 4.x si votre projet nécessite une partie de l’API non incluse dans le profil .NET Standard 2.0. Toutefois, certaines parties de cette API peuvent ne pas être prises en charge sur toutes les plateformes d’Unity.
Pour en savoir plus sur ces options, consultez le billet de blog d’Unity.
Ajout de références d’assembly lors de l’utilisation du niveau de compatibilité de l’API .NET 4.x
Lorsque vous utilisez le paramètre .NET Standard 2.1 dans la liste déroulante Niveau de compatibilité des API, tous les assemblys du profil d’API sont référencés et utilisables. Toutefois, lors de l’utilisation du profil .NET 4.x le plus étendu, certaines des bibliothèques fournies avec Unity ne sont pas référencées par défaut. Pour utiliser ces API, vous devez ajouter manuellement une référence d’assembly. Vous pouvez consulter les assemblages livrés avec Unity dans le répertoire MonoBleedingEdge/lib/mono de votre installation de l’éditeur Unity :
Par exemple, si vous utilisez le profil .NET 4.x et que vous souhaitez utiliserHttpClient, vous devez ajouter une référence d'assembly pour System.Net.Http.dll. Sans cela, le compilateur se plaindra que vous manquez une référence d’assembly :
Visual Studio régénère les fichiers .csproj et .sln pour les projets Unity chaque fois qu'ils sont ouverts. Par conséquent, vous ne pouvez pas ajouter de références d'assembly directement dans Visual Studio, car elles seront perdues lors de la réouverture du projet. Au lieu de cela, un fichier texte spécial nommé csc.rsp doit être utilisé :
Créez un fichier texte nommé csc.rsp dans le répertoire ressources racine de votre projet Unity.
Sur la première ligne du fichier texte vide, entrez :
-r:System.Net.Http.dllpuis enregistrez le fichier. Vous pouvez remplacer «System.Net.Http.dll» par n’importe quel assembly inclus qui peut manquer une référence.Redémarrez l’éditeur Unity.
Tirer parti de la compatibilité .NET
Outre les nouvelles fonctionnalités de syntaxe et de langage C#, le runtime de script .NET 4.x permet aux utilisateurs Unity d’accéder à une bibliothèque énorme de packages .NET incompatibles avec le runtime de script hérité .NET 3.5.
Ajouter des packages de NuGet à un projet Unity
NuGet est le gestionnaire de package pour .NET. NuGet est intégré à Visual Studio. Toutefois, les projets Unity nécessitent un processus spécial pour ajouter des packages NuGet, car lorsque vous ouvrez un projet dans Unity, ses fichiers projet Visual Studio sont régénérés, annulant les configurations nécessaires. Pour ajouter un package à partir de NuGet, à votre projet Unity :
Parcourez NuGet pour rechercher un package compatible que vous souhaitez ajouter (.NET Standard 2.0 ou .NET 4.x). Cet exemple illustre l’ajout de Json.NET, un package populaire pour l’utilisation de JSON, à un projet .NET Standard 2.0.
Cliquez sur le bouton Télécharger :
Recherchez le fichier téléchargé et remplacez l’extension de .nupkg par .zip.
Dans le fichier zip, accédez au répertoire lib/netstandard2.0 et copiez le fichier Newtonsoft.Json.dll .
Dans le dossier Ressources racines de votre projet Unity, créez un dossier nommé Plug-ins. Les plug-ins sont un nom de dossier spécial dans Unity. Pour plus d’informations, consultez la documentation Unity.
Collez le fichier Newtonsoft.Json.dll dans le répertoire plug-ins de votre projet Unity.
Créez un fichier nommé link.xml dans le répertoire Assets de votre projet Unity et ajoutez le code XML suivant, en veillant à ce que le processus de suppression d’octets d’Unity ne supprime pas les données nécessaires lors de l’exportation vers une plateforme IL2CPP. Bien que cette étape soit spécifique à cette bibliothèque, vous pouvez rencontrer des problèmes avec d’autres bibliothèques qui utilisent Reflection de manière similaire. Pour plus d’informations, consultez les documents d’Unity sur cet article.
<linker> <assembly fullname="System.Core"> <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" /> </assembly> </linker>
Avec tout ce qui est en place, vous pouvez désormais utiliser le package Json.NET.
using Newtonsoft.Json;
using UnityEngine;
public class JSONTest : MonoBehaviour
{
class Enemy
{
public string Name { get; set; }
public int AttackDamage { get; set; }
public int MaxHealth { get; set; }
}
private void Start()
{
string json = @"{
'Name': 'Ninja',
'AttackDamage': '40'
}";
var enemy = JsonConvert.DeserializeObject<Enemy>(json);
Debug.Log($"{enemy.Name} deals {enemy.AttackDamage} damage.");
// Output:
// Ninja deals 40 damage.
}
}
Il s’agit d’un exemple simple d’utilisation d’une bibliothèque, qui n’a aucune dépendance. Lorsque les packages NuGet s’appuient sur d’autres packages NuGet, vous devez télécharger ces dépendances manuellement et les ajouter au projet de la même façon.
Nouvelles fonctionnalités de syntaxe et de langage
L’utilisation de l’environnement d’exécution de script mis à jour permet aux développeurs Unity d’accéder à C# 8 ainsi qu’à de nombreuses nouvelles fonctionnalités et améliorations de syntaxe du langage.
Initialiseurs de propriété automatique
Dans le runtime de script .NET 3.5 d'Unity, la syntaxe de propriété automatique facilite la définition rapide des propriétés non initialisées, mais l'initialisation doit se produire ailleurs dans votre script. Maintenant, avec le runtime .NET 4.x, il est possible d'initialiser les propriétés automatiques dans la même ligne :
// .NET 3.5
public int Health { get; set; } // Health has to be initialized somewhere else, like Start()
// .NET 4.x
public int Health { get; set; } = 100;
Interpolation de chaîne
Avec l’ancienne .NET runtime 3.5, la concaténation de chaînes nécessite une syntaxe maladroite. Maintenant, avec le runtime .NET 4.x, la $ fonctionnalité d’interpolation de chaîne permet aux expressions d’être insérées dans des chaînes dans une syntaxe plus directe et lisible :
// .NET 3.5
Debug.Log(String.Format("Player health: {0}", Health)); // or
Debug.Log("Player health: " + Health);
// .NET 4.x
Debug.Log($"Player health: {Health}");
Membres expression-bodied
Avec la syntaxe C# plus récente disponible dans le runtime .NET 4.x, les expressions lambda peuvent remplacer le corps des fonctions pour les rendre plus succinctes :
// .NET 3.5
private int TakeDamage(int amount)
{
return Health -= amount;
}
// .NET 4.x
private int TakeDamage(int amount) => Health -= amount;
Vous pouvez également utiliser des membres expression-bodied dans des propriétés en lecture seule :
// .NET 4.x
public string PlayerHealthUiText => $"Player health: {Health}";
Modèle asynchrone basé sur des tâches (TAP)
La programmation asynchrone permet d’effectuer des opérations fastidieuses sans que votre application ne réponde pas. Cette fonctionnalité permet également à votre code d’attendre la fin des opérations fastidieuses avant de continuer avec le code qui dépend des résultats de ces opérations. Par exemple, vous pouvez attendre la fin d’un chargement d’un fichier ou d’une opération réseau.
Dans Unity, la programmation asynchrone se fait généralement à l’aide de coroutines. Toutefois, depuis C# 5, la méthode privilégiée de programmation asynchrone dans le développement .NET est le modèle asynchrone basé sur les tâches (TAP), utilisant les mots-clés async et await avec System.Threading.Task. En résumé, dans une async fonction, vous pouvez await terminer une tâche sans bloquer la mise à jour du reste de votre application :
// Unity coroutine
using UnityEngine;
public class UnityCoroutineExample : MonoBehaviour
{
private void Start()
{
StartCoroutine(WaitOneSecond());
DoMoreStuff(); // This executes without waiting for WaitOneSecond
}
private IEnumerator WaitOneSecond()
{
yield return new WaitForSeconds(1.0f);
Debug.Log("Finished waiting.");
}
}
// .NET 4.x async-await
using UnityEngine;
using System.Threading.Tasks;
public class AsyncAwaitExample : MonoBehaviour
{
private async void Start()
{
Debug.Log("Wait.");
await WaitOneSecondAsync();
DoMoreStuff(); // Will not execute until WaitOneSecond has completed
}
private async Task WaitOneSecondAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1));
Debug.Log("Finished waiting.");
}
}
TAP est un sujet complexe, avec des nuances spécifiques à Unity que les développeurs doivent prendre en compte. TAP n’est donc pas un remplacement universel des coroutines dans Unity. Toutefois, il s’agit d’un outil parmi d’autres à utiliser. L’étendue de cette fonctionnalité est au-delà de cet article, mais certaines bonnes pratiques générales et conseils sont fournis ci-dessous.
Informations de référence pour bien démarrer avec TAP dans Unity
Ces conseils peuvent vous aider à bien démarrer avec TAP dans Unity :
- Les fonctions asynchrones destinées à être attendues doivent avoir le type de retour
TaskouTask<TResult>. - Les fonctions asynchrones qui retournent une tâche doivent avoir le suffixe « Async » ajouté à leurs noms. Le suffixe « Async » permet d’indiquer qu’une fonction doit toujours être attendue.
- Utilisez uniquement le
async voidtype de retour pour les fonctions qui déclenchent des fonctions asynchrones à partir du code synchrone traditionnel. Ces fonctions ne peuvent pas elles-mêmes être attendues et ne doivent pas avoir le suffixe « Async » dans leurs noms. - Unity utilise UnitySynchronizationContext pour garantir que les fonctions asynchrones s’exécutent sur le thread principal par défaut. L’API Unity n’est pas accessible en dehors du thread principal.
- Il est possible d’exécuter des tâches sur des threads en arrière-plan avec des méthodes comme
Task.RunetTask.ConfigureAwait(false). Cette technique est utile pour décharger les opérations coûteuses du thread principal afin d’améliorer les performances. Toutefois, l’utilisation de threads d’arrière-plan peut entraîner des problèmes difficiles à déboguer, tels que les conditions de concurrence. - L’API Unity n’est pas accessible en dehors du thread principal.
- Les tâches qui utilisent des threads ne sont pas prises en charge sur les builds WebGL Unity.
Différences entre les coroutines et TAP
Il existe quelques différences importantes entre les coroutines et TAP / async-await :
- Les coroutines ne peuvent pas retourner de valeurs, contrairement à
Task<TResult>. - Vous ne pouvez pas mettre
yielddans une instruction try-catch, ce qui complique les erreurs de gestion avec les coroutines. Toutefois, try-catch fonctionne avec TAP. - La fonctionnalité coroutine d’Unity n’est pas disponible dans les classes qui ne dérivent pas de MonoBehaviour. TAP est idéal pour la programmation asynchrone dans ces classes.
- À ce stade, Unity ne suggère pas d’utiliser TAP en tant que remplacement systématique des coroutines. Le profilage est la seule façon de connaître les résultats spécifiques d’une approche par rapport à l’autre pour un projet donné.
opérateur nameof
L’opérateur nameof obtient le nom de chaîne d’une variable, d’un type ou d’un membre. Dans certains cas où nameof s’avère utile, notamment pour journaliser les erreurs et obtenir le nom sous forme de chaîne d’une énumération :
// Get the string name of an enum:
enum Difficulty {Easy, Medium, Hard};
private void Start()
{
Debug.Log(nameof(Difficulty.Easy));
RecordHighScore("John");
// Output:
// Easy
// playerName
}
// Validate parameter:
private void RecordHighScore(string playerName)
{
Debug.Log(nameof(playerName));
if (playerName == null) throw new ArgumentNullException(nameof(playerName));
}
Attributs des informations sur l’appelant
Les attributs d’informations de l’appelant fournissent des informations sur l’appelant d’une méthode. Vous devez fournir une valeur par défaut pour chaque paramètre que vous souhaitez utiliser avec un attribut Info de l’appelant :
private void Start ()
{
ShowCallerInfo("Something happened.");
}
public void ShowCallerInfo(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
Debug.Log($"message: {message}");
Debug.Log($"member name: {memberName}");
Debug.Log($"source file path: {sourceFilePath}");
Debug.Log($"source line number: {sourceLineNumber}");
}
// Output:
// Something happened
// member name: Start
// source file path: D:\Documents\unity-scripting-upgrade\Unity Project\Assets\CallerInfoTest.cs
// source line number: 10
Utilisation de static
L’utilisation de static vous permet d’utiliser des fonctions statiques sans taper son nom de classe. Avec l’utilisation statique, vous pouvez économiser de l’espace et du temps si vous devez utiliser plusieurs fonctions statiques à partir de la même classe :
// .NET 3.5
using UnityEngine;
public class Example : MonoBehaviour
{
private void Start ()
{
Debug.Log(Mathf.RoundToInt(Mathf.PI));
// Output:
// 3
}
}
// .NET 4.x
using UnityEngine;
using static UnityEngine.Mathf;
public class UsingStaticExample: MonoBehaviour
{
private void Start ()
{
Debug.Log(RoundToInt(PI));
// Output:
// 3
}
}
Considérations relatives à IL2CPP
Lors de l’exportation de votre jeu vers des plateformes comme iOS, Unity utilise son moteur IL2CPP pour « transpiler » IL vers du code C++ qui est ensuite compilé à l’aide du compilateur natif de la plateforme cible. Dans ce scénario, il existe plusieurs fonctionnalités .NET qui ne sont pas prises en charge, telles que des parties de réflexion et l'utilisation du dynamic mot clé. Bien que vous puissiez contrôler l’utilisation de ces fonctionnalités dans votre propre code, vous risquez de rencontrer des problèmes à l’aide de DLL et sdk tiers qui n’ont pas été écrits avec Unity et IL2CPP à l’esprit. Pour plus d’informations sur cet article, consultez la documentation sur les restrictions de script sur le site d’Unity.
En outre, comme mentionné dans l’exemple Json.NET ci-dessus, Unity tente de supprimer le code inutilisé pendant le processus d’exportation IL2CPP. Bien que ce processus ne soit généralement pas un problème, avec les bibliothèques qui utilisent Reflection, elle peut supprimer accidentellement des propriétés ou des méthodes qui seront appelées au moment de l’exécution qui ne peuvent pas être déterminées au moment de l’exportation. Pour résoudre ces problèmes, ajoutez à votre projet un fichier link.xml contenant une liste d’assemblys et d’espaces de noms à exclure du processus de suppression par stripping. Pour plus d’informations, consultez la documentation Unity sur la suppression du bytecode.
Exemple de projet Unity .NET 4.x
L’exemple contient des exemples de plusieurs fonctionnalités .NET 4.x. Vous pouvez télécharger le projet ou afficher le code source sur GitHub.