Baue ein Spiel aus dem Kit
Die expo-game-Vorlage enthält bereits die Engine: Festzeitschritt-Loop, Tastatur- und Touch-Eingabe, Kamera, Kollisionshelfer, HUD, Menü, Pause, Ergebnisse, Freischaltungen, gespeicherter Fortschritt und ein funktionierendes Beispielspiel (Coin Dash). Deine Aufgabe ist es, den Inhalt des Beispiels durch das Spiel des Benutzers zu ersetzen. Entwirf oder schreibe keine Engine.
Regeln für diesen Bau
- Schreibe Code in deinem ersten Schritt. Plane nicht langatmig. Entscheide die Spielregeln in maximal 10 Zeilen Gedanken, dann bearbeite Dateien. Behebe Probleme, nachdem sie auftreten.
- Halte jeden Schritt klein: eine Datei oder eine Funktion, dann führe
bun run typecheckaus. Ein langer Überlegungsdurchgang ohne Code ist ein Fehlschlag. - Implementiere niemals neu, was das Kit bereits bietet: Loop, Eingabe, Kollision, Kamera, HUD, Bildschirme, Speicher.
- Mache das Spiel in Echtzeit spielbar, bevor du Menüs, Grafiken oder Freischaltungen hinzufügst. Das Beispiel hat bereits Menüs.
Projekt
Erstelle das Projekt mit omg_create_project und template: "expo-game". Folge dem Expo-Schnellpfad im omg-app-builder-Skill für die erste Vorschau. Dann bearbeite.
Dateien, die du bearbeitest (in dieser Reihenfolge)
src/game/theme.ts: Farben aus dem Design.src/games/<dein-spiel>/logic.ts: Kopierecoin-dash/logic.ts. Reine Regeln:createGame(),stepGame(game, input, dt),finalScore(),UNLOCKS. Kein React, keine Zeichnung, keinDate.now(). Lese Eingaben nur durchaxisX/axisY/wasTapped(input, "p1" | "p2", ...). Verwendeseeded()für Zufälligkeit.src/games/<dein-spiel>/game-view.tsx: Kopierecoin-dash/game-view.tsx. Zeichne mitWorldundSprite(farbige Formen, Emojilabeloderreact-native-svg). Verwende Welteinheiten; die Kamera konvertiert.src/app/play.tsx: Ändere die Importe von coin-dash zu deinem Spiel. SetzekindaufTouchControls(dpad,stick,steer) und dessenbuttons({ action: "a", label: "SPRINGEN" }). Behalte die Verdrahtung von Loop, Tastatur, Pause und Abschluss bei.src/app/index.tsx,results.tsx,unlocks.tsx: Titel, Text und denUNLOCKS-Import. Schreibe keine automatisierten Tests, es sei denn, der Benutzer fragt danach. Überprüfe stattdessen mit der Selbstkontrolle (siehe Abschluss).
Bearbeite nicht src/game/loop.ts, input.ts, collision.ts, camera.ts, progress.ts, es sei denn, ein Kit-Bug blockiert dich. Füge Hilfsfunktionen in deinem Spielordner hinzu.
Kit-API (verwende diese Namen)
createLoop({ step })unduseGameLoop(update, { paused }): update läuft mit einem festen 1/60 s Schritt.createInput(),axisX/axisY,wasTapped,endFrame,setAction,setStick.useKeyboard(input, players, onEscape): P1 = WASD + Leertaste/E; im Zweispielermodus P2 = Pfeiltasten + Eingabetaste.TouchControls players={1|2} kind="dpad|stick|steer" buttons={[...]}: Der Zweispielermodus teilt P1 links und P2 rechts auf. Für geteilten Bildschirm rendere zweiGameViews, eine Kamera pro View (sieheplay.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.- Bildschirme:
MenuScreen,PauseOverlay,ResultsScreen,UnlocksScreen,SettingsRow,GameButton. HUD:Hud,HudStat,HudBar,Banner.
Web-Fallstricke
- Miss den Spielbereich mit
useViewSize()(onLayout).useWindowDimensions()ist in einem statischen Web-Export bis zu einer Größenänderung 0. Alert.alertmit Buttons tut im Web nichts. VerwendePauseOverlayoder einen modalen In-Bildschirm-Dialog.- Behalte den Fortschritt in
profileStore, nicht im Komponentenstatus: Ein Neuladen muss Bestwerte und Freischaltungen behalten. - Verwende für die Spielzeit kein
setIntervaloderDate.now(). Zähle das Loop-dt. - Lege keinen Zustand pro Frame in React-
useState. Behalte ihn im Spielobjekt (ein Ref); der Hook zeichnet jeden Frame neu.
3D-Spiele
Das Kit ist 2D. Für ein 3D-Spiel (ein echtes Bedürfnis, keine Stilwahl) behalte Loop, Eingabe, HUD und Bildschirme bei und ersetze nur game-view.tsx.
bunx expo install three @react-three/fiber expo-gl && bun add -d @types/three
Schreibe zwei kleine Dateien mit denselben Exporten, damit Metro die richtige auswählt:
src/components/scene.web.tsx:import { Canvas, useFrame } from "@react-three/fiber"src/components/scene.tsx: derselbe Code mitfrom "@react-three/fiber/native"
Füge ein <Canvas camera={{ position: [0, 0, 4] }}> mit einem ambientLight, einem directionalLight und einem <mesh> pro Spielobjekt ein (Boxen, Kugeln, Ebenen, einfarbige Farben). Kopiere Positionen aus deinem Spielobjekt bei jedem Rendern; führe stepGame in useGameLoop aus, nicht in useFrame. Behalte HUD und Steuerung als normale React-Native-Views über der Leinwand. Die Leinwand benötigt ein Elternelement mit einer Größe (flex: 1).
Verifiziert im Kit-Spike: Eine 3D-Szene rendert in Expo Web, und das Android-JS-Bundle kompiliert. Nicht verifiziert: Rendern auf einem Gerät oder in Expo Go. Sage dies in der abschließenden Nachricht.
Abschluss
Führe bun run typecheck und bun run build aus. Schreibe keine automatisierten Tests, es sei denn, der Benutzer fragt danach. Führe dann bun run check:ui aus: Es öffnet jede Route bei 390x844, speichert ein PNG pro Route in .omg/ui-check/ und meldet leere Seiten, Fehlergrenzen, Konsolenfehler, fehlgeschlagene Anfragen und Schaltflächen, die von einem anderen Element verdeckt werden. Öffne jedes PNG, vergleiche es mit der Anfrage und eventuellen Design-Referenzen, korrigiere, was falsch ist, und führe es erneut aus, bis der Bericht sauber ist. Der Check spielt das Spiel nicht, also überprüfe es auch in Expo Web: Es muss sich mit Tasten bewegen, beenden, den Ergebnisbildschirm anzeigen und den Bestwert nach einem Neuladen behalten. Sage, welche Oberflächen du getestet hast; ein Web-Lauf ist kein Beweis für natives Verhalten. Öffne nach der Veröffentlichung die Live-URL in Telefongröße. Die omg-Installationsleiste sitzt unterhalb des Spiels, das darüber zu einer Karte wird, sodass die Leiste keine Spielsteuerung verdeckt. Verschiebe oder verstecke sie nicht.