Ignorando a recarga do domínio: iteração mais rápida usando o recurso MethodReload.

Oct 22, 2026
Programming and DevOps
MethodReload

Esta página da Web foi automaticamente traduzida para sua conveniência. Não podemos garantir a precisão ou a confiabilidade do conteúdo traduzido. Se tiver dúvidas sobre a precisão do conteúdo traduzido, consulte a versão oficial em inglês da página da Web.

Todos nós já passamos por isso: você ajusta algum código de jogabilidade e depois espera o Editor completar um recarregamento de domínio. O processo pode diminuir seu tempo de iteração e realmente drenar sua criatividade. Sem mencionar o tempo extra gasto iterando em um dispositivo, onde cada ciclo requer uma compilação completa.

Nossa resposta para isso é MethodReload. Nós demonstramos no palco na Unite Seoul e agora está disponível como um pacote experimental. Neste post, abordaremos como usar o MethodReload, quais são suas limitações e como ele se compara às ferramentas de recarregamento a quente que você pode já ter no seu projeto.

Por que estamos presos a recarregar

Um pouco de história ajuda aqui, porque a razão pela qual o recarregamento de domínio existe é também a razão pela qual é difícil se livrar dele.

O Modo de Jogo foi construído sobre os AppDomains do Mono, onde as alterações de código acionavam um processo familiar: derrubar o domínio, carregar os novos assemblies, re-JIT e então retomar no mesmo processo. Por anos isso foi um superpoder genuíno, e é uma grande parte do motivo pelo qual iterar no Unity parecia rápido em comparação com construir um jogador.

É também um instrumento grosseiro, o que significa que durante o recarregamento tudo desaparece e tudo volta, quer você tenha mudado um método ou cem. Além disso, o IL2CPP não suporta recarregamentos de domínio de forma alguma, então toda alteração requer a reconstrução de todo o jogo.

Você pode estar se perguntando: "o CoreCLR vai consertar isso?" e a resposta é: Não magicamente. Embora o CoreCLR elimine domain reloads, ele ainda precisa recarregar assemblies, e o MethodReload pode ajudar nisso também.

Como usar MethodReload

Como mencionado acima, MethodReload está disponível agora e é enviado em com.unity.pipeline, atualmente na 0.7.0-exp.1.

O processo de usar o MethodReload é bastante simples. Apenas marque um ou mais métodos com o atributo [MethodReload], inicie o observador, entre no Modo Play ou anexe um jogador de desenvolvimento e, em seguida, edite o corpo dos seus métodos. A instância em execução pega o novo corpo em sua próxima chamada.

O MethodReload não substitui completamente os recarregamentos de domínio, no entanto. Mudanças maiores ainda passam pelo compilador usual. Mas para os pequenos ajustes que compõem a maior parte de um ciclo de iteração, isso transforma uma longa espera em uma questão de segundos, no Editor e no dispositivo.

Experimente

A maneira mais rápida de entender o MethodReload é executá-lo, então, se você quiser testar isso por conta própria, aqui estão alguns passos para começar. Nota: como este é um recurso experimental, o processo pode mudar no futuro e ser diferente do que é descrito aqui

1. Instale o pacote (Unity 6.0+ necessário). Você pode instalá-lo através do Unity CLI ou usar o Gerenciador de Pacotes para adicionar o pacote Pipeline manualmente usando "Instalar pacote por nome técnico" e esta string: com.unity.pipeline@0.7.0-exp.1.

2. Marque os métodos sobre os quais você itera. O atributo tem que estar lá antes de você entrar no Modo Play ou construir um jogador, porque a verificação de despacho é tecida no tempo de compilação. Métodos marcados devem ser public (instância ou estático) e retornar void ou IEnumerator; qualquer outra coisa é ignorada no tempo de compilação com um aviso.

using Unity.Pipeline.MethodReload;
using UnityEngine;

public class Spinner : MonoBehaviour
{
    public float rotationSpeed = 90f;

    [MethodReload]
    public void Update()
    {
        transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
    }
}

3. Iniciar o observador. Para que o seu MethodReload veja suas alterações ao vivo, você precisará iniciar o Watcher do MethodReload. Faça isso indo em Configurações do Projeto → Pipeline → Editor, e então na seção “Method Reload Interpreter Watch”, clique em Iniciar Monitoramento.

Configurações do projeto

Enquanto o observador cobre toda a árvore de Assets, um .cs salvo só compila se contiver um atributo MethodReload, então salvamentos não relacionados não custam nada.

