Voltar para o Blog
Quest Log

Erros do WebGL na Unity: Compressão, Gzip e Tela Preta

Janela de navegador com tela preta e uma barra de carregamento parada, com arquivos compactados e uma lupa ao redor

Unity WebGL compression error por sintoma: Unable to parse Build .gz, tela preta, carga parada em 90%, Content-Encoding no servidor e Decompression Fallback.

Ferramenta:Unity

Erros do WebGL na Unity: Compressão, Gzip e Tela Preta

O unity webgl compression error mais comum, "Unable to parse Build/...framework.js.gz", quase nunca é bug da build: é o servidor entregando um arquivo comprimido sem o cabeçalho Content-Encoding que diz ao navegador como descomprimir. Você resolve de dois jeitos: configurar o servidor (melhor) ou ativar Decompression Fallback na Unity (quando não dá para mexer no servidor).

Este post organiza os erros da build Web por sintoma, com a causa, o teste que confirma e a correção para cada um. Inclui a configuração de Nginx e Apache baseada nos exemplos oficiais da Unity e um servidor local em Python para testar sem cair no erro de file://.

Como a compressão da build Web funciona

Quando você gera a build Web (plataforma Web em File > Build Profiles), a Unity comprime os três arquivos grandes da pasta Build/:

  • .data: cenas, assets e recursos.
  • .framework.js: o JavaScript de ligação com o navegador.
  • .wasm: o seu código C# convertido em WebAssembly.

Com gzip, eles saem como Web.data.gz, Web.framework.js.gz e Web.wasm.gz. O Web.loader.js não é comprimido: ele é quem baixa e liga o resto.

A ideia é que o navegador descomprima durante o download, o que é rápido e não custa nada ao jogo. Para isso, o servidor precisa responder cada .gz com Content-Encoding: gzip (ou br para Brotli). Sem o cabeçalho, o navegador entrega os bytes comprimidos ao loader, que não consegue lê-los.

Um detalhe que confunde: segundo o manual do Unity 6.3, Compression Format vale só para builds de release. Development Build não é comprimida. Por isso "a build de desenvolvimento funcionava e a final não".

Unity WebGL compression error: a mensagem e a causa

A mensagem que aparece na página, para gzip, é esta:

Unable to parse Build/Web.framework.js.gz! This can happen if build compression was enabled but web server hosting the content was misconfigured to not serve the file with HTTP Response Header "Content-Encoding: gzip" present. Check browser Console and Devtools Network tab to debug.

O nome do arquivo muda conforme a sua build. Para Brotli, a mensagem fala em Content-Encoding: br e acrescenta que a compressão Brotli pode não ser suportada em conexões HTTP.

Como confirmar em 30 segundos:

  1. Abra o jogo no navegador e pressione F12.
  2. Aba Network (Rede), recarregue a página.
  3. Clique no arquivo .framework.js.gz e veja Response Headers.
  4. Se não existe Content-Encoding: gzip, a causa é essa.

Gzip vs Brotli vs Disabled

Em Edit > Project Settings > Player > Web > Publishing Settings > Compression Format, o manual do Unity 6.3 descreve as três opções:

  • Gzip (padrão): arquivos maiores que Brotli, mas build mais rápida e suporte nativo em todos os navegadores, em HTTP e HTTPS. É a escolha segura.
  • Brotli: a melhor taxa de compressão, porém build mais lenta, e Chrome e Firefox só descomprimem Brotli nativamente via HTTPS.
  • Disabled: sem compressão. Use quando o servidor ou a CDN já comprime os arquivos por conta própria, ou para testes locais.

Regra prática: hospedagem sem HTTPS ou que você não conhece bem, gzip. Servidor seu, com HTTPS e cabeçalhos configurados, Brotli economiza download.

Decompression Fallback: prós e contras

Com Decompression Fallback ligado (mesma seção Publishing Settings), a Unity embute um descompressor em JavaScript na build e os arquivos passam a ter a extensão .unityweb. Se o navegador não descomprimir, o loader descomprime.

A favor: a build funciona em quase qualquer hospedagem estática, sem configurar nada.

Contra, segundo o manual: loader maior e esquema de carregamento menos eficiente. E a compilação do WebAssembly em streaming (o navegador compilar enquanto baixa) deixa de funcionar, porque o .wasm precisa passar pelo descompressor primeiro. Em jogo grande, isso aumenta o tempo até a primeira tela.

