Unity e .NET, o que é o próximo?

Iniciamos recentemente uma iniciativa plurianual para ajudá-lo a escrever código de maior desempenho mais rápido e fornecer estabilidade e compatibilidade a longo prazo. Continue lendo para descobrir o que estamos fazendo para atualizar a pilha de tecnologia básica por trás de seus scripts.
O ecossistema.NET está evoluindo dinamicamente de várias maneiras benéficas, e queremos trazer essas melhorias para você o mais rápido possível. Nosso grupo interno .NET Tech trabalha em melhoria contínua de nossa integração .NET, incluindo novos recursos C# e .NET Standard 2.1. Mas recentemente, colocamos as coisas em um ritmo mais elevado para melhorar a sua experiência de desenvolvedor em todo o painel, com base no seu feedback.
Este post de blog apresenta os temas que estamos trabalhando. Também discutimos este tópico na Unity Dev Summit na GDC 2022. Você pode assistir a sessão completa aqui.
A história começa há 17 anos, quando nosso CTO começou a aproveitar o Mono .NET runtime com C#. Unity favoreceu o C# por sua simplicidade, combinado com um compilador JIT (just-in-time) que traduz seu C# em código nativo relativamente eficiente. As partes restantes e muito maiores do motor Unity foram desenvolvidas usando C++ para fornecer um desempenho equilibrado e controlado.
Por muitos anos, Unity tinha sido executado com uma forca específica do Mono .NET runtime e linguagem C# (2.0). Durante esse tempo, adicionamos suporte para plataformas adicionais. Também desenvolvemos nosso próprio compilador e runtime, IL2CPP, para permitir que você direcione para iOS e algumas plataformas de console.
Entretanto, o ecossistema global do Microsoft .NET evoluiu, com novas licenças e suporte para plataformas não Windows. Esta evolução nos permitiu atualizar o Unity .NET Mono Runtime em 2018 e abraçar versões mais modernas do idioma C# (7.0+). No mesmo ano, também lançamos a primeira versão do compilador Burst, pioneiro em código nativo rápido gerado para um subconjunto da linguagem C#. Este avanço permitiu a Unity imaginar um mundo onde poderíamos estender o uso de C# nos outros segmentos críticos do motor sem ter que desenvolver essas partes em C++, levando ao desenvolvimento do runtime DOTS.
O Unity 2020 LTS e o Unity 2021 LTS trouxeram versões mais recentes do idioma C# e novas APIs .NET. Paralelamente, vimos enormes melhorias de desempenho serem entregues no ecossistema .NET, bem como um ambiente de desenvolvimento mais amigável com a introdução do estilo SDK csproj e o florido ecossistema NuGet.
Como resultado desta longa evolução, a plataforma Unity inclui uma grande base de código C++ que interage diretamente com objetos .NET usando suposições específicas herdadas do Mono .NET Runtime. Estes não são mais válidos ou eficientes para o .NET (Core) Runtime.
Além disso, há um pipeline complicado de compilação personalizada ligado ao Editor Unity que não depende do MSBuild e, portanto, não pode facilmente beneficiar de todos os recursos padrão.
Também conversamos com muitos de vocês nos últimos anos, tanto em entrevistas quanto no Unity forum, para ver o que podemos melhorar para melhorar o seu sucesso. O que ouvimos é que você quer usar a mais recente linguagem C#, a tecnologia de execução .NET e código C# de terceiros da NuGet. Quando se trata de usar a plataforma Unity, você nos disse que queria tirar o máximo proveito do hardware alvo com testes de C# de alta qualidade, ferramentas de depuração e perfilização e boa integração entre a .NET API padrão e a Unity API. Como um programador C# Unity, você quer ferramentas Unity que funcionem sem problemas com o resto da caixa de ferramentas e permitam iterações rápidas, para que você possa alcançar o melhor desempenho em tempo de execução da classe.
Chegar lá vai levar-nos vários anos. Vamos mantê-lo atualizado com atualizações frequentes do blog e do fórum sobre os desafios técnicos que encontramos ao longo do caminho.
Nosso primeiro passo nessa iniciativa foi reunir-se com todas as pessoas internas apaixonadas por C# e .NET em Unity para formar um Grupo de Tecnologia C#/.NET para impulsionar esse esforço.
Queremos construir sobre o ecossistema .NET em vez de desenvolver soluções personalizadas. Para permitir que você aproveite as melhorias de desempenho e produtividade que vêm com o mais recente .NET SDK/Runtime e MSBuild, queremos migrar do Mono .NET Runtime para CoreCLR, o moderno .NET (Core) Runtime.
Esta iniciativa também está trazendo inovação para além do universo .NET existente, com os objetivos de entregar ciclos de iteração .NET mais rápidos em seus scripts C#. Vamos trabalhar na convergência das soluções JIT e AOT (ahead-of-time) – IL2CPP e Burst – para oferecer o melhor equilíbrio entre a eficiência do tempo de compilação e a qualidade do CodeGen.
Externamente, estamos trabalhando com parceiros do setor, como a Microsoft e a JetBrains, para garantir que os criadores do Unity usem a mais recente tecnologia .NET. Também estamos aumentando nossa participação em comunidades de código aberto. Vamos dividir esse esforço em várias etapas. Vamos ver o que vem a seguir.
Este ano, as equipes planejam trabalhar nas seguintes faixas.

