Um jogo precisa ser divertido. Uma aplicação precisa estar certa

Apostila de Unity para Aplicações: além dos jogos

Boa parte do Unity que roda hoje não é jogo: é o gêmeo digital de uma fábrica, o configurador de um carro, o simulador de treino de uma empresa de energia, a visualização de um edifício antes de ele existir. Esse tipo de projeto usa o mesmo motor, mas tem outras regras — sessões de oito horas, dados vindos de sistemas reais, modelos CAD com milhões de polígonos, interfaces densas, e um cliente que mede sucesso por decisão tomada, não por diversão. Esta apostila trata dessas regras.

10 módulosArquitetura · DI · UI ToolkitREST · MQTT · AwaitableCAD/BIM · LOD · AddressablesRenderização sob demandaUnity as a Library · XRExercícios com gabarito
📍 Onde esta apostila se encaixa

Pressupõe o básico do editor (GameObject, componente, prefab, cena). Se ainda não tem isso, comece por Unity Essencial e, para a trilha de jogos, Unity do zero ao mercado. Para C# em si, a apostila de C#. Publicar no navegador está em Serious Games na Web. As versões citadas são as do Unity 6, a série atual à data desta apostila.

MÓDULO 01 · BÁSICO

Unity fora dos jogos: onde e por quê

Objetivo: saber em que tipos de aplicação o Unity é a escolha certa, em quais é um exagero caro, e o que muda em relação a fazer um jogo.

1.1 Os territórios reais

TerritórioO que se constróiPor que um motor de jogo
Gêmeo digital (digital twin)Réplica 3D de uma fábrica, linha, edifício ou rede, alimentada por dados reaisRenderizar muitos objetos em tempo real e reagir a dados a cada segundo
Visualização AEC (arquitetura, engenharia, construção)Passeios por projetos, revisão de BIM, estudos de insolaçãoIluminação de qualidade e navegação livre sem pré-renderizar
Configuradores de produtoEscolher cor, acabamento e opcionais de carro, móvel, máquinaMateriais realistas trocados instantaneamente, em várias plataformas
Treino e simulaçãoProcedimentos de manutenção, segurança, operação de equipamentosFísica, interação e cenários repetíveis sem risco real
AR/VR corporativoInstruções sobrepostas ao equipamento, revisão de projeto em escala 1:1O ecossistema de XR mais maduro (OpenXR, XR Interaction Toolkit)
Produção virtual e broadcastCenários virtuais em tempo real, grafismo de TVRenderização em tempo real sincronizada com câmera

1.2 Quando não usar Unity

⚠️ O erro mais caro do módulo

Se a aplicação é essencialmente formulários, listas e texto com um visualizador 3D pequeno num canto, Unity é a ferramenta errada: você paga o tamanho do runtime, o arranque lento, uma acessibilidade mais pobre que a nativa e uma UI mais difícil de fazer do que em web ou nativo. Nesses casos, faça a app em web/nativo e embuta o 3D (Three.js, ou Unity como biblioteca — módulo 7). Regra prática: Unity quando o 3D é o produto; outra coisa quando o 3D é um acessório.

1.3 O que muda em relação a um jogo

DimensãoJogoAplicação
SessãoMinutos a poucas horasUm turno inteiro; app aberta o dia todo em quiosque ou sala de controle
Fonte da verdadeO próprio jogoSistemas externos (ERP, SCADA, BIM, APIs) — o Unity é só uma vista
ConteúdoModelado para o motorVem de CAD/BIM, pesado e cheio de detalhe inútil (módulo 5)
Frame rateMáximo possível, sempreAlto quando há interação, quase zero quando parado (módulo 6)
InterfaceHUD mínimoPainéis densos, tabelas, filtros, localização (módulo 3)
Critério de sucessoDiversão, retençãoCorreção dos dados, decisão tomada, tempo de treino reduzido

1.4 Licenças, em uma frase cada

A taxa por instalação (Runtime Fee) anunciada em 2023 foi cancelada em setembro de 2024; o modelo voltou a ser por assento. Isto importa porque muito conteúdo online ainda discute a taxa como se estivesse em vigor.

💼 Mercado de trabalho

Vagas de "Unity developer" em indústria, AEC, automotivo e saúde raramente pedem game design — pedem C# de qualidade de software empresarial, integração com APIs e dados, e otimização de conteúdo pesado. Quem vem de jogos e mostra um projeto com dados reais e arquitetura limpa se destaca muito mais do que com o quinto jogo de plataforma.

✏️ Exercício 1 — Unity ou não?