Para alternar entre perfis de build sem lembrar de cada caixa, use um script de editor. Arquivo Assets/Editor/WebCompressionMenu.cs:

using UnityEditor;

public static class WebCompressionMenu
{
    [MenuItem("Tools/Web/Servidor configurado (Gzip nativo)")]
    public static void UseNativeGzip()
    {
        PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Gzip;
        PlayerSettings.WebGL.decompressionFallback = false;
    }

    [MenuItem("Tools/Web/Hospedagem sem controle (Fallback)")]
    public static void UseFallback()
    {
        PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Gzip;
        PlayerSettings.WebGL.decompressionFallback = true;
    }

    [MenuItem("Tools/Web/Sem compressão (teste local)")]
    public static void UseNoCompression()
    {
        PlayerSettings.WebGL.compressionFormat = WebGLCompressionFormat.Disabled;
        PlayerSettings.WebGL.decompressionFallback = false;
    }
}

A API ainda usa o nome WebGL em PlayerSettings.WebGL, mesmo com a plataforma chamada Web no Unity 6.

Configurar servidor: Content-Encoding e Content-Type

O manual define dois cabeçalhos por arquivo. Content-Encoding (gzip para .gz, br para .br) e Content-Type: application/wasm para .wasm (habilita a compilação em streaming) e application/javascript para .js.

Nginx, baseado no exemplo oficial da Unity para gzip, dentro do location do site:

location ~ .+\.(data|symbols\.json)\.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    default_type application/gzip;
}

location ~ .+\.js\.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    default_type application/javascript;
}

location ~ .+\.wasm\.gz$ {
    gzip off;
    add_header Content-Encoding gzip;
    default_type application/wasm;
}

O gzip off evita que o Nginx tente comprimir de novo um arquivo que já está comprimido. O tipo application/gzip no .data é o que a Unity recomenda por causa de um bug do Safari.

Apache com .htaccess (exige o módulo mod_mime), também do exemplo oficial:

<IfModule mod_mime.c>
  RemoveType .gz
  AddEncoding gzip .gz
  AddType application/gzip .data.gz
  AddType application/wasm .wasm.gz
  AddType application/javascript .js.gz
  AddType application/octet-stream .symbols.json.gz
</IfModule>

Para Brotli, troque .gz por .br e gzip por br nos dois casos, e lembre do HTTPS.

Hospedagens estáticas (GitHub Pages e similares) em geral não deixam definir cabeçalho por arquivo. Nelas, use Decompression Fallback ou Compression Format em Disabled. No itch.io, a documentação de HTML5 informa que arquivos .br e gzip são detectados e servidos com o cabeçalho certo, então a build padrão funciona sem fallback. O passo a passo da página está em publicar Unity WebGL no itch.io.

Corrigir servidor é a parte mecânica. A parte que pesa é quando o jogo carrega e mesmo assim não funciona direito: tela preta por script que quebra na inicialização, memória estourada, cena pesada demais para o navegador. Esses problemas nascem na arquitetura do jogo, e é aí que aprender a estruturar um projeto completo faz diferença.

Curso de Unity
Quer fazer seu primeiro jogo na Unity?

No curso de Unity 6 da CursoGame.Dev você vai do zero ao primeiro jogo, com um desafio prático em cada level e suporte individual no WhatsApp quando travar.

Conhecer o curso de Unity
Suporte no WhatsAppAcesso vitalícioGarantia de 7 dias

Tela preta e travar em 90%

Tela preta depois da barra encher: os arquivos carregaram, mas algo falhou ao iniciar.

  • Abra o Console do navegador (F12) e procure a primeira exceção vermelha. Exceção de C# aparece com o nome do script quando a build tem stack trace.
  • Gere uma Development Build: ela não é comprimida (descarta compressão como causa) e mostra mensagens mais claras.
  • Causas frequentes: script que usa System.IO.File ou thread (não suportados no navegador), shader não incluído na build, cena inicial errada na Scene List.

