Construa um jogo a partir do kit
O template expo-game já possui o motor: loop de tempo fixo, entrada por teclado e toque, câmera, helpers de colisão, HUD, menu, pausa, resultados, desbloqueios, progresso salvo e um jogo de exemplo funcional (Coin Dash). Seu trabalho é substituir o conteúdo do exemplo pelo jogo do usuário. Não projete ou escreva um motor.
Regras para esta construção
- Escreva código no seu primeiro turno. Não planeje extensamente. Decida as regras do jogo em no máximo 10 linhas de pensamento e edite os arquivos. Corrija problemas depois que eles aparecerem.
- Mantenha cada turno pequeno: um arquivo ou uma funcionalidade, depois execute
bun run typecheck. Uma longa passagem de raciocínio sem código é uma falha. - Nunca reimplemente o que o kit já tem: loop, entrada, colisão, câmera, HUD, telas, armazenamento.
- Torne o jogo jogável em tempo real antes de adicionar menus, arte ou desbloqueios. O exemplo já tem menus.
Projeto
Crie o projeto com omg_create_project e template: "expo-game". Siga o caminho rápido do Expo no skill omg-app-builder para a primeira visualização. Depois edite.
Arquivos que você edita (em ordem)
src/game/theme.ts: cores do design.src/games/<seu-jogo>/logic.ts: copie decoin-dash/logic.ts. Regras puras:createGame(),stepGame(game, input, dt),finalScore(),UNLOCKS. Sem React, sem desenho, semDate.now(). Leia entrada apenas através deaxisX/axisY/wasTapped(input, "p1" | "p2", ...). Useseeded()para aleatoriedade.src/games/<seu-jogo>/game-view.tsx: copie decoin-dash/game-view.tsx. Desenhe comWorldeSprite(formas coloridas,labelemoji, oureact-native-svg). Use unidades de mundo; a câmera converte.src/app/play.tsx: altere os imports de coin-dash para seu jogo. DefinakindemTouchControls(dpad,stick,steer) e seusbuttons({ action: "a", label: "PULAR" }). Mantenha o loop, teclado, pausa e a fiação de finalização como estão.src/app/index.tsx,results.tsx,unlocks.tsx: títulos, texto e o import deUNLOCKS. Não escreva testes automatizados a menos que o usuário peça. Verifique com a auto-checagem (veja Finalizar).
Não edite src/game/loop.ts, input.ts, collision.ts, camera.ts, progress.ts a menos que um bug do kit o impeça. Adicione helpers na sua pasta de jogo.
API do Kit (use estes nomes)
createLoop({ step })euseGameLoop(update, { paused }): update executa em um passo fixo de 1/60 s.createInput(),axisX/axisY,wasTapped,endFrame,setAction,setStick.useKeyboard(input, players, onEscape): P1 = WASD + Space/E; no modo dois jogadores P2 = setas + Enter.TouchControls players={1|2} kind="dpad|stick|steer" buttons={[...]}: modo dois jogadores divide P1 à esquerda e P2 à direita. Para tela dividida, renderize duasGameViews, uma câmera cada (vejaplay.tsx).circlesOverlap,rectsOverlap,circleRectOverlap,pushCircleOutOfRect,keepCircleInside,angleDiff,seeded,clamp,lerp.createCamera,followCamera(cam, target, dt, { velocity, lookAhead, world, viewW, viewH }).profileStore.finishRun({ gameId, score, stats }, UNLOCKS),useProfile(),profileStore.select(group, id),setSettings.- Telas:
MenuScreen,PauseOverlay,ResultsScreen,UnlocksScreen,SettingsRow,GameButton. HUD:Hud,HudStat,HudBar,Banner.
Armadilhas da Web
- Meça a área do jogo com
useViewSize()(onLayout).useWindowDimensions()retorna 0 em uma exportação web estática até um redimensionamento. Alert.alertcom botões não funciona na web. UsePauseOverlayou um modal na tela.- Mantenha o progresso em
profileStore, não no estado do componente: um recarregamento deve manter as melhores pontuações e desbloqueios. - Não use
setIntervalouDate.now()para o tempo do jogo. Conte odtdo loop. - Não coloque estado por frame no React
useState. Mantenha-o no objeto do jogo (uma ref); o hook redesenha a cada frame.
Jogos 3D
O kit é 2D. Para um jogo 3D (uma necessidade real, não uma escolha de estilo), mantenha o loop, entrada, HUD e telas, e substitua apenas game-view.tsx.
bunx expo install three @react-three/fiber expo-gl && bun add -d @types/three
Escreva dois arquivos pequenos com as mesmas exportações, para que o Metro escolha o correto:
src/components/scene.web.tsx:import { Canvas, useFrame } from "@react-three/fiber"src/components/scene.tsx: o mesmo código comfrom "@react-three/fiber/native"
Coloque um <Canvas camera={{ position: [0, 0, 4] }}> com um ambientLight, um directionalLight e um <mesh> por objeto do jogo (caixas, esferas, planos, cores sólidas). Copie as posições do seu objeto de jogo a cada renderização; execute stepGame em useGameLoop, não em useFrame. Mantenha o HUD e controles como visualizações React Native normais sobre o canvas. O canvas precisa de um pai com um tamanho (flex: 1).
Verificado no spike do kit: uma cena 3D renderiza no Expo Web, e o pacote JS do Android compila. Não verificado: renderização em um dispositivo ou no Expo Go. Diga isso na mensagem final.
Finalizar
Execute bun run typecheck e bun run build. Não escreva testes automatizados a menos que o usuário peça. Depois execute bun run check:ui: ele abre cada rota em 390x844, salva um PNG por rota em .omg/ui-check/, e relata páginas em branco, boundaries de erro, erros de console, requisições com falha e botões cobertos por outro elemento. Abra cada PNG, compare com a requisição e quaisquer referências de design, corrija o que estiver errado e execute novamente até que o relatório esteja limpo. A verificação não joga o jogo, portanto verifique também no Expo Web: ele deve se mover com as teclas, terminar, mostrar a tela de resultados e manter a melhor pontuação após um recarregamento. Diga quais superfícies você testou; uma execução web não é prova de comportamento nativo. Após publicar, abra a URL ao vivo no tamanho de um telefone. A barra de instalação do omg fica abaixo do jogo, que se torna um cartão acima dela, então a barra não cobre os controles do jogo. Não mova ou esconda.