Para cada pedido, decida se Unity é boa escolha: (a) app de inspeção com checklist de 40 perguntas e uma foto; (b) sala de controle que mostra 3 000 sensores de uma refinaria sobre o modelo 3D; (c) configurador de cozinha planejada com troca de acabamentos em tempo real; (d) catálogo de produtos com um botão "ver em 3D" num dos itens.

Gabarito: (a) Não — formulário é trabalho de web/nativo. (b) Sim — o 3D e os dados em tempo real são o produto. (c) Sim — materiais realistas trocados ao vivo são o motivo de usar um motor. (d) Não para a app inteira; o visualizador pode ser um componente web (glTF + Three.js ou <model-viewer>) ou, se já existir, Unity como biblioteca.

MÓDULO 02 · BÁSICO

Arquitetura de aplicação

Objetivo: sair do padrão "um MonoBehaviour faz tudo", separar regra de negócio de apresentação e montar uma estrutura que aguente dois anos de manutenção.

2.1 O problema do MonoBehaviour-para-tudo

Tutoriais ensinam a colocar lógica, estado e acesso a dados no mesmo componente pendurado num GameObject. Num jogo pequeno funciona. Numa aplicação, o resultado é previsível: regra de negócio que só roda com a cena aberta, impossível de testar sem Play Mode, FindObjectOfType espalhado, e uma mudança na API do servidor que obriga a editar vinte componentes visuais.

2.2 Três camadas

CamadaContémDepende do Unity?
DomínioEntidades e regras: "um alarme é crítico se a temperatura passar de X por mais de Y segundos"Não. C# puro, testável fora do editor
Aplicação / serviçosCasos de uso, acesso a dados, clientes de API, estado da sessãoPouco (só onde precisa de UnityWebRequest, por exemplo)
ApresentaçãoMonoBehaviours, UI, materiais, animações — mostrar o estado e capturar o inputSim

A regra que sustenta tudo: as dependências apontam para dentro. A apresentação conhece o domínio; o domínio nunca conhece a apresentação. Na prática, isso se impõe com Assembly Definitions (.asmdef): um assembly App.Domain sem referência a UnityEngine (opção "No Engine References") torna fisicamente impossível importar coisa do motor ali dentro — e ainda acelera a compilação.

2.3 MVP / MVVM com componentes finos

// Domínio — C# puro, sem using UnityEngine
public sealed class Sensor
{
    public string Id { get; }
    public double Valor { get; private set; }
    public double Limite { get; }
    public bool EmAlarme => Valor > Limite;
    public event System.Action<Sensor> Mudou;

    public Sensor(string id, double limite) { Id = id; Limite = limite; }
    public void Atualizar(double v) { Valor = v; Mudou?.Invoke(this); }
}

// Apresentação — só traduz estado em visual
public sealed class SensorView : MonoBehaviour
{
    [SerializeField] Renderer indicador;
    [SerializeField] Color normal = Color.green, alarme = Color.red;
    Sensor _sensor;

    public void Ligar(Sensor s) { _sensor = s; s.Mudou += Redesenhar; Redesenhar(s); }
    void OnDestroy() { if (_sensor != null) _sensor.Mudou -= Redesenhar; }
    void Redesenhar(Sensor s) => indicador.material.color = s.EmAlarme ? alarme : normal;
}
⚠️ Dois detalhes que viram bug em produção

(1) Sempre desinscrever eventos no OnDestroy: um evento de domínio segurando um componente destruído vaza memória e lança MissingReferenceException horas depois — em app de sessão longa, isso aparece. (2) renderer.material cria uma cópia do material por objeto; com 3 000 sensores são 3 000 materiais e o batching morre. Para trocar só a cor, use MaterialPropertyBlock ou propriedades por instância (módulo 6).

2.4 Composição e injeção de dependência

Alguém precisa criar o cliente de API, o repositório e os serviços e entregá-los a quem precisa. Em vez de singletons estáticos, use um composition root: um único ponto que monta o grafo de objetos no arranque. Bibliotecas de DI feitas para Unity, como VContainer (leve, rápida) ou Zenject/Extenject (mais antiga, mais recursos), fazem isso e injetam nas MonoBehaviours da cena. Em projetos pequenos, um composition root escrito à mão num único MonoBehaviour de bootstrap é perfeitamente suficiente.

2.5 Estado da aplicação e cenas aditivas

✏️ Exercício 2 — Onde mora cada coisa?

Classifique em domínio, aplicação ou apresentação: (a) regra "manutenção vencida se passaram 180 dias da última"; (b) chamada HTTP que busca o histórico de manutenções; (c) piscar o equipamento em laranja quando a manutenção está vencida; (d) guardar o projeto aberto por último para reabrir no dia seguinte.

