For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navegação principal
4 de set. de 2026 Codex

Criando jogos com Astra

Como criei um jogo de exploração espacial procedural no Codex, da arte conceitual a planetas onde é possível pousar.

Criando jogos com Astra

Astra ficou muito bom em transformar o que tenho em mente em jogabilidade e direção de arte. Tenho usado Astra no Codex para criar Void Explorer, um jogo de exploração espacial em que você pode viajar de estrelas distantes até paisagens alienígenas.

Uma compilação de cenas de Void Explorer em ação.

O jogo tem 2.048 sistemas estelares e mais de 10.000 planetas gerados proceduralmente, incluindo mundos do tamanho da Terra. Toda estrela visível faz parte do universo e pode ser selecionada como destino. Você pode escolher um ponto de luz ao longe e viajar até ele.

Você pode se aproximar de um planeta vindo do espaço, atravessar sua atmosfera e continuar descendo até sobrevoar o litoral. Pode pousar, sair da nave, caminhar e decolar novamente. Fazer essa jornada funcionar exigiu resolver em conjunto as questões de escala, terreno, controles e renderização.

Incluí alguns prompts usados durante o desenvolvimento, editados para ficarem mais curtos e claros. Eles mostram como descrevi o que queria e como o trabalho técnico se desenvolveu a partir daí.

Comece pela experiência

Minha descrição inicial explicava o que o jogador deveria poder fazer:

Tudo o que eu conseguir ver deve ser alcançável. Mantenha as distâncias reais e viabilize as viagens ajustando a escala e a velocidade. Quero voar do espaço para a atmosfera de um planeta e descer até o solo. Os planetas podem ser tão grandes quanto a Terra, então vamos precisar de terreno procedural e de um renderizador que trabalhe em blocos.

Isso deu a Astra algumas restrições concretas. Uma estrela não podia ser apenas um ponto pintado no fundo. Um planeta não podia virar uma fase separada quando eu me aproximasse dele. As viagens precisavam cobrir distâncias reais, viabilizadas por recursos fictícios de viagem por pulso e hipervelocidade.

Também usei geração de imagens para definir o visual antes de desenvolver boa parte do jogo. Os primeiros conceitos eram realistas demais. A direção seguinte era simples demais. Esta foi uma das minhas correções:

Agora ficou um pouco simplista demais. Precisamos de um meio-termo: cores melhores, luz neon e mais contraste para evocar o espaço profundo. Mostre como fica em alta velocidade, com estrelas e poeira ao redor da nave.

Quando gostei das imagens, pedi a Astra que as salvasse e transformasse aquela direção visual em referências para o jogo. Tínhamos objetivos específicos para voo orbital, alta velocidade, entrada na atmosfera e pouso. Essas imagens facilitaram muito a avaliação da versão seguinte.

Arte conceitual gerada de uma nave cor de marfim sobre um planeta ciano facetado, com detalhes magenta e dois sóis de tons quentes

Arte conceitual gerada que definiu a paleta e a direção visual do jogo.

Deixe Astra propor a arquitetura

Eu defini as restrições, e Astra propôs a implementação. O aplicativo usa TypeScript e Vite, com Three.js para renderização. Isso dá ao código controle direto sobre malhas, materiais, iluminação e geometria procedural, que concentram a maior parte do trabalho visual deste jogo.

O renderizador da primeira versão jogável usava WebGL2. Depois, perguntei se o Three.js estava limitando o visual. Astra recomendou manter a simulação e migrar o renderizador para a arquitetura WebGPU do Three.js. Mantivemos os sistemas de universo, navegação e terreno enquanto alterávamos a camada de renderização.

O renderizador usa materiais baseados em nodes do Three.js e a Three.js Shading Language (TSL) para definir a atmosfera, a água e os efeitos de iluminação em código.

A geração de terreno roda em Web Workers para preparar a geometria fora da thread responsável pela entrada de comandos e pela renderização. O Vitest cobre aspectos como a geração reproduzível, os cálculos de coordenadas e os contratos do terreno. O Playwright testa o jogo em um navegador. Isso deu a Astra maneiras de verificar os sistemas subjacentes enquanto eu continuava avaliando a experiência de jogar.

A direção de arte continuou sendo a referência durante todo o processo. A atmosfera ao redor de um planeta, o brilho de seus anéis, a luz colorida das estrelas e o efeito Neon Phosphor contribuem para o visual. O filtro adiciona uma textura de tela retrô sem comprometer a legibilidade da navegação.

