react-three-game

安装量: 50
排名: #14814

安装

npx skills add https://github.com/prnthh/react-three-game-skill --skill react-three-game

react-three-game

Instructions for the agent to follow when this skill is activated.

When to use

generate 3D scenes, games and physics simulations in React.

Agent Workflow: JSON → GLB

Agents can programmatically generate 3D assets:

Create a JSON prefab following the GameObject schema Load it in PrefabEditor to render the Three.js scene Export the scene to GLB format using exportGLB or exportGLBData import { useRef, useEffect } from 'react'; import { PrefabEditor, exportGLBData } from 'react-three-game'; import type { PrefabEditorRef } from 'react-three-game'

const jsonPrefab = { root: { id: "scene", children: [ { id: "cube", components: { transform: { type: "Transform", properties: { position: [0, 0, 0] } }, geometry: { type: "Geometry", properties: { geometryType: "box", args: [1, 1, 1] } }, material: { type: "Material", properties: { color: "#ff0000" } } } } ] } };

function AgentExporter() { const editorRef = useRef(null);

useEffect(() => { const timer = setTimeout(async () => { const sceneRoot = editorRef.current?.rootRef.current?.root; if (!sceneRoot) return;

  const glbData = await exportGLBData(sceneRoot);
  // glbData is an ArrayBuffer ready for upload/storage
}, 1000); // Wait for scene to render

return () => clearTimeout(timer);

}, []);

return ; }

Core Concepts Asset Paths and Public Directory

All asset paths are relative to /public and omit the /public prefix:

{ "texture": "/textures/floor.png", "model": "/models/car.glb", "font": "/fonts/font.ttf" }

Path "/any/path/file.ext" refers to /public/any/path/file.ext.

GameObject Structure

Every game object follows this schema:

interface GameObject { id: string; disabled?: boolean; components?: Record; children?: GameObject[]; }

Prefab JSON Format

Scenes are defined as JSON prefabs with a root node containing children:

{ "root": { "id": "scene", "children": [ { "id": "my-object", "components": { "transform": { "type": "Transform", "properties": { "position": [0, 0, 0] } }, "geometry": { "type": "Geometry", "properties": { "geometryType": "box" } }, "material": { "type": "Material", "properties": { "color": "#ff0000" } } } } ] } }

Built-in Components Component Type Key Properties Transform Transform position: [x,y,z], rotation: [x,y,z] (radians), scale: [x,y,z] Geometry Geometry geometryType: box/sphere/plane/cylinder, args: dimension array Material Material color, texture?, metalness?, roughness?, repeat?, repeatCount? Physics Physics type: dynamic/fixed/kinematicPosition/kinematicVelocity, mass?, restitution?, friction?, linearDamping?, angularDamping?, gravityScale?, sensor?, activeCollisionTypes?: 'all' (enable kinematic/fixed collision detection), plus any Rapier RigidBody props - See advanced physics guide Model Model filename (GLB/FBX path), instanced? for GPU batching SpotLight SpotLight color, intensity, angle, penumbra, distance?, castShadow? DirectionalLight DirectionalLight color, intensity, castShadow?, targetOffset?: [x,y,z] AmbientLight AmbientLight color, intensity Text Text text, font, size, depth, width, align, color Text Component

Requires hb.wasm and a font file (TTF/WOFF) in /public/fonts/:

hb.wasm: https://github.com/prnthh/react-three-game/raw/refs/heads/main/docs/public/fonts/hb.wasm Sample font: https://github.com/prnthh/react-three-game/raw/refs/heads/main/docs/public/fonts/NotoSans-Regular.ttf

Font property: "font": "/fonts/NotoSans-Regular.ttf"

Geometry Args by Type geometryType args array box [width, height, depth] sphere [radius, widthSegments, heightSegments] plane [width, height] cylinder [radiusTop, radiusBottom, height, radialSegments] Material Textures { "material": { "type": "Material", "properties": { "color": "white", "texture": "/textures/floor.png", "repeat": true, "repeatCount": [4, 4] } } }

Rotations

Use radians: 1.57 = 90°, 3.14 = 180°, -1.57 = -90°

Common Patterns Usage Modes

GameCanvas + PrefabRoot: Pure renderer for embedding prefab data in standard R3F applications. Minimal wrapper - just renders the prefab as Three.js objects. Requires manual setup. Physics always active. Use this to integrate prefabs into larger R3F scenes.

import { Physics } from '@react-three/rapier'; import { GameCanvas, PrefabRoot } from 'react-three-game';

PrefabEditor: Managed scene with editor UI and play/pause controls for physics. Full authoring tool for level design and prototyping. Includes canvas, physics, transform gizmos, and inspector. Physics only runs in play mode. Can pass R3F components as children.

import { PrefabEditor } from 'react-three-game';

Tree Utilities import { findNode, updateNode, updateNodeById, deleteNode, cloneNode, exportGLBData } from 'react-three-game';

const node = findNode(root, nodeId); const updated = updateNode(root, nodeId, n => ({ ...n, disabled: true })); // or updateNodeById (identical) const afterDelete = deleteNode(root, nodeId); const cloned = cloneNode(node); const glbData = await exportGLBData(sceneRoot);

Hybrid JSON + R3F Children Pattern

Prefabs define static scene structure, R3F children add dynamic behavior:

import { useRef } from 'react'; import { useFrame } from '@react-three/fiber'; import { PrefabEditor, findNode } from 'react-three-game'; import type { PrefabEditorRef } from 'react-three-game';

function DynamicLight() { const lightRef = useRef(null!);

useFrame(({ clock }) => { lightRef.current.intensity = 100 + Math.sin(clock.elapsedTime) * 50; });

return ; }

Use cases: Player controllers, AI behaviors, procedural animation, real-time effects.