4. Se estiver testando no editor: entre no Modo de Jogo. Uma vez em execução, qualquer alteração de código marcada será compilada e injetada no local. Assim que você sair do modo de jogo, suas alterações serão compiladas e passarão pelo recarregamento de domínio usual.

5. Se testando no dispositivo: conecte um dispositivo. Construa um jogador de desenvolvimento com o driver de pipeline de tempo de execução ativado. Conecte o dispositivo com o Autoconnect Profiler, ou anexe através da janela Profiler ou Console. A partir de então, cada salvamento compila no Editor usando as definições do pré-processador do jogador e envia para todos os jogadores conectados. Nenhuma reinicialização é necessária em nenhuma direção.

Dois detalhes do fluxo de trabalho que vale a pena saber desde o início:

  • A atualização automática está desligada enquanto o observador está em execução, então um salvamento recarrega em vez de acionar um recarregamento de domínio; outros ativos não serão importados até que você pressione Ctrl/Cmd+R, e Parar de Observar restaura suas configurações.
  • O relógio sobrevive aos recarregamentos de domínio: ele reaplica edições que um recarregamento do Modo de Jogo apagou e reenvia para um jogador que se reconecta, já que um jogador reiniciado inicia o código compilado original.

E quanto a usar a CLI?

Se você prefere trabalhar em um terminal, quer trabalhar através de um agente ou quer enviar todos os métodos marcados em vez de apenas o que mudou desde que o monitoramento começou, a mesma funcionalidade é exposta como comandos CLI do Unity:

# Reload in the Editor through the interpreter
unity command reload_file_editor_interpreter "Assets/Scripts/Spinner.cs"

# Compile in the Editor, push to connected players (folders work too)
unity command reload_file_player_interpreter "Assets/Scripts" --player 2

Limitações

Não há como contornar o recarregamento de domínio sem compromissos, e as limitações do MethodReload estão em três lugares: que tipos de mudanças ele pode aplicar, qual C# ele pode executar e quão rápido ele executa.

Apenas os corpos de métodos marcados mudam. Um recarregamento nunca toca locais de chamada, campos ou layout de tipo. Sem novos campos, sem novas classes. Essa restrição é o que o torna seguro para aplicar a uma instância ativa, e é em torno dela que você planeja: decida quais métodos você irá iterar e marque-os. Um reload pode declarar um novo método e chamá-lo a partir de um corpo recarregado, mas esse método não pode ser um ponto de entrada de reload, porque nada compilado sabe chamá-lo até o seu próximo compilamento real.

Ele executa um subconjunto de C#. O interpretador é construído manualmente e tem que ser pequeno o suficiente para caber dentro de um jogador, então ele não implementa todo o C#. Dentro de um corpo [MethodReload] você não pode usar:

  • await e Task, try/catch/finally, lock, sintaxe de consulta LINQ, checked
  • decimal, dinâmico, tipos de reflexão, tipos de threading
  • parâmetros ref, out, e in, ref retornos e locais, arrays multidimensionais (jagged é aceitável), métodos genéricos

O que faz cobre a maior parte do código de jogabilidade: primitivos, chamadas de método, loops, delegados, interpolação de string, corrotina yield, Collections. Um analisador Roslyn vem com o pacote e sinaliza violações na sua IDE, apenas em corpos marcados, para que você descubra enquanto digita em vez de depois de um push.

No dispositivo, o código interpretado só pode chamar o que o jogador expõe. O IL2CPP remove código sem referências, então um corpo recarregado que chama um membro do UnityEngine ou BCL que seu build nunca usou não tem nada para chamar. No Editor, membros ausentes se vinculam sob demanda. Em um dispositivo, as verificações de push verificam na frente e listam membros ausentes como avisos; se o código chegar a um de qualquer maneira, ele lança uma exceção. Regra geral: mude o que um método faz, não o conjunto de APIs que ele depende.

Código interpretado é mais lento que código compilado. Essa é a natureza de um interpretador, seja a comparação IL2CPP pré-compilado ou Mono e CoreCLR apenas em tempo de execução. Isso afeta apenas métodos marcados em builds de desenvolvimento. Em builds de lançamento não há prólogo nem interpretador; um método marcado simplesmente executa seu corpo compilado. Em última análise, cabe a você decidir se o impacto no desempenho durante a iteração vale a velocidade e a conveniência aprimoradas da iteração.