Void Explorer em ação, mostrando a nave de quatro asas, um planeta ciano, anéis magenta, sóis gêmeos e o painel de navegação

Uma captura de tela do jogo com o efeito Neon Phosphor ativado.

Dê a Astra uma forma de inspecionar e jogar

Eu continuava jogando, mas Astra também precisava de maneiras de investigar um problema além de ler minha descrição. O jogo expõe uma pequena interface JavaScript, window.__VOID_EXPLORER__, que os testes no navegador podem chamar para inspecionar o estado atual: de qual corpo celeste estou me aproximando, o modo de voo, se o terreno está pronto e qual é a câmera ativa. Também expõe contadores de renderização e streaming, incluindo chamadas de renderização, contagens de triângulos, tarefas de terreno na fila e dados de terreno armazenados em buffer.

O jogo tem cenas de teste nomeadas para órbita, viagem por pulso, descida atmosférica e pouso no litoral. Elas permitem que Astra volte a um ponto de partida útil sem precisar atravessar o universo a cada vez. O Playwright pode carregar uma cena, esperar o terreno ficar pronto, capturar a tela e verificar o estado por trás dela. Testes separados de jornada usam os controles reais e registram posições e mudanças de estado enquanto o jogo roda, para que preparar uma cena de pouso não substitua o teste do pouso em si.

Esses testes cobrem pousar, sair da nave, caminhar, salvar e recarregar, embarcar e decolar. Eles podem detectar problemas que uma captura de tela sozinha não revelaria, como uma superfície de colisão que ainda não está pronta ou uma nave que salta entre posições durante uma transição.

Eu também podia pedir a Astra que inspecionasse a aba do navegador em que estava jogando. Quando perguntei se as nuvens de Aurelia ainda estavam visíveis, Astra capturou minha visão atual e leu as informações de voo na tela. Eram verificações em momentos específicos; Astra não observava continuamente cada quadro do meu voo.

O ciclo consistia em reproduzir um problema, inspecionar capturas de tela e o estado, rastrear o código relevante, fazer uma alteração e repetir a verificação. Em outro jogo, eu criaria essas ferramentas logo no início: algumas cenas reproduzíveis, informações úteis de estado e contadores de desempenho, além de testes no navegador para as principais interações. Elas dão a Astra maneiras de investigar e testar alterações de forma independente enquanto continuo avaliando o visual e a resposta dos controles.

Represente o universo em várias escalas

Ter milhares de planetas não significa carregar milhares de malhas detalhadas. Um planeta começa como uma descrição: seu raio, órbita, atmosfera e semente. A semente permite reproduzir o terreno. O jogo gera a geometria ao redor da área de que estou me aproximando e pode gerar a mesma paisagem novamente quando eu voltar.

As coordenadas exigem um cuidado semelhante. Distâncias de anos-luz e uma pessoa ao lado de uma nave são escalas muito diferentes. Enviar essas posições enormes diretamente para a GPU causaria a perda da precisão necessária perto do solo.

Astra separou o endereço físico da posição usada na renderização. O universo usa células representadas por inteiros grandes, com deslocamentos locais menores. Antes de renderizar, o jogo subtrai a posição do observador, de modo que a câmera permaneça na origem e os objetos próximos tenham coordenadas pequenas. As distâncias exibidas e os tamanhos relativos permanecem consistentes.

Os elementos em movimento também precisam usar a mesma referência de tempo. Estrelas, planetas e luas seguem órbitas analíticas, e o renderizador calcula suas posições no mesmo instante da simulação usado para o observador. Uma nave estacionada permanece presa ao planeta em rotação. As posições e cores das estrelas alimentam a iluminação, então os dois sóis de um pôr do sol binário são as mesmas estrelas que vi da órbita.

Torne a descida contínua

Voando do espaço em direção à superfície de uma lua em Void Explorer.

A transição do espaço até o solo exigiu muitas iterações. Um dos meus prompts surgiu ao me aproximar de um planeta em um ângulo baixo:

Quando me aproximo quase em paralelo à superfície de um planeta, ainda consigo chegar rápido demais, e o planeta quase desaparece enquanto o terreno carrega. Precisamos corrigir a curva de velocidade e o streaming do terreno em conjunto. A atmosfera poderia suavizar a transição entre o LOD distante e o terreno detalhado? O planeta nunca deve desaparecer durante a aproximação.