Carregamento parado em 90% ou perto do fim: o download terminou e a página está compilando o WebAssembly ou inicializando.

  • Em build grande, isso é lentidão, não erro. Espere um minuto e veja se avança.
  • Se o servidor não envia Content-Type: application/wasm, o navegador não compila em streaming e a etapa final demora mais. Confira o cabeçalho na aba Network.
  • Se trava sempre e o console mostra erro de memória (Out of memory), a cena é pesada demais para o navegador. Reduza texturas e divida o conteúdo.

Testar localmente sem erro de file://

Abrir o index.html com duplo clique usa o protocolo file://, e o navegador bloqueia o carregamento dos arquivos da build. Três saídas:

  1. Build And Run em Build Profiles: a Unity sobe um servidor local já configurado.
  2. Build sem compressão (menu do script acima) e qualquer servidor estático.
  3. Um servidor local que envia os cabeçalhos certos. Salve como servidor_unity.py dentro da pasta da build e rode python servidor_unity.py:
import http.server

class UnityHandler(http.server.SimpleHTTPRequestHandler):
    def end_headers(self):
        path = self.path.split("?")[0]
        if path.endswith(".gz"):
            self.send_header("Content-Encoding", "gzip")
        elif path.endswith(".br"):
            self.send_header("Content-Encoding", "br")
        super().end_headers()

    def guess_type(self, path):
        name = str(path)
        for ext in (".gz", ".br"):
            if name.endswith(ext):
                name = name[: -len(ext)]
        if name.endswith(".wasm"):
            return "application/wasm"
        if name.endswith(".js"):
            return "application/javascript"
        if name.endswith(".data"):
            return "application/octet-stream"
        return super().guess_type(path)

if __name__ == "__main__":
    print("Abra http://localhost:8000")
    http.server.ThreadingHTTPServer(("localhost", 8000), UnityHandler).serve_forever()

Ele só usa a biblioteca padrão do Python. Teste com gzip: Brotli pode não ser aceito em HTTP pelo navegador, como visto acima. Esse servidor é para teste na sua máquina, não para hospedar o jogo.

Checklist de diagnóstico

  1. A mensagem cita Content-Encoding? Confira os cabeçalhos na aba Network.
  2. Está usando Brotli? O site está em HTTPS? Se não, troque para gzip.
  3. Pode mexer no servidor? Configure Content-Encoding e Content-Type. Não pode? Ative Decompression Fallback.
  4. A hospedagem ou CDN comprime por conta própria? Use Compression Format em Disabled para não comprimir duas vezes.
  5. Carrega e fica preto? Console do navegador e Development Build.
  6. Para em 90%? Espere, confira application/wasm e memória.
  7. Abriu por file://? Use Build And Run ou um servidor local.

Próximo passo

Gere duas builds do mesmo projeto, uma com gzip nativo e outra com fallback, e suba as duas no seu servidor. Compare o tempo até a primeira tela: você vai ver na prática o custo do fallback e decidir com número. Se o destino é o itch.io, a configuração padrão já resolve; leia como publicar jogo no itch.io para montar a página. E se em outro projeto você usa Godot, os mesmos cabeçalhos aparecem em exportar jogo Godot para web.

Perguntas frequentes

O que significa "Unable to parse Build/...gz" na Unity?

Significa que o navegador recebeu o arquivo comprimido sem o cabeçalho Content-Encoding que avisa que ele é gzip ou Brotli. O loader da Unity tenta ler bytes comprimidos como se fossem JavaScript e falha. A correção é configurar o servidor ou ativar Decompression Fallback na build.

Brotli funciona em HTTP?

Não de forma nativa no Chrome e no Firefox. O manual do Unity 6.3 informa que esses navegadores só descomprimem Brotli nativamente via HTTPS. Em hospedagem sem HTTPS, use gzip, que funciona em HTTP e HTTPS em todos os navegadores.

Devo ativar Decompression Fallback?

Ative quando você não controla o servidor, por exemplo numa hospedagem que não deixa mudar cabeçalhos. O custo é um loader maior e um carregamento menos eficiente, e o WebAssembly perde a compilação em streaming. Quando você pode configurar o servidor, deixe desligado.

Por que a build abre no Build And Run e não abre no meu servidor?

Porque o servidor local da Unity já envia os cabeçalhos certos para .gz e .br, e o seu servidor não. Compare os cabeçalhos de resposta dos arquivos da pasta Build na aba Network do navegador nos dois casos e copie a configuração.