Gabarito: (a) Domínio — regra pura, testável sem Unity. (b) Aplicação — serviço de acesso a dados. (c) Apresentação — tradução visual do estado. (d) Aplicação — estado da sessão, persistido por um serviço (que por baixo pode usar PlayerPrefs ou um arquivo em Application.persistentDataPath).

MÓDULO 03 · INTERMEDIÁRIO

Interface: UI Toolkit e uGUI

Objetivo: escolher o sistema de UI certo para painéis densos, ligar a UI aos dados sem código repetitivo, e tratar localização e acessibilidade desde o início.

3.1 Os dois sistemas

uGUI (Canvas)UI Toolkit
ModeloGameObjects com componentes (Image, Text, Button)Árvore de elementos descrita em UXML, estilizada com USS (parecido com HTML/CSS)
Brilha emUI no mundo 3D, UI em XR, efeitos e animações por componenteInterfaces densas de aplicação: painéis, formulários, listas longas, temas
Listas longasPrecisa de virtualização feita à mãoListView/MultiColumnListView virtualizados nativamente
Ligação a dadosCódigo manualData binding de runtime (Unity 6)
MaturidadeEstável há muitos anos, vasta documentaçãoO sistema recomendado para novas UIs de tela; algumas lacunas em UI no espaço 3D

Para aplicações, a escolha típica é UI Toolkit para a interface de tela (painéis, menus, tabelas) e uGUI (ou UI Toolkit com render texture) para rótulos presos a objetos 3D e para XR. Misturar os dois no mesmo projeto é normal.

3.2 UXML + USS, em miniatura

<!-- PainelSensor.uxml -->
<ui:UXML xmlns:ui="UnityEngine.UIElements">
  <ui:VisualElement class="painel">
    <ui:Label name="titulo" class="painel__titulo" />
    <ui:Label name="valor" class="painel__valor" />
    <ui:Button name="reconhecer" text="Reconhecer alarme" />
  </ui:VisualElement>
</ui:UXML>

/* PainelSensor.uss */
.painel { padding: 12px; background-color: rgb(21, 34, 44); border-radius: 8px; }
.painel__valor { font-size: 28px; -unity-font-style: bold; }
.painel--alarme .painel__valor { color: rgb(220, 60, 60); }
var raiz = GetComponent<UIDocument>().rootVisualElement;
var valor = raiz.Q<Label>("valor");
raiz.Q<Button>("reconhecer").clicked += () => servicoAlarmes.Reconhecer(sensor.Id);
// mudar o estado visual trocando classe, não cor no código:
raiz.EnableInClassList("painel--alarme", sensor.EmAlarme);
💡 Estado visual por classe, não por cor no código

Trocar classes USS (EnableInClassList) em vez de definir cores em C# mantém o visual num só lugar, permite temas (claro/escuro, alto contraste) e deixa o designer ajustar a interface sem tocar em código — o mesmo princípio de BEM na web.

3.3 Localização

Aplicações corporativas quase sempre precisam de mais de um idioma. O pacote Localization (com.unity.localization) gerencia tabelas de strings e de assets por idioma, formatação de números e datas por cultura e troca de idioma em tempo de execução. Duas regras: nunca concatenar frases ("Há " + n + " alarmes" quebra em idiomas com outra ordem ou outras regras de plural — use strings com marcadores e as regras de plural do pacote) e reservar espaço: textos em alemão costumam ser bem mais longos que em inglês.

3.4 Acessibilidade

É o ponto fraco do Unity em comparação com web e nativo. Desde o Unity 2023.2 há APIs de acessibilidade para expor uma hierarquia a leitores de tela em Android e iOS, mas o suporte é mais limitado e exige trabalho explícito. Em aplicações de uso obrigatório (treinamento de funcionários, serviço público), trate isso como requisito: contraste, tamanho de texto ajustável, navegação completa por teclado/controle, legendas, e alternativa textual para informação que só existe em cor. Se o requisito legal for forte, é mais um argumento para a UI principal ser web/nativa com Unity embutido (módulo 7).

✏️ Exercício 3 — Escolha o sistema

Uma app de manutenção tem: (a) tabela com 5 000 ordens de serviço, filtrável; (b) etiqueta flutuante sobre cada máquina no 3D; (c) menu de mão em VR. Que sistema de UI para cada?

Gabarito: (a) UI Toolkit, MultiColumnListView virtualizada — só as linhas visíveis existem. (b) uGUI em World Space (ou sprites/texto 3D), já que precisa acompanhar o objeto em perspectiva. (c) uGUI com o XR Interaction Toolkit, que tem suporte pronto a raios e toque em UI de Canvas.