O controlador de voo agora aplica um limite de velocidade específico para aproximações em ângulos baixos, com base na altitude e em uma estimativa de velocidade orbital calculada a partir do raio e da gravidade do planeta. Mais perto do solo, ele também coleta amostras do terreno à frente para ajustar a frenagem.

O jogo usa vários níveis de detalhe, ou LODs. De longe, um planeta é uma esfera facetada de baixo custo computacional, chamada de proxy. À medida que ele ocupa mais espaço na tela, o renderizador adiciona detalhes. A aproximação da superfície ativa um terreno construído a partir das seis faces de um cubo projetadas sobre uma esfera. Cada face pode se dividir repetidamente em quatro quadrados menores: uma árvore quaternária.

Isso permite que o jogo refine o terreno na área visível e na direção do deslocamento sem gerar uma superfície do tamanho da Terra com a resolução necessária para caminhar. Perto do solo, trechos locais fornecem uma geometria mais detalhada ao redor da nave e do jogador.

Todas essas representações extraem amostras do mesmo planeta subjacente. Uma função compartilhada combina continentes, cadeias de montanhas, crateras e detalhes menores do relevo. Ela fornece elevação, água, bioma e outros sinais usados pela paisagem. Malhas menos e mais detalhadas aproximam esses dados em resoluções diferentes, com uma política de cores compartilhada para manter a aparência do planeta consistente.

A substituição entre representações importa tanto quanto a geração. O planeta distante permanece visível até que as seis faces de terreno menos detalhadas estejam prontas. Um bloco de terreno permanece no lugar até que seus quatro blocos filhos estejam prontos. Durante uma transição, máscaras de pixels complementares atribuem cada pixel à superfície antiga ou à nova. Isso evita que o planeta inteiro fique transparente enquanto sua representação substituta carrega.

A atmosfera muda com a altitude física, e as nuvens se deslocam sobre a geografia do planeta. Elas ajudam a conectar a visão orbital à paisagem. O renderizador só remove a cobertura menos detalhada onde o terreno mais detalhado já está pronto. As silhuetas das montanhas ainda podem mudar à medida que surge uma geometria mais detalhada.

O pouso acrescenta um requisito mais rigoroso. O solo visível, os triângulos de colisão e os perigos líquidos de uma área de contato precisam vir da mesma geração e ficar prontos juntos. A caminhada usa Rapier em um pequeno mundo de simulação física local. A lógica de pouso da nave verifica separadamente seus apoios, a inclinação e o espaço livre em relação ao terreno.

Há um limite intencional aqui: os planetas usam campos de altura, com uma elevação em cada direção da superfície. Isso funciona bem para grandes paisagens, mas não permite cavernas, saliências suspensas ou túneis destrutíveis.

Meça o trabalho por trás de um quadro lento

Alguns dos meus comentários combinavam aparência e desempenho, porque eu via os dois problemas durante o mesmo voo:

Você pode analisar como carregamos e renderizamos um planeta enquanto voo do espaço em direção ao solo? O globo distante e o terreno detalhado não têm uma aparência consistente, e a transição é brusca, especialmente quando as cores mudam. Quero entender o que causa esses saltos visuais e onde podemos reduzir o trabalho necessário para carregar mais detalhes, para que a descida fique mais bonita e fluida.

Astra usou os contadores do renderizador do Three.js para inspecionar chamadas de renderização, triângulos, geometrias e texturas. Um teste do Playwright coletou 90 intervalos entre quadros e apresentou a média e os percentis dos tempos junto com essas contagens. Para problemas visuais, os testes também podiam comparar os pixels renderizados com um efeito específico ativado e desativado, como o terreno com e sem a máscara que oculta o solo sobreposto. Isso ajudou a isolar a causa do desaparecimento do solo, em vez de depender apenas de uma captura de tela da cena com o problema.

Uma versão anterior da nave mostrou por que essas medições eram úteis. Substituir a nave procedural pelo primeiro modelo AURORA criado no Blender aumentou a contagem de triângulos da cena, mas reduziu suas chamadas de renderização. Astra executou o mesmo teste orbital de 90 quadros antes e depois da alteração no backend WebGPU:

MediçãoAntesDepois
Total de chamadas de renderização da cena11977
Total de triângulos da cena48.80961.092
Intervalo médio entre quadros251,48 ms199,26 ms

