Construye un juego a partir del kit
La plantilla expo-game ya incluye el motor: bucle de paso fijo, entrada por teclado y táctil, cámara, ayudas de colisión, HUD, menú, pausa, resultados, desbloqueos, progreso guardado y un juego de ejemplo funcional (Coin Dash). Tu trabajo es reemplazar el contenido del ejemplo con el juego del usuario. No diseñes ni escribas un motor.
Reglas para esta construcción
- Escribe código en tu primer turno. No planees extensamente. Decide las reglas del juego en un máximo de 10 líneas de pensamiento, luego edita los archivos. Soluciona los problemas después de que se ejecuten.
- Mantén cada turno pequeño: un archivo o una característica, luego ejecuta
bun run typecheck. Un largo paso de razonamiento sin código es un fracaso. - Nunca reimplementes lo que el kit ya tiene: bucle, entrada, colisión, cámara, HUD, pantallas, almacenamiento.
- Haz que el juego sea en tiempo real y jugable antes de añadir menús, arte o desbloqueos. El ejemplo ya tiene menús.
Proyecto
Crea el proyecto con omg_create_project y template: "expo-game". Sigue la ruta rápida de Expo en el skill omg-app-builder para la primera vista previa. Luego edita.
Archivos que editas (en orden)
src/game/theme.ts: colores del diseño.src/games/<tu-juego>/logic.ts: copiacoin-dash/logic.ts. Reglas puras:createGame(),stepGame(game, input, dt),finalScore(),UNLOCKS. Sin React, sin dibujo, sinDate.now(). Lee la entrada solo a través deaxisX/axisY/wasTapped(input, "p1" | "p2", ...). Usaseeded()para la aleatoriedad.src/games/<tu-juego>/game-view.tsx: copiacoin-dash/game-view.tsx. Dibuja conWorldySprite(formas de colores,labelcon emoji, oreact-native-svg). Usa unidades del mundo; la cámara convierte.src/app/play.tsx: cambia las importaciones de coin-dash a tu juego. EstablecekindenTouchControls(dpad,stick,steer) y susbuttons({ action: "a", label: "SALTAR" }). Mantén el bucle, el teclado, la pausa y la conexión de finalización como están.src/app/index.tsx,results.tsx,unlocks.tsx: títulos, texto y la importación deUNLOCKS. No escribas pruebas automatizadas a menos que el usuario lo pida. Verifica con la autocomprobación en su lugar (ver Finalizar).
No edites src/game/loop.ts, input.ts, collision.ts, camera.ts, progress.ts a menos que un error del kit te bloquee. Añade ayudas en tu carpeta de juego.
API del kit (usa estos nombres)
createLoop({ step })yuseGameLoop(update, { paused }): la actualización se ejecuta en un paso fijo de 1/60 s.createInput(),axisX/axisY,wasTapped,endFrame,setAction,setStick.useKeyboard(input, players, onEscape): P1 = WASD + Espacio/E; en modo dos jugadores P2 = flechas + Enter.TouchControls players={1|2} kind="dpad|stick|steer" buttons={[...]}: el modo dos jugadores divide P1 a la izquierda y P2 a la derecha. Para pantalla dividida, renderiza dosGameView, una cámara cada una (verplay.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.- Pantallas:
MenuScreen,PauseOverlay,ResultsScreen,UnlocksScreen,SettingsRow,GameButton. HUD:Hud,HudStat,HudBar,Banner.
Errores comunes en web
- Mide el área del juego con
useViewSize()(onLayout).useWindowDimensions()es 0 en una exportación web estática hasta que se redimensiona. Alert.alertcon botones no hace nada en web. UsaPauseOverlayo un modal dentro de la pantalla.- Mantén el progreso en
profileStore, no en el estado del componente: una recarga debe conservar las mejores puntuaciones y desbloqueos. - No uses
setIntervaloDate.now()para el tiempo del juego. Cuenta eldtdel bucle. - No pongas estado por fotograma en
useStatede React. Mantenlo en el objeto del juego (un ref); el hook redibuja cada fotograma.
Juegos 3D
El kit es 2D. Para un juego 3D (una necesidad real, no una elección de estilo), mantén el bucle, la entrada, el HUD y las pantallas, y reemplaza solo game-view.tsx.
bunx expo install three @react-three/fiber expo-gl && bun add -d @types/three
Escribe dos archivos pequeños con las mismas exportaciones, para que Metro elija el correcto:
src/components/scene.web.tsx:import { Canvas, useFrame } from "@react-three/fiber"src/components/scene.tsx: el mismo código confrom "@react-three/fiber/native"
Coloca un <Canvas camera={{ position: [0, 0, 4] }}> con un ambientLight, un directionalLight y un <mesh> por objeto del juego (cajas, esferas, planos, colores planos). Copia las posiciones de tu objeto del juego en cada renderizado; ejecuta stepGame en useGameLoop, no en useFrame. Mantén el HUD y los controles como vistas normales de React Native sobre el lienzo. El lienzo necesita un padre con tamaño (flex: 1).
Verificado en el spike del kit: una escena 3D se renderiza en Expo Web, y el paquete JS de Android compila. No verificado: renderizado en un dispositivo o en Expo Go. Dilo en el mensaje final.
Finalizar
Ejecuta bun run typecheck y bun run build. No escribas pruebas automatizadas a menos que el usuario lo pida. Luego ejecuta bun run check:ui: abre cada ruta a 390x844, guarda un PNG por ruta en .omg/ui-check/, e informa páginas en blanco, límites de error, errores de consola, solicitudes fallidas y botones cubiertos por otro elemento. Abre cada PNG, compáralo con la solicitud y cualquier referencia de diseño, corrige lo que esté mal, y ejecútalo de nuevo hasta que el informe esté limpio. La comprobación no juega al juego, así que verifícalo también en Expo Web: debe moverse con teclas, terminar, mostrar la pantalla de resultados y mantener la mejor puntuación después de una recarga. Di qué superficies probaste; una ejecución web no es prueba de comportamiento nativo. Después de publicar, abre la URL en vivo con tamaño de teléfono. La barra de instalación de omg se sitúa debajo del juego, que se convierte en una tarjeta sobre ella, por lo que la barra no cubre ningún control del juego. No la muevas ni la ocultes.