O tempo de iteração continua sendo nossa primeira prioridade, pois sabemos que você quer tirar mais proveito do seu tempo. Aqui estão alguns exemplos do que estamos fazendo para melhorar isso.
- Como parte do pipeline de compilação, estamos melhorando o tempo gasto pelo IL Post Processing, que é responsável por modificar os assemblagens .NET compilados depois que seu C# foi compilado. Agora estamos usando um processo persistente para executar o IL Post Processing após a fase de compilação, e isso pode afrouxar algumas centenas de milissegundos.
- Com o compilador Burst sendo usado com mais frequência, estamos melhorando a granularidade da detecção de alterações de código com um algoritmo de hashing transitivo. Isso nos permite identificar qual código Burstable precisamos compilar mais rapidamente. Estamos trabalhando para retirar o compilador Burst do processo, para que ele possa compilar seu código mais rápido graças ao executar em um executável .NET 6.0 separado.
- Também estamos fazendo melhorias na recarga do domínio, melhorando os dados de reflexão construídos atrás da cena sempre que o TypeCache é usado.
- Vamos adicionar testes e validação para melhor acompanhar a regressão de tempo de iteração para pacotes e modelos do Project.
Para a migração para MSBuild, o primeiro passo é desconectar o pipeline de compilação do Editor Unity e movê-lo para um processo separado. Esta é uma operação complicada porque há anos de código legado com milhares de linhas de código C++ e C# que precisamos desencadear para alcançar isso – ao mesmo tempo em que também permanecemos compatíveis para trás. Você não verá mudanças do seu ponto de vista, mas vai preparar o caminho para o MSBuild e simplificar a manutenção.
Nós também vamos melhorar a experiência de depuração do IDE C# com Burst introduzindo um modo que vai automaticamente mudar o depurador para depuração gerenciada quando um ponto de interrupção é definido em um caminho de código executando com Burst. Isso significa que você não terá que remover manualmente o atributo [BurstCompile] no codepath sendo debugado.
O trabalho envolvido na migração para .NET CoreCLR runtime já começou, e é uma jornada muito desafiadora. Para que possamos realizar com sucesso esta migração, gostaríamos de abordar o problema gradualmente e garantir que possamos lançar peças de uma forma que mantenha a estabilidade dos projetos Unity existentes.
Então, estamos planejando entregar essa migração em várias fases:
- Primeiro, forneceremos suporte ao .NET CoreCLR para jogadores autônomos em plataformas de desktop. Você poderá selecionar esse tempo de execução nas configurações do jogador ao lado do backend Mono e IL2CPP existente. Esta primeira fase deve nos ajudar a migrar a parte central do Unity Engine (que é muito menor do que a parte Editor), e espero que resolva boa parte dos desafios técnicos envolvidos nesta migração. Você ainda terá acesso ao runtime .NET através da API .NET Standard 2.1, e pretendemos lançar este novo runtime durante 2023.
- Em segundo lugar, vamos portar o Unity Editor para .NET CoreCLR e remover o suporte para o .NET Mono runtime ao mesmo tempo. Esta segunda fase irá desafiar como vamos recarregar seus scripts no Editor sem usar AppDomains e completar a transição para .NET CoreCLR. Também envolverá a atualização do IL2CPP para suportar as bibliotecas de classe base do repositório dotnet/runtime. Finalmente terá acesso à API completa do .NET 7.x ou 8.0. Esperamos lançar este novo editor em 2024.
O suporte ao .NET Standard 2.1 no Unity 2021 LTS permite-nos começar a modernizar o runtime do Unity de várias maneiras. Atualmente estamos trabalhando em duas melhorias.
Melhorando o modelo de programação asíncrona/espera. Async/Wait é uma abordagem de programação fundamental para escrever código de jogo que deve esperar que uma operação assíncrona seja concluída sem bloquear o mainloop do motor.
Em 2011, antes de async/await ser mainstream no .NET, Unity introduziu operações assíncronas com coroutinas baseadas em iterador, mas esta abordagem é incompatível com async/await e pode ser menos eficiente. Entretanto, o .NET Standard 2.1 tem vindo a melhorar o suporte de async/await em C# e .NET com a introdução de uma gestão mais eficiente de operações de async/await através do ValueTask, e permitindo seu próprio sistema de tarefas através do AsyncMethodBuilder.
Agora, podemos aproveitar essas melhorias, por isso estamos trabalhando para permitir o uso de asíncope/atendimento com operações assíncronas existentes no Unity (como esperar o próximo quadro ou esperar a conclusão do UnityWebRequest). Como um primeiro passo, estamos melhorando o suporte para cancelar tarefas assíncronas pendentes quando um MonoBehavior está sendo destruído ou quando sair do modo Play usando tokens de cancelamento. Também trabalhamos em estreita colaboração com nossos maiores colaboradores da comunidade, como o autor do UniTask, para garantir que eles possam aproveitar essas novas funcionalidades.
Reduzir as atribuições de memória e cópias aproveitando o Span. Como Unity é um motor C++ com uma camada de C# Scripting, há um grande número de dados sendo trocados entre os dois. Isso pode ser ineficiente, pois muitas vezes requer copiar dados de volta e de volta ou atribuir novos objetos gerenciados.
O Span foi introduzido no C# 7.2 para melhorar esses cenários e está disponível por padrão no .NET Standard 2.1. Nos últimos anos, você pode ter ouvido ou lido sobre muitas melhorias significativas no desempenho do .NET Runtime graças a Span (ver detalhes de melhorias em .NET Core 2.1, .NET Core 3.0, .NET 6, .NET 6). Queremos aproveitar o seu uso em Unity, pois isso ajudará a reduzir as atribuições e, consequentemente, a coleta de lixo se pausa, melhorando o desempenho geral de muitas APIs.
Esperamos que todos vocês estejam tão entusiasmados quanto nós com essas mudanças e características.
Deixe-nos saber o que você acha dos nossos planos no foro. Também vamos atualizar regularmente a secção de engenharia do Roadmap da Unity Platform, onde você pode compartilhar suas solicitações de recursos e sugestões de priorização.
Nota do editor: Este artigo foi atualizado pela última vez em fevereiro de 2023.