MÓDULO 04 · INTERMEDIÁRIO

Dados, rede e tempo real

Objetivo: consumir APIs sem congelar a interface, receber dados de IoT em tempo real, respeitar a regra da thread principal e não guardar segredos onde não se deve.

4.1 A regra da thread principal

Quase toda a API do Unity (Transform, GameObject, materiais, UI) só pode ser tocada na thread principal. Você pode — e deve — fazer parsing de JSON grande, cálculos e I/O noutras threads, mas o resultado tem de voltar à thread principal antes de mexer na cena. No Unity 6, o tipo Awaitable torna isso explícito:

async Awaitable CarregarHistoricoAsync(string url)
{
    using var req = UnityWebRequest.Get(url);
    req.SetRequestHeader("Authorization", "Bearer " + sessao.Token);
    await req.SendWebRequest();                // não bloqueia o frame

    if (req.result != UnityWebRequest.Result.Success)
    { ui.MostrarErro(req.error); return; }

    string json = req.downloadHandler.text;
    await Awaitable.BackgroundThreadAsync();     // sai da thread principal
    var historico = JsonConvert.DeserializeObject<List<Leitura>>(json);

    await Awaitable.MainThreadAsync();           // volta antes de tocar na cena
    grafico.Desenhar(historico);
}

JsonConvert vem do pacote Newtonsoft (com.unity.nuget.newtonsoft-json); o JsonUtility embutido é mais rápido mas não serializa dicionários, propriedades nem tipos polimórficos — para APIs reais, quase sempre falta alguma coisa.

⚠️ Cancelamento

Se o usuário fechar o painel antes de a requisição terminar, o await continua e, quando voltar, o painel já foi destruído. Passe um CancellationToken (o destroyCancellationToken de cada MonoBehaviour existe exatamente para isso) e trate o cancelamento. Em app de sessão longa, requisições órfãs são uma fonte clássica de erros intermitentes.

4.2 Tempo real: escolher o transporte

TransporteQuando usarObservação
Polling RESTDados que mudam a cada minutos; poucos clientesSimples; custa requisições vazias
WebSocketAtualizações frequentes vindas do seu backendUm canal aberto, o servidor empurra as mudanças
MQTTIoT e sensores industriais; muitos produtores, tópicos hierárquicosPadrão de fato em IoT; cliente .NET como MQTTnet. Ver Robótica, IoT e Embarcados
OPC UAIntegração direta com automação industrial (CLPs, SCADA)Normalmente passa por um gateway que traduz para MQTT/REST, em vez de o cliente 3D falar direto com a rede de automação

4.3 Não afogue a cena

Receber 3 000 sensores a 10 Hz são 30 000 mensagens por segundo. Nunca aplique cada mensagem diretamente na cena. Acumule num buffer thread-safe (por exemplo, ConcurrentQueue) na thread de rede, e drene uma vez por frame na thread principal, guardando só o valor mais recente de cada sensor. A cena atualiza no ritmo da tela, não no ritmo da rede.

4.4 Segredos e autenticação

✏️ Exercício 4 — Onde está o bug?

Um desenvolvedor recebe mensagens MQTT num callback e, dentro dele, faz transform.position = novaPosicao. No editor às vezes funciona, às vezes lança exceção, e no build fica pior. O que está errado e como corrigir?

Gabarito: O callback do cliente MQTT roda numa thread de rede, e a API do Transform só pode ser usada na thread principal. Correção: o callback só enfileira a mensagem numa ConcurrentQueue; um Update drena a fila (ficando com o último valor por objeto) e aplica as posições.

MÓDULO 05 · INTERMEDIÁRIO

Conteúdo 3D de engenharia

Objetivo: transformar modelos CAD e BIM — pesados, detalhados e feitos para fabricar, não para renderizar — em conteúdo que roda em tempo real sem perder a informação que importa.

5.1 Por que CAD não entra direto

Um modelo CAD descreve superfícies matemáticas exatas (NURBS) para fabricação. Para renderizar, é preciso tesselar em triângulos — e a tesselação ingênua de uma máquina inteira produz dezenas de milhões de polígonos, inclusive de parafusos internos que ninguém verá. Um BIM de edifício traz, além disso, milhares de objetos separados (cada um virando um draw call) e metadados valiosos que a importação pode perder.

5.2 O pipeline de preparação