Quick Reference Examples // Static geometry with physics (floor, wall, platform, ramp) { "id": "floor", "components": { "transform": { "type": "Transform", "properties": { "position": [0, -0.5, 0] } }, "geometry": { "type": "Geometry", "properties": { "geometryType": "box", "args": [40, 1, 40] } }, "material": { "type": "Material", "properties": { "texture": "/textures/floor.png", "repeat": true, "repeatCount": [20, 20] } }, "physics": { "type": "Physics", "properties": { "type": "fixed" } } }}

// Lighting { "id": "spot", "components": { "transform": { "type": "Transform", "properties": { "position": [10, 15, 10] } }, "spotlight": { "type": "SpotLight", "properties": { "intensity": 200, "angle": 0.8, "castShadow": true } } }}

// 3D Text { "id": "title", "components": { "transform": { "type": "Transform", "properties": { "position": [0, 3, 0] } }, "text": { "type": "Text", "properties": { "text": "Welcome", "font": "/fonts/font.ttf", "size": 1, "depth": 0.1 } } }}

// GLB Model { "id": "tree", "components": { "transform": { "type": "Transform", "properties": { "position": [0, 0, 0], "scale": [1.5, 1.5, 1.5] } }, "model": { "type": "Model", "properties": { "filename": "/models/tree.glb" } } }}

Editor Basic Usage import { PrefabEditor } from 'react-three-game';

Keyboard shortcuts: T (Translate), R (Rotate), S (Scale)

Camera Control

By default, PrefabEditor uses an orbit camera. Override it by adding a custom camera with makeDefault:

import { PerspectiveCamera } from '@react-three/drei'; import { PrefabEditor } from 'react-three-game';

Any R3F camera component works: PerspectiveCamera, OrthographicCamera, or custom camera controllers.

Programmatic Updates import { useRef } from 'react'; import { PrefabEditor, updateNodeById } from 'react-three-game'; import type { PrefabEditorRef } from 'react-three-game';

function Scene() { const editorRef = useRef(null);

const moveBall = () => { const prefab = editorRef.current!.prefab; const newRoot = updateNodeById(prefab.root, "ball", node => ({ ...node, components: { ...node.components, transform: { ...node.components!.transform!, properties: { ...node.components!.transform!.properties, position: [5, 0, 0] } } } })); editorRef.current!.setPrefab({ ...prefab, root: newRoot }); };

return ; }

PrefabEditorRef: prefab, setPrefab(), screenshot(), exportGLB(), rootRef

GLB Export import { exportGLBData } from 'react-three-game';

const glbData = await exportGLBData(editorRef.current!.rootRef.current!.root);

Runtime Animation import { useRef } from "react"; import { useFrame } from "@react-three/fiber"; import { PrefabEditor, updateNodeById } from "react-three-game";

function Animator({ editorRef }) { useFrame(() => { const prefab = editorRef.current!.prefab; const newRoot = updateNodeById(prefab.root, "ball", node => ({ ...node, components: { ...node.components, transform: { ...node.components!.transform!, properties: { ...node.components!.transform!.properties, position: [x, y, z] } } } })); editorRef.current!.setPrefab({ ...prefab, root: newRoot }); }); return null; }

function Scene() { const editorRef = useRef(null); return ( ); }

Custom Component import { Component, registerComponent, FieldRenderer } from 'react-three-game';

const MyComponent: Component = { name: 'MyComponent', Editor: ({ component, onUpdate }) => ( ), View: ({ properties, children }) => {children}, defaultProperties: { speed: 1 } };

registerComponent(MyComponent);

Field types: vector3, number, string, color, boolean, select, custom

Game Events

A general-purpose event system for game-wide communication. Handles physics events, gameplay events, and any custom events.

Core API import { gameEvents, useGameEvent } from 'react-three-game';

// Emit events gameEvents.emit('player:death', { playerId: 'p1', cause: 'lava' }); gameEvents.emit('score:change', { delta: 100, total: 500 });

// Subscribe (React hook - auto cleanup on unmount) useGameEvent('player:death', (payload) => { showGameOver(payload.cause); }, []);

// Subscribe (manual - returns unsubscribe function) const unsub = gameEvents.on('score:change', (payload) => { updateUI(payload.total); }); unsub(); // cleanup

Built-in Physics Events

Physics components automatically emit these events:

Event When Payload sensor:enter Something enters a sensor collider { sourceEntityId, targetEntityId, targetRigidBody } sensor:exit Something exits a sensor collider { sourceEntityId, targetEntityId, targetRigidBody } collision:enter A collision starts { sourceEntityId, targetEntityId, targetRigidBody } collision:exit A collision ends { sourceEntityId, targetEntityId, targetRigidBody }

Collision filtering: By default, kinematic/fixed bodies don't detect each other. For kinematic sensors or projectiles to detect walls/floors, add "activeCollisionTypes": "all" to the Physics properties.

See Advanced Physics for sensor setup and collision handling patterns.

TypeScript: Typed Custom Events

Extend GameEventMap for type-safe custom events:

declare module 'react-three-game' { interface GameEventMap { 'player:death': { playerId: string; cause: string }; 'score:change': { delta: number; total: number }; 'level:complete': { levelId: number; time: number }; } }

Common Patterns // Gameplay controller function GameController() { const [score, setScore] = useState(0);

useGameEvent('score:change', ({ total }) => setScore(total), []); useGameEvent('player:death', () => setGameOver(true), []);

return ; }

// Pickup system useGameEvent('sensor:enter', (payload) => { if (payload.sourceEntityId.startsWith('coin-')) { gameEvents.emit('score:change', { delta: 10, total: score + 10 }); removeEntity(payload.sourceEntityId); } }, [score]);

返回排行榜