Essa comparação usou o Chromium sem interface gráfica, com renderização por software via SwiftShader, e foi feita antes da nave de quatro asas mostrada abaixo. Ela mede a mudança naquele ambiente de teste, não a taxa de quadros na minha GPU. As ferramentas forneceram ao Astra medições de tempo do navegador, imagens renderizadas e contagens de recursos; elas não mediram comandos individuais da GPU nem tempos de execução dos shaders.

O Astra investigou o processamento durante o carregamento e a descida: quanta geometria era gerada, quanto volume de dados era transferido entre os workers e o renderizador e com que frequência as tarefas de terreno eram descartadas antes de terem alguma utilidade.

Uma das mudanças foi definir o nível de detalhe dos planetas distantes de acordo com seu tamanho na tela. Um planeta que ocupa toda a visão precisa de uma boa silhueta imediatamente. Um planeta menor que um pixel não precisa da mesma geometria. Em uma comparação controlada da cena inicial, a política revisada usou 7.040 triângulos nos proxies em vez de 51.200. As duas políticas usaram o mesmo gerador de geometria, o que permitiu isolar a decisão de carregamento.

Outra melhoria manteve exatamente o mesmo número de triângulos do terreno. Malhas indexadas reutilizam vértices compartilhados por triângulos vizinhos, em vez de repetir seus dados de posição, cor e normal. No conjunto das seis camadas de solo do teste, com a qualidade do terreno no nível Alto, os buffers transferidos passaram de cerca de 35 MB para 15 MB, mantendo 241.952 triângulos. O solo e os elementos do cenário também passaram a ser gerados em tarefas separadas, para que a geometria do solo pudesse ficar pronta sem esperar pela decoração.

Também havia desperdício de processamento no seletor de terreno. Ele podia solicitar blocos, mudar de decisão, descartá-los e solicitar substitutos. O Astra estabilizou essas decisões de refinamento e passou a refinar um bloco apenas quando o limite de recursos permitia gerar os quatro blocos filhos. Em uma simulação controlada com latência fixa dos workers, seis segundos de descida seguidos por três segundos de estabilização resultaram em 13 tarefas descartadas, em vez de 6.074.

Gráfico de linhas das tarefas agendadas de terreno que foram descartadas ao longo de nove segundos simulados. O seletor anterior chega a 6.074 tarefas; o seletor revisado se mantém em 13. A câmera para após seis segundos.

Em uma simulação controlada com dois workers e latência simulada de 100 ms por worker, o número de tarefas agendadas descartadas caiu de 6.074 para 13. Essa medição avalia o agendamento de tarefas de terreno, não a taxa de quadros.

Os workers, por si só, não resolvem todas as travadas. O mecanismo alternativo de geração de terreno na thread principal também precisava ceder tempo de execução. O Astra dividiu a geração em etapas que podiam ser retomadas, com um limite de aproximadamente 4 ms por quadro, para que uma tarefa grande de terreno não precisasse ser concluída em um único bloco ininterrupto. O renderizador também limita a resolução do mundo separadamente da interface, mantendo o texto de navegação nítido quando a qualidade visual é reduzida.

Essas medições do terreno mostram menos geometria, menos dados transferidos e menos processamento descartado. Elas não são benchmarks da taxa de quadros do hardware. Testar o jogo no navegador continua sendo importante para avaliar a compilação de shaders, as transferências para a GPU, o movimento e a aparência real da transição.

Minha contribuição era o feedback ao jogar: o que parecia lento, o que estava visualmente errado e o que eu queria melhorar. O Astra rastreava o código, criava scripts de testes repetíveis do terreno e registrava as medições. Depois que eu aprovava a abordagem, ele implementava as mudanças e executava os mesmos testes novamente para comparar os resultados. Ele conduzia essa investigação e implementação sem que eu precisasse determinar cada etapa, enquanto eu continuava avaliando a aparência do jogo e a sensação de jogá-lo.

Transforme arte conceitual em recursos para o jogo

O universo é gerado principalmente por código: planetas, terreno, nuvens, anéis e estrelas. A nave do jogador é o principal recurso 3D modelado individualmente, e adotei uma abordagem diferente para ela.

Usei artes conceituais geradas e renderizações do Blender para avaliar a nave antes de colocá-la no jogo. Quando as asas não correspondiam ao que eu tinha em mente, pedi referências mais úteis:

Gere vistas claras desta nave de vários ângulos. Preste atenção às asas dianteiras e traseiras. Vamos usar essas imagens para refazer o modelo 3D. Não gosto das asas unidas e arredondadas do modelo atual.