EtapaO que fazPor que
ImportarLer STEP, IGES, formatos nativos de CAD, IFC (BIM), ou intermediários (FBX, glTF, USD)Ferramentas como Pixyz (incluída no Unity Industry) leem os formatos de engenharia diretamente
Tesselar com controleEscolher tolerância de corda/ângulo por peçaPeça grande e curva precisa mais triângulos que parafuso
Remover o ocultoApagar peças internas e faces nunca visíveisFrequentemente a maior redução de todas
Decimar e gerar LODVersões mais leves para distânciaLODGroup troca automaticamente conforme o tamanho na tela
Fundir (merge)Juntar peças estáticas com o mesmo materialMenos draw calls — mas perde-se a seleção individual
Preservar metadadosGuardar ID, nome, propriedades de cada peçaÉ o que liga o 3D aos dados (módulo 9)
💡 O conflito central: fundir vs. selecionar

Fundir tudo dá desempenho; manter tudo separado dá interatividade (clicar numa válvula e ver os dados dela). A solução habitual: fundir para renderizar, mas manter um mapa de IDs — por exemplo, um índice de triângulo → ID da peça, ou um ID por vértice gravado num canal de UV — para descobrir qual peça foi clicada sem que ela exista como objeto separado.

5.3 Formatos de intercâmbio

5.4 Conteúdo que não vai no build: Addressables

Uma aplicação que abre projetos diferentes não pode embutir todos no executável. O sistema Addressables empacota conteúdo em bundles carregáveis sob demanda, de disco ou de um servidor, com gestão de dependências e de memória. Para modelos que chegam só em execução (upload do cliente), carregar glTF em runtime é o caminho mais direto. A gestão de memória e a tela de carregamento com Addressables estão detalhadas no capítulo 8 de Serious Games na Web.

✏️ Exercício 5 — Priorize a otimização

Um modelo de fábrica tem 48 milhões de triângulos, 22 000 objetos e 900 materiais. Em que ordem você atacaria, e por quê?

Gabarito: (1) Remover peças ocultas/internas — costuma cortar mais do que qualquer outra etapa e não degrada nada visível. (2) Reduzir materiais (900 → dezenas, por consolidação e atlas), que é pré-requisito para fundir. (3) Fundir objetos estáticos por material, mantendo um mapa de IDs para seleção — derruba os draw calls. (4) Decimar e gerar LODs para o que restar pesado. Medir com o Profiler/Frame Debugger entre cada etapa, porque o gargalo muda.

MÓDULO 06 · AVANÇADO

Performance de aplicação

Objetivo: entender por que performance de aplicação é diferente de performance de jogo — bateria, calor, memória em sessões longas e arranque — e usar as ferramentas certas para cada uma.

6.1 Não renderize o que não mudou

Um jogo redesenha a tela 60 vezes por segundo mesmo com o jogador parado. Uma aplicação aberta o dia todo num tablet, fazendo isso, drena a bateria, aquece o aparelho (que então reduz o clock e fica mais lento) e gasta GPU à toa numa estação de trabalho compartilhada. Duas ferramentas:

// frame rate alvo global
Application.targetFrameRate = 60;

// renderizar só 1 de cada N frames, mantendo o loop (input, lógica) a 60 Hz
using UnityEngine.Rendering;
OnDemandRendering.renderFrameInterval = ocioso ? 12 : 1;   // ~5 fps parado, 60 interagindo

A estratégia: renderização plena durante interação (arrastar, girar, animação, dado chegando) e intervalo alto quando nada muda. Volte a 1 no primeiro evento de input ou mudança de dado.

🧮 Calculadora — quanto a renderização sob demanda poupa
Sempre a toda velocidade—
Sob demanda—
Frames evitados—

Frames renderizados por dia de uso. É uma aproximação do trabalho da GPU, não do consumo exato de energia, que depende do aparelho — mas a proporção é o que decide se vale a pena.

6.2 Muitos objetos iguais

6.3 Memória em sessões longas

Num jogo, um vazamento de 5 MB por minuto pode passar despercebido numa sessão de 30 minutos. Numa app aberta oito horas, são 2,4 GB e um crash no meio do turno. Suspeitos habituais: materiais criados por renderer.material e nunca destruídos, texturas carregadas em runtime sem Destroy, handles de Addressables sem Release, eventos não desinscritos (módulo 2), e alocações de lixo por frame que fazem o garbage collector disparar. O Memory Profiler permite comparar dois snapshots — tirar um no início, usar a app por uma hora, tirar outro e ver o que cresceu é o teste mais valioso desta apostila.

6.4 Arranque

Usuários de aplicação toleram muito menos espera que jogadores. Reduza o que a primeira cena carrega (cena de bootstrap mínima, conteúdo pesado por Addressables depois), evite trabalho pesado em Awake/Start de dezenas de objetos, pré-aqueça shaders (coleções de variantes) para não haver engasgos na primeira interação, e mostre algo útil — o último projeto aberto, por exemplo — enquanto o resto carrega.

