Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
C# y .NET, las tecnologías subyacentes al scripting de Unity, han seguido recibiendo actualizaciones desde Microsoft las publicó originalmente en 2002. Sin embargo, es posible que los desarrolladores de Unity no conozcan el flujo estable de nuevas características agregadas al lenguaje C# y .NET Framework, ya que antes de Unity 2017.1, Unity ha estado usando un entorno de ejecución de scripting equivalente .NET 3.5, faltan años de actualizaciones.
Con el lanzamiento de Unity 2017.1, Unity introdujo una versión experimental de su entorno de ejecución de scripting actualizado a una versión compatible con .NET 4.6, C# 6.0. En Unity 2018.1, el entorno de ejecución equivalente de .NET 4.x ya no se considera experimental, mientras que el entorno de ejecución equivalente de .NET 3.5 anterior ahora se considera la versión heredada. Con el lanzamiento de Unity 2018.3, Unity tiene previsto que el entorno de ejecución de scripts actualizado pase a ser la opción predeterminada y seguir actualizándolo hasta C# 7. Para obtener más información y las actualizaciones más recientes de este plan de desarrollo, lea la entrada de blog de Unity o visite su foro de versiones preliminares de Scripting Experimental. Mientras tanto, consulte las secciones siguientes para obtener más información sobre las nuevas características disponibles ahora con el entorno de ejecución de scripting de .NET 4.x.
Prerequisites
- Unity 2022.2 o superior (se recomienda 2022.1.7)
- Visual Studio 2019
Habilitar el runtime de scripting de .NET 4.x en Unity
Para habilitar el entorno de ejecución de scripting de .NET 4.x, siga estos pasos:
Abra PlayerSettings en el Inspector de Unity seleccionando Editar > Configuración del proyecto > Player > Otros ajustes.
En el encabezado Configuración, haga clic en la lista desplegable Nivel de compatibilidad de API y seleccione .NET Framework. Se le pedirá que reinicie Unity.
Elección entre perfiles de .NET 4.x y .NET Standard 2.1
Una vez que hayas cambiado al entorno de ejecución de scripting equivalente a .NET 4.x, puedes especificar el Nivel de compatibilidad de API mediante el menú desplegable en PlayerSettings (Editar > Configuración del proyecto > Reproductor). Hay dos opciones:
.NET Standard 2.1. Este perfil coincide con el perfil de .NET Standard 2.1 publicado por .NET Foundation. Unity recomienda .NET Standard 2.1 para proyectos nuevos. Es menor que .NET 4.x, lo que es ventajoso para las plataformas con restricciones de tamaño. Además, Unity se ha comprometido a admitir este perfil en todas las plataformas compatibles con Unity.
.NET Framework. Este perfil proporciona acceso a la API de .NET 4 más reciente. Incluye todo el código disponible en las bibliotecas de clases de .NET Framework y también admite perfiles de .NET Standard 2.1. Use el perfil de .NET 4.x si el proyecto requiere parte de la API no incluida en el perfil de .NET Standard 2.0. Sin embargo, es posible que algunas partes de esta API no se admita en todas las plataformas de Unity.
Para más información sobre estas opciones, consulte la entrada de blog de Unity.
Agregar referencias de ensamblado al usar el nivel de compatibilidad de API de .NET 4.x
Al usar la configuración de .NET Standard 2.1 en el menú desplegable Nivel de compatibilidad de API, todos los ensamblados del perfil de la API están referenciados y se pueden usar. Sin embargo, al usar el perfil más amplio de .NET 4.x, algunos de los ensamblados incluidos con Unity no se referencian de forma predeterminada. Para usar estas API, debe agregar manualmente una referencia de ensamblado. Puede ver los ensamblados que Unity incluye en el directorio MonoBleedingEdge/lib/mono de la instalación del editor de Unity:
Por ejemplo, si usa el perfil de .NET 4.x y quiere usar HttpClient, debe agregar una referencia de ensamblado para System.Net.Http.dll. Sin él, el compilador se quejará de que falta una referencia de ensamblado:
Visual Studio vuelve a generar archivos .csproj y .sln para proyectos de Unity cada vez que se abren. Como resultado, no puede agregar referencias de ensamblado directamente en Visual Studio porque se perderán al volver a abrir el proyecto. En su lugar, se debe usar un archivo de texto especial denominado csc.rsp :
Cree un nuevo archivo de texto denominado csc.rsp en el directorio assets raíz del proyecto de Unity.
En la primera línea del archivo de texto vacío, escriba:
-r:System.Net.Http.dlly guarde el archivo. Puede reemplazar "System.Net.Http.dll" por cualquier ensamblado incluido al que pueda faltarle una referencia.Reinicie el editor de Unity.
Aprovechar la compatibilidad de .NET
Además de las nuevas características de lenguaje y sintaxis de C#, el entorno de ejecución de scripting de .NET 4.x proporciona a los usuarios de Unity acceso a una biblioteca enorme de paquetes de .NET que no son compatibles con el entorno de ejecución de scripting heredado .NET 3.5.
Adición de paquetes de NuGet a un proyecto de Unity
NuGet es el administrador de paquetes para .NET. NuGet se integra en Visual Studio. Sin embargo, los proyectos de Unity requieren un proceso especial para agregar paquetes NuGet porque al abrir un proyecto en Unity, sus archivos de proyecto Visual Studio se vuelven a generar, lo que deshace las configuraciones necesarias. Para agregar un paquete de NuGet al proyecto de Unity:
Examine NuGet para buscar un paquete compatible que le gustaría agregar (.NET Standard 2.0 o .NET 4.x). En este ejemplo se muestra cómo agregar Json.NET, un paquete popular para trabajar con JSON, a un proyecto de .NET Standard 2.0.
Haga clic en el botón Descargar :
Busque el archivo descargado y cambie la extensión de .nupkg a .zip.
En el archivo ZIP, vaya al directorio lib/netstandard2.0 y copie el archivo Newtonsoft.Json.dll .
En la carpeta raíz Assets del proyecto de Unity, cree una carpeta nueva llamada Plugins. Los complementos son un nombre de carpeta especial en Unity. Para más información, consulte la documentación de Unity.
Pegue el archivo Newtonsoft.Json.dll en el directorio Plugins del proyecto de Unity.
Cree un archivo denominado link.xml en el directorio Assets del proyecto de Unity y agregue el siguiente XML, lo que garantiza que el proceso de eliminación de código de bytes de Unity no quite los datos necesarios al exportar a una plataforma IL2CPP. Aunque este paso es específico de esta biblioteca, puede encontrarse con problemas con otras bibliotecas que usan Reflection de maneras similares. Para obtener más información, consulte la documentación de Unity en este artículo.
<linker> <assembly fullname="System.Core"> <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" /> </assembly> </linker>
Con todo en su lugar, ahora puede usar el paquete 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.
}
}
Este es un ejemplo sencillo de usar una biblioteca, que no tiene dependencias. Cuando los paquetes NuGet se basan en otros paquetes NuGet, tendría que descargar estas dependencias manualmente y agregarlos al proyecto de la misma manera.
Nuevas características de sintaxis y lenguaje
El uso del entorno de ejecución de scripting actualizado proporciona a los desarrolladores de Unity acceso a C# 8 y a un host de nuevas características y sintaxis del lenguaje.
Inicializadores de propiedades automáticas
En el entorno de ejecución de scripts .NET 3.5 de Unity, la sintaxis de las propiedades automáticas permite definir rápidamente propiedades sin inicializar, pero la inicialización debe realizarse en otra parte de tu script. Ahora con el entorno de ejecución de .NET 4.x, es posible inicializar las propiedades automáticas en la misma línea:
// .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;
Interpolación de cadenas
Con el antiguo entorno de ejecución .NET 3.5, la concatenación de cadenas requería una sintaxis engorrosa. Ahora con el entorno de ejecución de .NET 4.x, la $ característica de interpolación de cadenas permite insertar expresiones en cadenas en una sintaxis más directa y legible:
// .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}");
Miembros con cuerpo de expresión
Con la sintaxis más reciente de C# disponible en el entorno de ejecución de .NET 4.x, las expresiones lambda pueden reemplazar el cuerpo de las funciones para que sean más concisos:
// .NET 3.5
private int TakeDamage(int amount)
{
return Health -= amount;
}
// .NET 4.x
private int TakeDamage(int amount) => Health -= amount;
También puede utilizar miembros implementados mediante expresiones en las propiedades de solo lectura:
// .NET 4.x
public string PlayerHealthUiText => $"Player health: {Health}";
Patrón asincrónico basado en tareas (TAP)
La programación asincrónica permite que se produzcan operaciones que consumen mucho tiempo sin provocar que la aplicación deje de responder. Esta funcionalidad también permite que el código espere a que finalicen las operaciones que consumen mucho tiempo antes de continuar con el código que depende de los resultados de estas operaciones. Por ejemplo, podría esperar a que se cargue un archivo o se complete una operación de red.
En Unity, la programación asincrónica se realiza normalmente con corrutinas. Sin embargo, desde C# 5, el método preferido de programación asincrónica en el desarrollo de .NET ha sido el patrón asincrónico basado en tareas (TAP) mediante las palabras clave async y await con System.Threading.Task. En resumen, en una async función puede completar await una tarea sin impedir que el resto de la aplicación actualice:
// 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 es un tema complejo, con matices específicos de Unity que los desarrolladores deben tener en cuenta. Como resultado, TAP no es un sustituto universal de las corrutinas en Unity; sin embargo, es una herramienta más. El ámbito de esta característica está fuera de este artículo, pero a continuación se proporcionan algunos procedimientos recomendados y sugerencias generales.
Referencia de introducción para TAP con Unity
Estas sugerencias pueden ayudarle a empezar a trabajar con TAP en Unity:
- Las funciones asincrónicas que deban esperarse deben tener el tipo de valor devuelto
TaskoTask<TResult>. - Las funciones asincrónicas que devuelven una tarea deben tener el sufijo "Async" anexado a sus nombres. El sufijo "Async" ayuda a indicar que siempre se debe esperar una función.
- Usa solo el
async voidtipo de devolución para las funciones que inician funciones asíncronas desde código síncrono tradicional. Estas funciones no se pueden esperar y no deben tener el sufijo "Async" en sus nombres. - Unity usa UnitySynchronizationContext para asegurarse de que las funciones asincrónicas se ejecutan en el subproceso principal de forma predeterminada. La API de Unity no es accesible fuera del hilo principal.
- Es posible ejecutar tareas en subprocesos en segundo plano con métodos como
Task.RunyTask.ConfigureAwait(false). Esta técnica es útil para descargar operaciones costosas desde el subproceso principal para mejorar el rendimiento. Sin embargo, el uso de subprocesos en segundo plano puede provocar problemas difíciles de depurar, como condiciones de carrera. - No se puede acceder a la API de Unity fuera del subproceso principal.
- Las tareas que usan subprocesos no se admiten en las compilaciones de WebGL de Unity.
Diferencias entre corrutinas y TAP
Existen algunas diferencias importantes entre las corrutinas y TAP/async-await:
- Las corrutinas no pueden devolver valores, pero
Task<TResult>sí. - No se puede poner un
yielden una instrucción try-catch, lo que dificulta el manejo de errores con corrutinas. Sin embargo, try-catch funciona con TAP. - La funcionalidad de corrutinas de Unity no está disponible para las clases que no derivan de MonoBehaviour. TAP es excelente para la programación asincrónica en estas clases.
- Por el momento, Unity no sugiere TAP como sustituto completo de las corrutinas. La generación de perfiles es la única manera de conocer los resultados específicos de un enfoque frente al otro para cualquier proyecto determinado.
operador nameof
El nameof operador obtiene el nombre de cadena de una variable, un tipo o un miembro. Algunos casos nameof en los que resulta útil son los errores de registro y obtener el nombre de cadena de una enumeración:
// 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));
}
Atributos de información del autor de la llamada
Atributos de información del llamador proporcionan información sobre quien llama a un método. Debe proporcionar un valor predeterminado para cada parámetro que desee usar con un atributo de información de la persona que llama:
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
Uso de static
El uso de static permite usar funciones estáticas sin escribir su nombre de clase. Con el uso de static, puede ahorrar espacio y tiempo si necesita usar varias funciones estáticas de la misma clase:
// .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
}
}
Consideraciones sobre IL2CPP
Al exportar el juego a plataformas como iOS, Unity usará su motor IL2CPP para "transpile" IL al código C++ que luego se compila mediante el compilador nativo de la plataforma de destino. En este escenario, hay varias características de .NET que no se admiten, como partes de Reflexión y uso de la dynamic palabra clave . Aunque puede controlar el uso de estas funciones en su propio código, puede encontrarse con problemas al usar DLL y SDK de terceros que no se desarrollaron pensando en Unity e IL2CPP. Para obtener más información acerca de este artículo, consulte la documentación Restricciones de scripting en el sitio web de Unity.
Además, como se mencionó en el ejemplo de Json.NET anterior, Unity intentará quitar el código sin usar durante el proceso de exportación de IL2CPP. Aunque este proceso normalmente no supone un problema, en bibliotecas que usan reflexión puede eliminar accidentalmente propiedades o métodos a los que se llamará en tiempo de ejecución y que no pueden determinarse en el momento de la exportación. Para corregir estos problemas, añada un archivo link.xml a su proyecto que contenga una lista de ensamblados y espacios de nombres para excluirlos del proceso de eliminación. Para obtener más información, consulte la documentación de Unity sobre la eliminación de código de bytes.
Proyecto de ejemplo de Unity para .NET 4.x
El ejemplo contiene ejemplos de varias características de .NET 4.x. Puede descargar el proyecto o ver el código fuente en GitHub.