Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
C# и .NET технологии, базовые сценарии Unity, продолжают получать обновления, так как Microsoft первоначально выпустили их в 2002 году. Но разработчики Unity могут не знать о постоянном появлении новых возможностей в языке C# и .NET Framework, поскольку до версии Unity 2017.1 в Unity использовалась среда выполнения сценариев, эквивалентная .NET 3.5, из-за чего были упущены обновления за несколько лет.
В выпуске Unity 2017.1 Unity представила экспериментальную версию среды выполнения сценариев, обновленную до совместимой версии .NET 4.6, C# 6.0. В Unity 2018.1 эквивалентная среда выполнения .NET 4.x больше не считается экспериментальной, в то время как более старая среда выполнения .NET 3.5 теперь считается устаревшей версией. С выходом Unity 2018.3 компания Unity планирует сделать обновлённую среду выполнения скриптов вариантом по умолчанию, а также перейти на C# 7. Дополнительные сведения и последние обновления на этой дорожной карте см. в записи блога Unity или посетите форум по предварительной версии экспериментальных сценариев. В то же время ознакомьтесь с приведенными ниже разделами, чтобы узнать больше о новых функциях, доступных сейчас с помощью среды выполнения сценариев .NET 4.x.
Необходимые условия
- Unity 2022.2 или более поздней версии (рекомендуется 2022.1.7)
- Visual Studio 2019 г.
Включение среды выполнения скриптов .NET 4.x в Unity
Чтобы включить среду выполнения скриптов .NET 4.x, выполните следующие действия:
Откройте PlayerSettings в окне Inspector в Unity, выбрав Edit > Project Settings > Player > Other Settings.
В разделе "Конфигурация" щелкните раскрывающийся список уровня совместимости API и выберите .NET Framework. Вам будет предложено перезапустить Unity.
Выбор между профилями .NET 4.x и .NET Standard 2.1
После переключения на эквивалентную среду выполнения скриптов .NET 4.x вы можете указать уровень совместимости API с помощью раскрывающегося списка в PlayerSettings (Edit > Project Settings > Player). Существует два варианта.
.NET standard 2.1. Этот профиль соответствует профилю .NET Standard 2.1, опубликованному .NET Foundation. Unity рекомендует .NET Standard 2.1 для новых проектов. Это меньше, чем .NET 4.x, что выгодно для платформ с ограниченным размером. Кроме того, Unity привержена поддержке этого профиля на всех платформах, поддерживаемых Unity.
.NET Framework. Этот профиль предоставляет доступ к последнему .NET 4 API. Он включает весь код из библиотек классов .NET Framework и также поддерживает профили .NET Standard 2.1. Используйте профиль .NET 4.x, если для проекта требуется часть API, не включенная в профиль .NET standard 2.0. Однако некоторые части этого API могут не поддерживаться на всех платформах Unity.
Дополнительные сведения об этих параметрах см. в записи блога Unity.
Добавление ссылок на сборки при использовании уровня совместимости API .NET 4.x
При использовании параметра .NET standard 2.1 в раскрывающемся списке уровня совместимости API все сборки в профиле API ссылаются и доступны для использования. Однако при использовании более полного профиля .NET 4.x на некоторые сборки, поставляемые вместе с Unity, по умолчанию не создаются ссылки. Чтобы использовать эти API, необходимо вручную добавить ссылку на сборку. Сборки, поставляемые с Unity, можно просмотреть в каталоге MonoBleedingEdge/lib/mono в каталоге установки редактора Unity:
Например, если вы используете профиль .NET 4.x и хотите использовать HttpClient, необходимо добавить ссылку на сборку System.Net.Http.dll. Без него компилятор будет жаловаться на отсутствие ссылки на сборку:
Visual Studio пересоздает файлы .csproj и .sln для проектов Unity каждый раз при их открытии. В результате вы не можете добавлять ссылки на сборки непосредственно в Visual Studio, так как они будут потеряны при повторном открытии проекта. Вместо этого необходимо использовать специальный текстовый файл csc.rsp :
Создайте текстовый файл csc.rsp в корневом каталоге ресурсов проекта Unity.
В первой строке в пустом текстовом файле введите:
-r:System.Net.Http.dllи сохраните файл. Вы можете заменить "System.Net.Http.dll" на любую включённую сборку, у которой может отсутствовать ссылка.Перезапустите редактор Unity.
Использование преимуществ совместимости .NET
Помимо новых функций синтаксиса И языка C# среда выполнения скриптов .NET 4.x предоставляет пользователям Unity доступ к огромной библиотеке пакетов .NET, несовместимых с устаревшей средой выполнения сценариев .NET 3.5.
Добавление пакетов из NuGet в проект Unity
NuGet — это диспетчер пакетов для .NET. NuGet интегрирован в Visual Studio. Однако для проектов Unity требуется специальный процесс добавления пакетов NuGet, так как при открытии проекта в Unity его Visual Studio файлы проектов создаются повторно, отменяя необходимые конфигурации. Чтобы добавить пакет из NuGet, в проект Unity:
Просмотрите NuGet, чтобы найти совместимый пакет, который вы хотите добавить (.NET standard 2.0 или .NET 4.x). В этом примере демонстрируется добавление Json.NET, популярного пакета для работы с JSON в проект .NET standard 2.0.
Нажмите кнопку "Скачать ":
Найдите скачанный файл и измените расширение с .nupkg на .zip.
В ZIP-файле перейдите в каталог lib/netstandard2.0 и скопируйте файлNewtonsoft.Json.dll .
В корневой папке проекта Unity создайте новую папку с именем Подключаемые модули. Plugins — это специальное название папки в Unity. Дополнительные сведения см. в документации по Unity.
Вставьте файлNewtonsoft.Json.dll в каталог подключаемых модулей проекта Unity.
Создайте файл с именемlink.xml в каталоге ресурсов проекта Unity и добавьте следующий XML-код, чтобы процесс удаления байт-кода Unity не удалял необходимые данные при экспорте на платформу IL2CPP. Хотя этот шаг предназначен для этой библиотеки, вы можете столкнуться с проблемами с другими библиотеками, которые используют отражение аналогичным образом. Дополнительные сведения см. в документации Unity по этой теме.
<linker> <assembly fullname="System.Core"> <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" /> </assembly> </linker>
Теперь, когда всё готово, вы можете использовать пакет 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.
}
}
Это простой пример использования библиотеки, которая не имеет зависимостей. Если пакеты NuGet используют другие пакеты NuGet, необходимо вручную скачать эти зависимости и добавить их в проект таким же образом.
Новые функции синтаксиса и языка
С помощью обновленной среды выполнения сценариев разработчики Unity получают доступ к C# 8 и узлу новых функций языка и синтаксиса.
Инициализаторы автоматических свойств
В среде выполнения сценариев Unity .NET 3.5 синтаксис автоматического свойства упрощает быстрое определение неинициализированных свойств, но инициализация должна происходить в другом месте скрипта. Теперь с помощью среды выполнения .NET 4.x можно инициализировать автоматические свойства в той же строке:
// .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;
Интерполяция строк
В более старой среде выполнения .NET 3.5 для конкатенации строк приходилось использовать неудобный синтаксис. Теперь в среде выполнения .NET 4.x функция $интерполяции строк позволяет вставлять выражения в строки, используя более простой и удобочитаемый синтаксис:
// .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}");
Элементы, воплощающие выражение
Благодаря более новому синтаксису C# в среде выполнения .NET 4.x лямбда-выражения могут заменить текст функций, чтобы сделать их более краткими:
// .NET 3.5
private int TakeDamage(int amount)
{
return Health -= amount;
}
// .NET 4.x
private int TakeDamage(int amount) => Health -= amount;
Можно также использовать элементы с выражением в свойствах, доступных только для чтения:
// .NET 4.x
public string PlayerHealthUiText => $"Player health: {Health}";
Асинхронный шаблон на основе задач (TAP)
Асинхронное программирование позволяет выполнять длительные операции, не приводя к зависанию приложения. Эта функция также позволяет коду ожидать завершения длительных операций, прежде чем продолжать работу с кодом, зависящим от результатов этих операций. Например, можно ожидать завершения загрузки файла или сетевой операции.
В Unity асинхронное программирование обычно реализуется с помощью корутин. Однако начиная с C# 5 предпочтительным методом асинхронного программирования при разработке на .NET является асинхронный шаблон, основанный на задачах (TAP) с использованием ключевых слов async и await и System.Threading.Task. В итоге в async функции можно await выполнить задачу без блокировки остальной части приложения от обновления:
// 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 — это сложная тема, и разработчики Unity должны учитывать конкретные нюансы. В результате TAP не является универсальной заменой для корутинов в Unity; однако это еще один инструмент для использования. Область действия этой функции выходит за рамки этой статьи, но ниже приведены некоторые общие рекомендации и советы.
Справочник по началу работы с TAP в Unity
Эти советы помогут вам приступить к работе с TAP в Unity:
- Асинхронные функции, предназначенные для ожидания, должны иметь тип возвращаемого значения
TaskилиTask<TResult>. - Асинхронные функции, возвращающие задачу, должны добавлять суффикс Async к их именам. Суффикс "Async" помогает указать, что функция всегда должна ожидаться.
- Используйте тип возвращаемого значения
async voidтолько для функций, которые вызывают асинхронные функции из обычного синхронного кода. Такие функции не могут ожидать себя и не должны иметь суффикс "Async" в их именах. - Unity использует UnitySynchronizationContext, чтобы обеспечить асинхронные функции, выполняемые в основном потоке по умолчанию. API Unity недоступен за пределами основного потока.
- Задачи можно выполнять в фоновых потоках с такими методами, как
Task.RunиTask.ConfigureAwait(false). Этот метод полезен для разгрузки дорогостоящих операций из основного потока для повышения производительности. Однако использование фоновых потоков может привести к проблемам, которые трудно отлаживать, такие как условия гонки. - API Unity недоступен за пределами основного потока.
- Задачи, использующие потоки, не поддерживаются в сборках Unity WebGL.
Различия между корутинами и TAP
Существуют некоторые важные различия между корутинами и TAP / async-await:
- Корутины не могут возвращать значения, а
Task<TResult>— могут. - Вы не можете использовать
yieldвнутри инструкции try-catch, что затрудняет обработку ошибок при использовании корутин. Однако try-catch работает и с TAP. - Функция корутины Unity недоступна в классах, которые не являются производными от MonoBehaviour. TAP отлично подходит для асинхронного программирования в таких классах.
- На данный момент Unity не предлагает TAP в качестве полноценной замены корутин. Профилирование — единственный способ узнать конкретные результаты одного подхода и другого для любого конкретного проекта.
оператор nameof
Оператор nameof получает строковое имя переменной, типа или члена. Некоторые случаи, когда nameof может пригодиться: регистрация ошибок в журнале и получение строкового имени перечисления.
// 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));
}
Атрибуты вызывающего абонента
Атрибуты сведений о вызывающем объекте предоставляют информацию о вызывающем объекте метода. Необходимо указать значение по умолчанию для каждого параметра, который вы хотите использовать с атрибутом Сведений вызывающего абонента:
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
Использование статики
Использование статических функций позволяет использовать статические функции без ввода имени класса. Используя статические функции, можно сэкономить место и время, если необходимо использовать несколько статических функций из одного класса:
// .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
}
}
Рекомендации по IL2CPP
При экспорте игры на такие платформы, как iOS, Unity будет использовать его подсистему IL2CPP для транспилирования il в код C++, который затем компилируется с помощью собственного компилятора целевой платформы. В этом сценарии не поддерживаются некоторые возможности .NET, такие как отдельные части механизма рефлексии и использование ключевого слова dynamic. Хотя вы можете управлять использованием этих функций в собственном коде, вы можете столкнуться с проблемами, используя сторонние библиотеки DLL и пакеты SDK, которые не были написаны с помощью Unity и IL2CPP. Дополнительные сведения об этой статье см. в документации по ограничениям сценариев на сайте Unity.
Кроме того, как упоминалось в примере json.NET выше, Unity попытается удалить неиспользуемый код во время процесса экспорта IL2CPP. Хотя этот процесс обычно не вызывает проблем, в случае библиотек, использующих рефлексию, он может случайно удалить свойства или методы, вызовы которых происходят во время выполнения и которые невозможно определить на этапе экспорта. Чтобы устранить эти проблемы, добавьте в проект файл link.xml, содержащий список сборок и пространств имён, к которым не следует применять процесс удаления. Дополнительные сведения см. в документации Unity по разделению байтов.
Пример проекта Unity для .NET 4.x
Пример содержит примеры нескольких функций .NET 4.x. Вы можете скачать проект или просмотреть исходный код на GitHub.