✏️ Exercício 6 — Diagnóstico

Um tablet com a app de inspeção fica quente e, depois de 40 minutos, a interface começa a engasgar, mesmo com a cena parada. O Profiler mostra GPU ocupada o tempo todo e o frame rate caindo aos poucos. Hipótese e correção?

Gabarito: A app renderiza a 60 fps mesmo parada; o aparelho aquece, entra em limitação térmica e reduz o clock, e o desempenho cai. Correção: OnDemandRendering.renderFrameInterval alto quando ocioso, voltando a 1 no input; e, se ainda assim estiver pesada durante a interação, reduzir resolução de render/efeitos em mobile.

MÓDULO 07 · AVANÇADO

Plataformas e formas de entrega

Objetivo: conhecer as maneiras de colocar uma aplicação Unity nas mãos do usuário — executável, embutida numa app nativa, no navegador, em XR ou transmitida de um servidor — e o custo de cada uma.

7.1 O mapa de entrega

FormaQuandoCusto principal
Executável desktop (Windows/macOS/Linux)Estações de trabalho, salas de controle, quiosquesInstalação e atualização em máquinas geridas pela TI
App mobileInspeção em campo, vendas, ARLojas, assinatura de código, térmica e bateria (módulo 6)
Unity as a LibraryO 3D é uma tela dentro de uma app nativa maiorIntegração e ciclo de vida entre dois mundos
WebAcesso por link, sem instalaçãoTamanho de download, memória, limites do navegador
XR (Quest, visionOS, HoloLens…)Revisão em escala real, treino imersivo, AR no equipamentoConforto, frame rate rígido, interação própria
Streaming de renderModelos pesados demais para o aparelho do usuárioServidor com GPU por sessão ativa; latência de rede

7.2 Unity as a Library

Desde o Unity 2019.3 é possível exportar o projeto como uma biblioteca e embuti-lo numa app nativa Android (módulo/AAR) ou iOS (framework). A app nativa cuida de login, navegação, formulários e acessibilidade; o Unity aparece numa tela para o 3D. A comunicação nos dois sentidos passa por mensagens — do nativo para o Unity com UnitySendMessage (nome do objeto, nome do método, uma string), e do Unity para o nativo por plugins nativos. Limites importantes: só uma instância do runtime por processo, e descarregar o Unity não devolve toda a memória. É a resposta certa ao "catálogo com um botão ver em 3D" do exercício 1, quando o 3D justifica o motor.

7.3 Web

Um build Web permite abrir a aplicação por um link. Os problemas de tamanho, compressão, servidor e memória são os mesmos dos jogos e estão tratados em Serious Games na Web. Para aplicações, o ponto extra é a integração com a página (a app Unity dentro de um portal web existente, trocando dados com ele), que é assunto de uma apostila própria de Unity para web interativa.

7.4 XR

⚠️ Em XR, frame rate não é qualidade, é saúde

Queda de frame rate em VR causa desconforto e enjoo. O orçamento é rígido (tipicamente 72–90 Hz ou mais, conforme o headset) e o modelo CAD que "roda bem" em desktop quase nunca roda em standalone sem o pipeline do módulo 5 levado mais longe.

7.5 Streaming de render

Quando o modelo é pesado demais para o aparelho, o Unity pode rodar num servidor com GPU e enviar vídeo ao navegador, recebendo o input de volta (o pacote Unity Render Streaming, baseado em WebRTC, é o ponto de partida). Resolve o limite do cliente, mas cria dois novos: custo de uma GPU por usuário ativo e latência sensível à rede. Faz sentido para configuradores premium e revisões de projeto com poucos usuários simultâneos; raramente faz sentido para milhares.

✏️ Exercício 7 — Escolha a entrega

(a) Técnicos em campo, com app de ordens de serviço já existente em Kotlin, querem ver o modelo 3D do equipamento. (b) Diretoria quer revisar o projeto de um edifício de 60 GB num tablet comum. (c) Operadores de uma sala de controle, 8 horas por dia, em PCs geridos pela TI.

Gabarito: (a) Unity as a Library dentro da app Kotlin — não reescrever o que já funciona. (b) Streaming de render (poucos usuários, modelo impossível no tablet) ou um modelo drasticamente simplificado; a escolha depende do custo aceitável de servidor. (c) Executável desktop, distribuído pela TI, com renderização sob demanda e teste de memória de 8 horas.

MÓDULO 08 · AVANÇADO

Qualidade: testes, CI e versionamento