Corrotinas capturam mudanças na criação. Um método [MethodReload] IEnumerator usa o novo corpo na próxima vez que StartCoroutine for executado. Corrotinas já em execução mantêm o corpo com o qual começaram. Emparelhe-o com [OnMethodReloaded], um método de instância sem parâmetros que dispara em cada instância ativa após um recarregamento cair em seu tipo, e você tem um OnEnable livre de recarregamento de domínio para reiniciar loops de longa duração:

[OnMethodReloaded]
void OnMethodReloaded()
{
    StopAllCoroutines();
    StartCoroutine(MainLoop());
}

Como funciona internamente

É a combinação de duas ideias: a primeira é que compilar C# produz bytecode IL, e que IL que permanece dentro de um subconjunto modesto pode ser executado por um interpretador em vez de ser compilado. Esse interpretador é C# comum, então o IL2CPP o compila junto com o resto do seu jogo, o que permite que ele rode no dispositivo.

A segunda ideia é entrar nisso. Todo método [MethodReload] tem um caminho conectado ao interpretador. A ramificação para o interpretador é feita tecendo um prólogo nos métodos marcados em tempo de compilação. Descompilado, parece assim:

[MethodReload]
public void TakeDamage(int amount)
{
    // weaved during compilation
    if (MethodReloadRegistry.HasOverride("Player.TakeDamage") &&
        MethodReloadRegistry.TryInvokeMethodReload(
            "Player.TakeDamage", this, amount))
    {
        return;
    }

    // initial code
    hp -= amount;
}

A verificação tecida pergunta ao registro se existe uma substituição para este método. O observador e a CLI preenchem esse registro com IL recém-compilado. Se houver uma entrada, o interpretador a executa e o corpo original é ignorado. Caso contrário, o código compilado executa normalmente.

Como isto se compara

Boas ferramentas já existem para iteração em C# sem recarregamento de domínio, e preferimos ser precisos sobre onde cada uma se aplica do que fingir que não se aplicam.

Pacotes da Asset Store métodos de patch em um runtime Mono em execução. Isso lhes dá uma excelente experiência de Editor, C# completo no corpo recarregado, sem etapa de marcação e, no caso do Hot Reload, um modo on-device para builds do player Mono. Todas as vantagens reais. Também significa que eles não trabalham com um jogador IL2CPP porque não há tempo de execução para aplicar patches.

.NET Hot Reload, o recurso no Visual Studio e Rider para aplicações .NET, aplica edições através do caminho de edição e continuação do tempo de execução. É construído para CoreCLR e não visa os runtimes Mono ou IL2CPP do Unity.

O MethodReload fica em um lugar diferente com seu backend de interpretador cobrindo a linguagem de trading para alcance: um subconjunto de C#, mas rodando na mesma compilação de desenvolvimento IL2CPP que você de outra forma estaria reimplantando em um telefone ou kit de desenvolvimento de console, pelo cabo que você já tem conectado, sem servidor extra no jogador e sem porta para abrir.

Versão honesta: se sua dor de iteração está no Editor, as ferramentas existentes são maduras e você deve continuar usando o que funciona para você. Se sua dor de iteração vive em um dispositivo, e especialmente no IL2CPP, esta é a primeira opção que vai para lá.

Dicas finais

Se você está configurando um fluxo de trabalho para um projeto agora:

  • Marque seus pontos quentes. [MethodReload] não custa nada em builds de lançamento, então marque os métodos Update, de ajuste e de sensação do jogo que você sabe que irá mexer no dispositivo.
  • Deixe o analisador mantê-lo dentro do subconjunto. Se um método precisar de try/catch ou await, mova essa parte para um helper sem tags e recarregue a lógica ao redor. O analisador avisará se você estiver fazendo algo errado.
  • Tente no Editor primeiro. O observador, ou reload_file_editor_interpreter a partir da CLI, executa sua edição através do mesmo interpretador que roda no dispositivo. Se funcionar lá, funcionará no hardware, então você pega problemas de subconjunto e de API ausente sem esperar por uma compilação.

O que vem a seguir

A referência de comandos, a página de configuração de tempo de execução e a lista completa de restrições estão em com.unity.pipeline docs.

Incentivamos você a experimentar isso em seus projetos e nos diga como corre. Feedback é muito apreciado e os pacotes experimentais melhoram mais rápido quando as pessoas trabalham neles.

Recarregamento de domínio não está acontecendo para as mudanças que exigem isso. Para as mudanças que não, você pode parar de esperar.