Escolhi a referência com quatro asas separadas, formato simétrico e luzes rosas nas pontas. Depois, pedi ao Astra que criasse um novo modelo no Blender, preservasse o estilo artístico do jogo e o adicionasse ao jogo.

Isso envolveu transformar a referência em geometria, revisar a silhueta e os materiais e preparar um recurso para uso em tempo de execução. O arquivo-fonte do Blender tem 193 malhas editáveis. A nave exportada tem 14.968 triângulos, agrupados em oito lotes de materiais opacos. Eu podia pedir mais detalhes no modelo enquanto o Astra mantinha sob controle o custo de renderização da versão exportada.

Arte conceitual gerada em vista superior, mostrando quatro asas separadas, luzes rosas nas pontas das asas, casco cor de marfim e cobertura verde da cabine

A arte conceitual gerada da nave que eu aprovei.

Renderização no Blender do casco cerâmico da nave, das quatro asas separadas e dos dois motores ciano

Uma renderização no Blender do modelo usado no jogo.

Experimente com água procedural

Também passei bastante tempo experimentando com água em Sunwake, um jogo em que você pilota um pequeno barco por um oceano procedural. Eu queria ondas facetadas, movimento suave e cores de vitrais. O Astra criou um renderizador de água personalizado em Three.js, com o mesmo modelo de ondas controlando tanto o mar visível quanto a flutuação do barco. O casco sobe e se inclina para a frente, para trás e para os lados com as ondas, enquanto uma simulação local transporta ondulações e espuma, e o barco deixa esteiras e respingos. Mais tarde, pedi ao Astra que criasse um barco mais detalhado no Blender e o trouxesse para o jogo. É a mesma abordagem de Void Explorer: um ambiente procedural com um veículo modelado individualmente, conectados pela simulação.

Cena de Sunwake mostrando um barco laranja deixando uma esteira de espuma entre ondas azuis facetadas sob um nascer do sol rosado

Sunwake combina um oceano procedural com um barco modelado no Blender.

Hollowflux levou essa ideia em outra direção: um pequeno RPG de ação 2D construído em torno de um rio subterrâneo luminoso. As cavernas, os personagens e os equipamentos são desenhados por código, sem folhas de sprites importadas. Eu pedia repetidamente ao Astra que tornasse mais convincente o efeito de caminhar, dar arrancadas e atacar sobre a água. Uma grade de células acompanha a altura da água, as correntes, a espuma e a carga elétrica. Os passos deixam ondulações, as estocadas de lança abrem esteiras estreitas e os golpes de martelo espalham ondas em anéis. As correntes fazem curvas ao longo das margens e carregam a espuma rio abaixo, enquanto o mesmo fluxo empurra o jogador e os inimigos. A água também afeta o combate: a descarga de uma enguia se propaga pelas áreas de água conectadas, então subir em uma pedra seca protege o jogador dessa descarga.

Um aventureiro em pixel art atravessa a pé um rio luminoso verde-azulado em Hollowflux, cercado por ondulações, criaturas e paredes escuras de caverna

Hollowflux desenha a arte do jogo por código, com água que reage ao movimento e ao combate.

Criar e compartilhar jogos

O que eu gosto em trabalhar com o Astra é poder partir da experiência que quero criar, usar imagens para tornar a direção visual concreta e dar feedback com base na experiência de jogar. Uma reclamação sobre um planeta que desaparece pode levar a mudanças no streaming, na geometria e no movimento. Um pedido de água mais convincente pode virar uma simulação compartilhada pelo visual e pela jogabilidade. Ainda preciso decidir se a experiência está como eu quero, mas o Astra pode aplicar essas decisões ao código, aos recursos e aos testes.

Compartilhar um jogo de navegador também é simples. Com o plug-in Sites, você pode pedir ao Astra:

Use @Sites para fazer o deploy deste jogo e me dar um link.

Publique o jogo com acesso público, e qualquer pessoa poderá jogar diretamente no navegador.

Você pode jogar Void Explorer, Sunwake e Hollowflux no navegador.

Se você tem uma ideia de jogo em mente, experimente criar uma pequena versão jogável com o Astra. Comece por um aspecto da experiência que você quer acertar, forneça algumas referências visuais e teste o resultado. Um barco, uma sala ou uma única interação já basta para começar. Jogue, seja específico sobre o que quer mudar e continue construindo a partir daí.