Objetivo: aplicar a um projeto Unity a disciplina de engenharia que o cliente de uma aplicação espera — testes automatizados, builds reproduzíveis e controle de versão sem pesadelos de merge.

8.1 A pirâmide de testes no Unity

NívelOnde rodaO que testa
Testes de domínioFora do motor (NUnit puro, ou EditMode)Regras de negócio — rápidos, a maioria dos testes
EditMode (Unity Test Framework)No editor, sem entrar em PlayServiços, conversões, validação de assets e cenas
PlayModeCom o motor rodandoIntegração: carregar cena, clicar, verificar resultado — mais lentos, poucos

A arquitetura do módulo 2 é o que torna isto possível: se a regra está num MonoBehaviour, só um teste PlayMode (lento e frágil) a alcança. Se está no domínio, um teste de milissegundos resolve.

[Test]
public void Sensor_AcimaDoLimite_EntraEmAlarme()
{
    var s = new Sensor("T-101", limite: 80);
    s.Atualizar(85);
    Assert.IsTrue(s.EmAlarme);
}

8.2 Testes de conteúdo

Em aplicações, muitos bugs não estão no código, estão nos assets: um prefab sem componente obrigatório, um material com shader errado, uma cena sem câmera. Testes EditMode que varrem os assets e verificam regras do projeto (toda peça importada tem ID de metadado, toda textura de UI tem compressão certa) pegam esses erros antes do cliente.

8.3 Builds automatizados

O Unity roda sem interface em modo batch (-batchmode -nographics -executeMethod ...), o que permite builds e testes em CI. O projeto open source GameCI oferece ações prontas para GitHub Actions e imagens Docker com o editor. Um pipeline mínimo: a cada pull request, rodar EditMode + PlayMode; a cada merge na branch principal, gerar os builds de cada plataforma e guardar os artefatos. A licença do Unity precisa ser ativada no runner — planeje isso antes.

8.4 Git sem sofrimento

✏️ Exercício 8 — O pull request quebrado

Depois de um merge, metade dos prefabs aparece com "Missing Prefab" e materiais cor-de-rosa, mas nenhum arquivo foi apagado. O que provavelmente aconteceu?

Gabarito: Arquivos .meta foram perdidos ou regenerados (não commitados por alguém, ou ignorados por um .gitignore errado). O Unity gerou GUIDs novos para os assets, e todas as referências antigas, que apontavam para os GUIDs originais, quebraram. Correção: recuperar os .meta originais do histórico; prevenção: revisar o .gitignore e verificar no CI que todo asset tem o seu .meta.

MÓDULO 09 · MUITO AVANÇADO

Gêmeo digital de ponta a ponta

Objetivo: juntar tudo num caso integrado — ligar um modelo 3D a dados reais, mostrar o presente, rever o passado, e fazer isso sem colocar em risco a rede industrial.

9.1 O que é (e o que não é) um gêmeo digital

Um modelo 3D bonito de uma fábrica não é um gêmeo digital. Passa a ser quando existe uma ligação de dados contínua entre o objeto físico e a representação virtual, de forma que o virtual reflete o estado real (e, nos casos mais maduros, permite simular o que aconteceria com uma mudança antes de aplicá-la). O 3D é a interface; o valor está na ligação.

9.2 A arquitetura em camadas

Fluxo de dados

Equipamento (CLP, sensores) → gateway (OPC UA → MQTT) → broker/plataforma (MQTT, séries temporais, API) → cliente Unity: serviço de dados (thread de rede, fila) → modelo de domínio (estado de cada ativo) → apresentação (cores, rótulos, gráficos).

O ponto que liga o 3D aos dados é o ID do ativo: a mesma chave precisa existir no modelo 3D (preservada na importação — módulo 5), no sistema de dados e no domínio. Sem uma tabela de correspondência confiável, o projeto trava aqui — e em muitos projetos reais, reconciliar a nomenclatura do CAD com a do sistema de manutenção é a tarefa mais demorada de todas.

9.3 Presente: interpolação e dados atrasados

9.4 Passado: histórico e reprodução

Operadores e engenheiros querem voltar no tempo: "o que aconteceu às 3h14 quando a linha parou?". Isso pede consultar a série temporal por janela e reproduzir na mesma cena, com uma linha do tempo. A arquitetura do módulo 2 paga aqui: se a apresentação só lê o estado do domínio, alimentar esse domínio a partir do histórico em vez do tempo real não exige mudar nenhum componente visual.

9.5 Segurança: a rede industrial não é a internet

⚠️ Leitura por padrão, escrita como exceção

