Atualizando da versão 4.x para 5.x
Comece por aqui: a chamada principal não mudou.
MySceneManager.TransitionAsync("my-target-scene", "my-loading-scene"); // 4.x e 5.x, idêntico
Se isso é a maior parte do que o seu projeto faz, a migração é pequena. A maioria dos projetos vai lidar com renomeações e argumentos removidos — coisa de localizar e substituir, não de rearquitetura.
Chamadas addressable agora ficam idênticas às não addressable. Uma string simples se resolve sozinha,
então a família *AddressableAsync foi removida em vez de renomeada:
MySceneManager.TransitionAsync("target", "loading"); // Build Settings
MySceneManager.TransitionAsync("target-address", "loading-address"); // Addressables
MySceneManager.TransitionAsync(SceneRef.Address("target"), "loading"); // forçado, e o caminho rápido
Não há camada de compatibilidade — nenhum shim [Obsolete], nenhum método de encaminhamento. Isso segue
o que a 3.0 e a 4.0 fizeram, e significa que cada chamada que precisa mudar produz um simples
erro de compilação exatamente na linha a ser alterada. A 4.x não receberá mais manutenção; a resposta
a um relato de bug na 4.x é atualizar.
Remova completamente a versão anterior antes de importar a 5.0. Essa sempre foi a regra, mas em uma versão major a chance de dar problema é maior.
Principais mudanças
- 64 métodos async públicos viraram 4. Todo tipo de referência, quantidade de cenas e host fica acessível pelas
conversões implícitas de
SceneParameters, em vez de por um método próprio. SceneRefsubstituiILoadSceneInfoe as cinco structs que a implementavam — um único value type sem boxing para nomes, caminhos, endereços, índices de build,AssetReferences eScenes.- Uma
stringsimples se resolve sozinha, consultando primeiro as Build Settings e depois o Addressables. - Toda operação retorna uma
SceneOperationem vez de umaTask<SceneResult>: progresso, cancelamento, fase e eventos por cena vivem todos no handle. CancellationTokeneIProgress<float>saíram da API pública.ISceneBackendsubstituiISceneDataeIAsyncSceneOperation, então a seleção de backend acontece uma vez por operação, em vez de a cada chamada.- Telas de carregamento não precisam mais ser cenas —
LoadingScreentambém cobre prefabs e documentos do UI Toolkit. - Gates da tela de carregamento são retenções, não toggles.
waitForScriptedStart/waitForScriptedEndeStartTransition()/EndTransition()foram removidos; um componente que precisa que a transição espere faz uma retenção noLoadingProgresse a libera quando terminar. LoadingScreenComponenté a base para tudo que vive em uma tela de carregamento. A referência aoLoadingBehavioré opcional e, quando ausente, é buscada nos pais.SceneManagerLogdá ao pacote uma única camada de logging configurável e roteável.- Consultas de cena respondem em vez de lançar exceção.
GetLoadedSceneAteGetLoadedSceneByNameviraramTryGetLoadedSceneAt/TryGetLoadedSceneByName. LoadingFaderfaz o fade em tempo não escalado e limitado, comfadeInTime/fadeOutTimeseparados, eMinimumDisplayTimeé um componente do pacote.- Corrigido:
LoadingProgressnão lança mais exceção quando uma transição é iniciada duas vezes — liberar uma retenção duas vezes é inofensivo.
Tipos removidos e seus substitutos
Esta é a tabela para ler primeiro. Renomeações de método se resolvem com uma tecla no IntelliSense; tipos removidos,
não — LoadSceneInfoName não autocompleta para SceneRef.
ILoadSceneInfo e as structs LoadSceneInfo* → SceneRef
Também cobre LoadSceneInfoType.
// 4.x
ILoadSceneInfo byName = new LoadSceneInfoName("sceneA");
ILoadSceneInfo byPath = new LoadSceneInfoName("Assets/Scenes/sceneA.unity");
ILoadSceneInfo byIndex = new LoadSceneInfoIndex(1);
ILoadSceneInfo byScene = new LoadSceneInfoScene(someScene);
ILoadSceneInfo byAddress = new LoadSceneInfoAddress("sceneA");
ILoadSceneInfo byAsset = new LoadSceneInfoAssetReference(assetReference);
// 5.x
SceneRef byName = "sceneA"; // implícito
SceneRef byPath = "Assets/Scenes/sceneA.unity"; // implícito
SceneRef byIndex = 1; // implícito
SceneRef byScene = someScene; // implícito
SceneRef byAddress = SceneRef.Address("sceneA"); // explícito: força o Addressables
SceneRef byAsset = assetReference; // implícito
Na maior parte do tempo você nem vai escrever SceneRef — as conversões significam que você passa a string,
o índice ou o AssetReference direto para a operação.
ISceneData, SceneData*, SceneDataBuilder, SceneDataUtilities → ISceneBackend
Também cobre IAsyncSceneOperation, AsyncSceneOperationStandard e
AsyncSceneOperationAddressable.
// 4.x — meio implementado por design: cada tipo avisava quando você chamava a metade errada
public interface ISceneData
{
IAsyncSceneOperation AsyncOperation { get; }
void SetSceneReferenceManually(Scene scene); // avisa na implementação addressable
void UpdateSceneReference(); // avisa na implementação padrão
// ...
}
// 5.x — todo método tem significado em toda implementação
public interface ISceneBackend
{
bool CanHandle(SceneRefKind kind);
SceneBackendHandle Load(SceneRef sceneRef);
SceneBackendHandle Unload(SceneBackendHandle handle);
float GetProgress(SceneBackendHandle handle);
bool IsDone(SceneBackendHandle handle);
bool TryResolveScene(SceneBackendHandle handle, out Scene scene);
}
Registre o seu com SceneBackendRegistry.Register(backend); ele tem precedência sobre os
backends embutidos para os tipos que declarar suportar.
WaitTask<T> e TaskExtensions → SceneOperation.ToCoroutine()
// 4.x
yield return MySceneManager.LoadAsync("sceneA").ToWaitTask();
yield return new WaitTask<SceneResult>(MySceneManager.LoadAsync("sceneA"));
// 5.x
yield return MySceneManager.LoadAsync("sceneA").ToCoroutine();
SceneManagerExtensions → removido
As 698 linhas de métodos de extensão existiam para enumerar cada combinação de operação, quantidade de cenas e
tipo de referência. As conversões implícitas de SceneParameters substituem todos eles; veja a tabela de métodos
abaixo.
waitForScriptedStart / waitForScriptedEnd e StartTransition() / EndTransition() → retenções
Na 4.x, uma tela de carregamento que animava a entrada ou a saída marcava dois toggles no LoadingBehavior e
chamava dois gatilhos no seu LoadingProgress — e se dois componentes quisessem controlar o gate da mesma
transição, o primeiro a chamar EndTransition() liberava para os dois. Na 5.x os gates ficam
abertos a menos que algo os retenha: cada participante faz a sua própria retenção e o gate abre
quando o último deles libera.
// 4.x — waitForScriptedStart e waitForScriptedEnd marcados no Inspector
void Awake()
{
_loadingBehavior.Progress.LoadingCompleted += PlayOut;
PlayIn();
}
void OnPlayInFinished() => _loadingBehavior.Progress.StartTransition();
void OnPlayOutFinished() => _loadingBehavior.Progress.EndTransition();
// 5.x — nada para marcar; as retenções são a declaração de que a transição deve esperar
void Awake()
{
_loadingBehavior.Progress.HoldShow(this);
_loadingBehavior.Progress.HoldHide(this);
_loadingBehavior.Progress.LoadingCompleted += PlayOut;
PlayIn();
}
void OnPlayInFinished() => _loadingBehavior.Progress.ReleaseShow(this);
void OnPlayOutFinished() => _loadingBehavior.Progress.ReleaseHide(this);
Faça as retenções no Awake ou no OnEnable, antes de a transição ler os gates. Um novo
par HoldCompletion / ReleaseCompletion atrasa o próprio sinal de LoadingCompleted, que é exatamente o que
um tempo mínimo de exibição precisa. Veja Gates e retenções.
O LoadingFader agora faz suas próprias retenções, então uma cena que só usava ele funciona sem nenhuma mudança
além dos toggles que desaparecem do Inspector.
LoadingProgress.TransitionInTask / TransitionOutTask → WaitForShowAsync() / WaitForHideAsync()
Esses eram campos públicos TaskCompletionSource<bool>, então qualquer consumidor podia completá-los e
dessincronizar a transição. Se você os lia para descobrir quando uma transição terminava uma
fase, use SceneOperation.StateChanged no lugar — veja Observando uma transição.
// 4.x
await loadingBehavior.Progress.TransitionInTask.Task;
// 5.x
await loadingBehavior.Progress.WaitForShowAsync();
bool shown = loadingBehavior.Progress.IsShown;
Campo loadingBehavior dos componentes de feedback → LoadingScreenComponent.LoadingBehavior
LoadingFader, LoadingFeedbackSlider, LoadingFeedbackText e LoadingFeedbackTextMeshPro
agora estendem LoadingScreenComponent. O campo público loadingBehavior deles virou uma propriedade
LoadingBehavior, mantida serializada com o nome antigo para que cenas existentes preservem suas ligações — e ela é
opcional, resolvida a partir do mesmo objeto ou do pai mais próximo quando deixada vazia.
// 4.x
slider.loadingBehavior = behavior;
// 5.x — ou deixe vazio e coloque o LoadingBehavior em um pai
slider.LoadingBehavior = behavior;
Se você escreveu seu próprio feedback em cima de LoadingBehavior.Progress, estenda LoadingScreenComponent
no lugar e mova a inscrição para o OnBound:
// 4.x
public class LoadingFeedbackImageFill : MonoBehaviour
{
public LoadingBehavior loadingBehavior;
void Start() => loadingBehavior.Progress.Progressed += p => _image.fillAmount = p;
}
// 5.x
public class LoadingFeedbackImageFill : LoadingScreenComponent
{
protected override void OnBound() => Progress.Progressed += p => _image.fillAmount = p;
}
GetLoadedSceneAt / GetLoadedSceneByName → TryGetLoadedSceneAt / TryGetLoadedSceneByName
Os dois lançavam exceção quando nada correspondia, então "essa cena está carregada?" só podia ser
perguntado por meio de uma exceção. As formas Try respondem, e a versão por índice checa os limites
explicitamente — LoadedSceneCount muda enquanto outros carregamentos e descarregamentos rodam, então
é seguro percorrê-la.
// 4.x
try { var hud = sceneManager.GetLoadedSceneByName("HUD"); }
catch (ArgumentException) { /* não carregada */ }
// 5.x
if (sceneManager.TryGetLoadedSceneByName("HUD", out Scene hud))
hud.GetRootGameObjects();
TryGetLoadedSceneByName enxerga cenas que terminaram de carregar, então não serve de proteção
contra iniciar um segundo carregamento da mesma cena. Para isso, guarde a SceneOperation que o
primeiro LoadAsync retornou.
LoadingFader.fadeTime → fadeInTime / fadeOutTime
O único fadeTime virou dois campos. Telas existentes migram sozinhas: o valor serializado cai em
fadeInTime, então o fade para o qual elas foram ajustadas mantém o tempo, e fadeOutTime começa
no padrão de um segundo.
Os fades também rodam agora em tempo não escalado e limitado. Uma transição iniciada a partir de
um jogo pausado não trava mais em timeScale = 0, e um único frame longo — a cena sendo ativada —
avança um fade no máximo maxFrameStep (1/30 s por padrão), em vez de consumi-lo antes que qualquer
coisa seja desenhada.
MinimumDisplayTime saiu do exemplo Loading Scene Examples e entrou no pacote, em
MyGameDevTools.SceneLoading, então um jogo pode depender dele sem copiar o arquivo. Se você tinha
copiado a versão do exemplo, apague a sua cópia — as duas têm o mesmo nome. O campo _seconds virou
um seconds público; valores serializados são preservados.
Todo método da 4.x e seu equivalente na 5.x
Cada grupo começa pelo caso que não muda.
Load
| 4.x | 5.x |
|---|---|
LoadAsync(sceneParameters, progress, token) | LoadAsync(sceneParameters) + op.Progressed / op.CancelWith(token) |
LoadAsync(string sceneName, bool setActive, ...) | LoadAsync(sceneName) — ou LoadAsync(new SceneParameters(sceneName, setActive: true)) |
LoadAsync(string[] sceneNames, int setIndexActive, ...) | LoadAsync(sceneNames) — ou LoadAsync(new SceneParameters(sceneNames, setIndexActive)) |
LoadAsync(int buildIndex, bool setActive, ...) | LoadAsync(buildIndex) — ou LoadAsync(new SceneParameters((SceneRef)buildIndex, true)) |
LoadAsync(int[] buildIndices, int setIndexActive, ...) | LoadAsync(buildIndices) — ou LoadAsync(new SceneParameters(buildIndices, setIndexActive)) |
LoadAddressableAsync(string address, bool setActive, ...) | LoadAsync(SceneRef.Address(address)) |
LoadAddressableAsync(string[] addresses, int setIndexActive, ...) | LoadAsync(new SceneParameters(addresses.Select(SceneRef.Address).ToArray(), setIndexActive)) |
LoadAddressableAsync(AssetReference assetReference, bool setActive, ...) | LoadAsync(assetReference) |
LoadAddressableAsync(AssetReference[] assetReferences, int setIndexActive, ...) | LoadAsync(assetReferences) — ou LoadAsync(new SceneParameters(assetReferences, setIndexActive)) |
Um endereço simples só precisa de SceneRef.Address(...) quando o mesmo nome também existe nas suas Build
Settings; caso contrário, LoadAsync(address) resolve para o Addressables por conta própria.
Unload
| 4.x | 5.x |
|---|---|
UnloadAsync(sceneParameters, token) | UnloadAsync(sceneParameters) |
UnloadAsync(string sceneName, token) | UnloadAsync(sceneName) |
UnloadAsync(string[] sceneNames, token) | UnloadAsync(sceneNames) |
UnloadAsync(int buildIndex, token) | UnloadAsync(buildIndex) |
UnloadAsync(int[] buildIndices, token) | UnloadAsync(buildIndices) |
UnloadAsync(Scene scene, token) | UnloadAsync(scene) |
UnloadAsync(Scene[] scenes, token) | UnloadAsync(scenes) |
UnloadAddressableAsync(string address, token) | UnloadAsync(SceneRef.Address(address)) |
UnloadAddressableAsync(string[] addresses, token) | UnloadAsync(addresses.Select(SceneRef.Address).ToArray()) |
UnloadAddressableAsync(AssetReference assetReference, token) | UnloadAsync(assetReference) |
UnloadAddressableAsync(AssetReference[] assetReferences, token) | UnloadAsync(assetReferences) |
Transition
| 4.x | 5.x |
|---|---|
TransitionAsync(sceneParameters, intermediateSceneReference, token) | TransitionAsync(sceneParameters, loadingScreen) |
TransitionAsync(string target, string loading, token) | TransitionAsync(target, loading) — inalterado |
TransitionAsync(string[] targets, string loading, int setIndexActive, token) | TransitionAsync(new SceneParameters(targets, setIndexActive), loading) |
TransitionAsync(int target, int loading, token) | TransitionAsync(target, loading) — inalterado |
TransitionAsync(int[] targets, int loading, int setIndexActive, token) | TransitionAsync(new SceneParameters(targets, setIndexActive), loading) |
TransitionAddressableAsync(string target, string loading, token) | TransitionAsync(SceneRef.Address(target), SceneRef.Address(loading)) |
TransitionAddressableAsync(string[] targets, string loading, int setIndexActive, token) | TransitionAsync(new SceneParameters(targets.Select(SceneRef.Address).ToArray(), setIndexActive), SceneRef.Address(loading)) |
TransitionAddressableAsync(AssetReference target, AssetReference loading, token) | TransitionAsync(target, loading) |
TransitionAddressableAsync(AssetReference[] targets, AssetReference loading, int setIndexActive, token) | TransitionAsync(new SceneParameters(targets, setIndexActive), loading) |
setIndexActive tinha 0 como padrão em toda sobrecarga de transição da 4.x, e uma transição continua
ativando o índice 0 a menos que você diga o contrário — então remover o argumento mantém o mesmo comportamento.
Reload
| 4.x | 5.x |
|---|---|
ReloadActiveSceneAsync(intermediateSceneReference, token) | ReloadActiveSceneAsync(loadingScreen) |
ReloadActiveSceneAsync(string loadingSceneName, token) | ReloadActiveSceneAsync(loadingSceneName) — inalterado |
ReloadActiveSceneAsync(int loadingBuildIndex, token) | ReloadActiveSceneAsync(loadingBuildIndex) — inalterado |
ReloadActiveSceneAddressableAsync(string loadingAddress, token) | ReloadActiveSceneAsync(SceneRef.Address(loadingAddress)) |
ReloadActiveSceneAddressableAsync(AssetReference loadingAssetReference, token) | ReloadActiveSceneAsync(loadingAssetReference) |
Await, progresso e cancelamento
Tudo que antes era um argumento agora é algo que você anexa ao handle.
// 4.x
var progress = new Progress<float>(p => bar.value = p);
var cts = new CancellationTokenSource();
Task<SceneResult> task = MySceneManager.LoadAsync("sceneA", progress: progress, token: cts.Token);
SceneResult result = await task;
// 5.x
SceneOperation op = MySceneManager.LoadAsync("sceneA");
op.Progressed += p => bar.value = p;
SceneResult result = await op;
await op não precisa de Task. Se você precisar de uma para interoperar com bibliotecas de terceiros, op.AsTask() entrega uma.
O cancelamento agora tem um único mecanismo:
op.Cancel(); // interrompe esta operação
op.CancelWith(destroyCancellationToken); // ponte opcional para concorrência estruturada
Operações de cena da Unity não podem ser abortadas — a própria documentação da 4.x dizia isso em todos os 64 métodos, e
o token só cancelava o await. Cancel() para de reportar progresso, pula as
fases restantes e completa a operação em Canceled; o carregamento subjacente ainda termina.
Observando uma transição
Uma SceneOperation reporta em qual fase está, o que antes exigia entrar em um
LoadingBehavior e chamar ContinueWith em um TaskCompletionSource exposto publicamente:
SceneOperation op = MySceneManager.TransitionAsync("target", "loading");
op.StateChanged += o =>
{
if (o.State == SceneOperationState.ScreenOut)
BeginIntroAnimation(); // a tela de carregamento terminou de se esconder
};
await op;
Os estados seguem Pending → Resolving → ScreenIn → Unloading → Loading → Activating → ScreenOut → Completed, e uma operação pula as fases que não fazem sentido para o seu tipo.
Telas de carregamento personalizadas
Uma tela de carregamento não precisa mais ser uma cena. Tudo que funcionava na 4.x continua funcionando — um nome
de cena, caminho, endereço, índice de build, Scene ou AssetReference são todos convertidos em uma tela baseada em cena
— e agora você pode escrever a sua própria:
public class MyScreen : LoadingScreen
{
public override SceneOperationPump.ConditionAwaiter PrepareAsync(LoadingScreenHost host, SceneOperation op)
{
/* instancie dentro do host e então faça BindProgress(...) do LoadingProgress que controla o gate */
return SceneOperationPump.Completed(op);
}
public override void Dispose() { /* desmonte tudo */ base.Dispose(); }
}
await MySceneManager.TransitionAsync("target", new MyScreen());
PrepareAsync é o único membro que uma tela precisa implementar, além de Dispose se ela construiu algo.
Exibir, esconder e reportar progresso são conduzidos pelo LoadingProgress ao qual a tela se vincula — um encontrado em um
LoadingBehavior, ou um que ela mesma cria — então toda tela controla o gate do mesmo jeito.
LoadingScreenHost é uma cena de propriedade do pacote que existe durante uma transição, então uma
tela que instancia algo tem onde colocá-lo sem que ele se perca quando a cena de saída for
descarregada. Ela também substitui a temp-transition-scene interna da 4.x.
O exemplo Loading Scene Examples inclui PrefabLoadingScreen
e UIDocumentLoadingScreen como implementações de referência para copiar.
As cenas Loading_Fade e Loading_Custom e os scripts SceneTransitionTrigger,
AnimatedTrigger e LoadingFeedbackImageFill do exemplo da 4.x foram removidos. Se você copiou algum deles para o
seu projeto, saiba que eles foram escritos em cima dos toggles e gatilhos removidos — reimporte o exemplo
e parta dos scripts da 5.x.
Resolução de strings e sua precedência
Uma string simples é resolvida quando a operação começa:
- Build Settings, por nome ou caminho. Uma única consulta em dicionário, síncrona — e o caso comum.
- Addressables, se as Build Settings não a tiverem e o Addressables estiver instalado. Isso precisa do catálogo, então é assíncrono — o primeiro carregamento addressable por string paga a latência de inicialização do catálogo, e os carregamentos seguintes, de qualquer chave, vêm do cache.
- Nenhum dos dois → uma exceção que cita os dois lugares onde procuramos.
As Build Settings vencem. Se Level1 existe nos dois, LoadAsync("Level1") carrega a versão das Build
Settings, e SceneRef.Address("Level1") é a forma de sobrescrever isso.
Adicionar uma cena às Build Settings mais tarde pode mudar uma string do backend addressable para o
padrão, sem nenhuma alteração no código. Uma chave que corresponde aos dois é reportada no nível Warning, e toda
primeira resolução é registrada em Verbose, então o problema é diagnosticável, não misterioso.
Logging
O pacote agora tem uma única camada de logging em vez de nove chamadas Debug.LogWarning espalhadas.
SceneManagerLog.Level = SceneLogLevel.Verbose; // Off | Error | Warning | Info | Verbose
SceneManagerLog.Handler = myLogHandler; // redirecione para um console dentro do jogo ou para analytics
O padrão é Warning em builds de desenvolvimento e Error em release, e pode ser alterado em tempo de execução,
então dá para elevar o nível em um build publicado para diagnosticar um problema em produção. Defina MSM_DISABLE_LOGGING para remover
a camada por completo.
Verbose é onde a camada de vinculação de cenas narra o que está fazendo — qual referência resolveu para o quê e
qual cena carregada foi vinculada a qual referência. Historicamente, essa é a parte mais delicada do
pacote, então vale a pena ligar quando alguma vinculação sai errada.
Uma nota sobre progresso
Progresso significa algo ligeiramente diferente em cada backend, e sempre foi assim. O progresso do Addressables abrange download, carregamento e ativação; o caminho padrão cobre apenas o carregamento. Um grupo que mistura os dois, portanto, avança de forma desigual. Isso é documentado, não corrigido — reescalar um para bater com o outro seria inventar um número que nenhum dos dois backends reporta.