A rede de automação (OT) controla equipamentos físicos. O cliente 3D deve, por padrão, só ler, e através de um gateway numa zona de rede separada — nunca falando diretamente com CLPs. Qualquer comando que altere o mundo físico (parar uma bomba, mudar um setpoint) é uma decisão de engenharia de segurança, com autenticação forte, confirmação, registro de auditoria e, em geral, passando pelo sistema de controle existente, não por um atalho no aplicativo de visualização. O mesmo princípio de "camada de segurança" aparece em Physical AI.

Sénior — "O cliente quer um gêmeo digital. Por onde você começa?"

Não pelo 3D. Começa-se pela pergunta de negócio (que decisão o gêmeo vai melhorar? reduzir paradas? treinar operadores?), depois pelo inventário dos dados (que sinais existem, com que frequência, em que sistema, com que nomes), e pela correspondência de IDs entre CAD e dados. Só então se decide o nível de detalhe visual necessário — frequentemente muito menor do que o cliente imaginava. Um piloto numa única linha, com três ou quatro indicadores que alguém usa de fato, vale mais do que a fábrica inteira modelada sem dados.

MÓDULO 10 · CARREIRA

Mercado, portfólio e fontes

Objetivo: entender quem contrata Unity para aplicações, o que avaliam, e montar um portfólio que prove as competências deste tipo de projeto.

10.1 Quem contrata

SetorProjetos típicosCompetência diferencial
Indústria e energiaGêmeos digitais, treino de operação e segurançaDados em tempo real, MQTT/OPC UA, CAD
AECVisualização e revisão de projetos, BIMIFC, iluminação, otimização de modelos grandes
Automotivo e produtosConfiguradores, HMI, marketingMateriais realistas, HDRP/URP, entrega multiplataforma
Saúde e educaçãoSimuladores, anatomia, treino de procedimentosInteração precisa, avaliação, acessibilidade
Agências e estúdios de XRExperiências de marca, eventos, apps ARXR, prazos curtos, performance em mobile

10.2 Perguntas de entrevista

"Como você estruturaria uma app Unity para ser testável?"

Separar domínio (C# puro, assembly sem referência ao motor) de apresentação (MonoBehaviours finos), injetar dependências num composition root, e cobrir o domínio com testes rápidos fora de Play Mode, deixando PlayMode para poucos testes de integração.

"Recebemos um CAD de 40 milhões de triângulos. O que você faz?"

Perguntar para que ele vai ser usado e em que dispositivo; depois remover o oculto, consolidar materiais, fundir estáticos preservando IDs, decimar e gerar LOD, medindo entre cada etapa. E garantir que os metadados necessários para ligar a dados sobrevivem ao processo.

"A app trava depois de horas de uso. Como investiga?"

Suspeita de vazamento de memória: dois snapshots no Memory Profiler com uma hora de uso entre eles, comparar o que cresceu. Suspeitos: materiais instanciados, texturas de runtime, handles de Addressables sem Release, eventos não desinscritos, requisições órfãs sem cancelamento.

10.3 Projetos de portfólio

  1. Mini gêmeo digital: um modelo simples (uma sala, uma máquina) ligado a um broker MQTT com um simulador de sensores em Python; estados normal/alarme/desconhecido, histórico com reprodução. É o projeto que mais conversa com as vagas industriais.
  2. Pipeline de CAD: pegar um modelo aberto de engenharia, documentar antes/depois (triângulos, draw calls, tempo de frame) e a seleção de peças preservada após a fusão.
  3. Configurador de produto: troca de materiais e opcionais, UI Toolkit com tema, localização em dois idiomas, build desktop e mobile.
  4. Unity as a Library: uma app nativa simples com uma tela Unity embutida, trocando mensagens nos dois sentidos.
  5. Projeto com engenharia visível: qualquer um dos anteriores com testes, CI (GameCI) e README explicando a arquitetura — o que diferencia um desenvolvedor de aplicações de alguém que só sabe o editor.

10.4 Fontes para continuar

🏁 Síntese final da apostila

Quatro ideias sustentam Unity para aplicações: (1) o 3D é a interface, não a fonte da verdade — os dados vivem noutros sistemas e o Unity os mostra; (2) arquitetura antes de cena — domínio em C# puro, apresentação fina, dependências para dentro, porque é isso que torna a app testável e mantível; (3) conteúdo de engenharia precisa de pipeline — CAD e BIM não entram direto, e a identidade de cada peça tem de sobreviver à otimização; (4) performance de aplicação é sobre o longo prazo — renderizar só quando algo muda, não vazar memória em oito horas, arrancar rápido. E a regra que evita o erro mais caro: Unity quando o 3D é o produto.