npx skills add https://github.com/opusgamelabs/game-creator --skill threejs-game你是一位专业的 Three.js 游戏开发者。在构建 3D 浏览器游戏时,请遵循以下经过验证的模式。
参考:关于官方的 Three.js LLM 文档,请参阅
reference/llms.txt(快速指南)和reference/llms-full.txt(完整 API + TSL)。当这些文件中的模式与本技能冲突时,优先采用文件中的模式。
详细参考请查阅本目录下的配套文件:
core-patterns.md — 完整的 EventBus、GameState、Constants 和 Game.js 协调器代码tsl-guide.md — Three.js 着色语言参考(NodeMaterial 类,何时使用 TSL)input-patterns.md — 陀螺仪输入、虚拟摇杆、统一的模拟 InputSystem、输入优先级系统广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
three@0.183.0+, ESM 导入)搭建新的 Three.js 游戏时:
mkdir <game-name> && cd <game-name>
npm init -y
npm install three@^0.183.0
npm install -D vite
创建 vite.config.js:
import { defineConfig } from 'vite';
export default defineConfig({
root: '.',
publicDir: 'public',
server: { port: 3000, open: true },
build: { outDir: 'dist' },
});
添加到 package.json 的脚本中:
{
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.183.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.183.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>
当发布单个 HTML 文件且无需构建工具时,使用导入映射。在导入映射的 URL 中固定版本号。
每个 Three.js 游戏必须使用以下目录结构:
src/
├── core/
│ ├── Game.js # 主协调器 - 初始化系统,渲染循环
│ ├── EventBus.js # 单例发布/订阅,用于所有模块间通信
│ ├── GameState.js # 集中式状态单例
│ └── Constants.js # 所有配置值、平衡数值、资源路径
├── systems/ # 底层引擎系统
│ ├── InputSystem.js # 键盘/鼠标/手柄输入
│ ├── PhysicsSystem.js # 碰撞检测
│ └── ... # 音频、粒子等
├── gameplay/ # 游戏机制
│ └── ... # 玩家、敌人、武器等
├── level/ # 关卡/世界构建
│ ├── LevelBuilder.js # 构建游戏世界
│ └── AssetLoader.js # 加载模型、纹理、音频
├── ui/ # 用户界面
│ └── ... # 游戏结束、覆盖层
└── main.js # 入口点 - 创建 Game 实例
GameState.reset() 必须能恢复到干净的状态。清理时释放几何体/材质/纹理。重启之间不能有陈旧的引用或泄漏的监听器。每个 Three.js 游戏都需要以下四个核心模块。完整的实现代码在 core-patterns.md 中。
所有模块间通信都通过 EventBus (core/EventBus.js) 进行。模块之间绝不直接导入进行通信。提供 on、once、off、emit 和 clear 方法。事件使用 domain:action 命名(例如 player:hit、game:over)。完整实现请参阅 core-patterns.md。
一个单例 (core/GameState.js) 保存所有游戏状态。系统从中读取,事件更新它。必须包含一个 reset() 方法,用于在重启时恢复到干净的状态。完整实现请参阅 core-patterns.md。
所有魔法数字、平衡值、资源路径和配置都放在 core/Constants.js 中。绝不要在游戏逻辑中硬编码值。按领域组织:PLAYER_CONFIG、ENEMY_CONFIG、WORLD、CAMERA、COLORS、ASSET_PATHS。完整实现请参阅 core-patterns.md。
Game 类 (core/Game.js) 初始化所有内容并运行渲染循环。使用 renderer.setAnimationLoop() —— 这是官方的 Three.js 模式(正确处理 WebGPU 异步,并在标签页隐藏时暂停)。在 init() 中设置渲染器、场景、相机、系统、UI 和事件监听器。完整实现请参阅 core-patterns.md。
最大程度的浏览器兼容性。成熟稳定,大多数示例和教程都使用它。我们的模板默认使用 WebGLRenderer。
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
需要自定义基于节点的材质 (TSL)、计算着色器和高级渲染时使用。注意:导入路径改为 'three/webgpu' 且初始化是异步的。
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
何时选择 WebGPU:你需要 TSL 自定义着色器、计算着色器或基于节点的材质。否则,坚持使用 WebGL。TSL 详情请参阅 tsl-guide.md。
Play.fun SDK 在 top: 0; z-index: 9999 处渲染一个 75px 的固定 iframe。所有 HTML 覆盖层 UI(游戏结束画面、菜单、按钮、文本)都必须考虑到这一点。
// 在 Constants.js 中
export const SAFE_ZONE = {
TOP_PX: 75, // 像素 — 用于 CSS/HTML 覆盖层
TOP_PERCENT: 8, // 视口高度的百分比
};
所有 .overlay 元素(游戏结束、暂停、菜单)必须包含内边距以避开小部件:
.overlay {
padding-top: max(20px, 8vh); /* Play.fun 小部件栏的安全区域 */
}
注意:3D 画布本身在小部件后面渲染,这没问题——只有 HTML 覆盖层 UI 需要安全区域偏移。游戏世界内的 3D 元素(HUD 纹理、浮动文本)应避开屏幕顶部 8% 的空间。
renderer.setAnimationLoop() 而不是手动的 requestAnimationFrame。它会在标签页隐藏时暂停,并正确处理 WebGPU 异步。Math.min(clock.getDelta(), 0.1) 以防止死亡螺旋renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) — 避免在高 DPI 屏幕上 GPU 过载Vector3、Box3、临时对象以最小化 GC。避免每帧分配——预分配并重用。MeshBasicMaterial 或 MeshStandardMaterial。除非明确需要,否则避免使用 MeshPhysicalMaterial、自定义着色器或复杂的材质设置。powerPreference: 'high-performance'.dispose()/public/ 目录下供 Vite 使用three/addons 的 THREE.TextureLoader、GLTFLoaderimport { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
function loadModel(path) {
return new Promise((resolve, reject) => {
loader.load(
path,
(gltf) => resolve(gltf.scene),
undefined,
(error) => reject(error),
);
});
}
除非明确指定,否则所有游戏必须在桌面和移动设备上都能运行。在进行权衡时,分配 60% 的精力给移动端 / 40% 给桌面端。为每个游戏概念选择最佳的移动端输入:
| 游戏类型 | 主要移动端输入 | 备用方案 |
|---|---|---|
| 弹珠/倾斜/平衡 | 陀螺仪 (DeviceOrientation) | 虚拟摇杆 |
| 跑酷/无尽 | 点击区域(左/右半屏) | 滑动手势 |
| 解谜/回合制 | 点击目标(最小 44px) | 拖放 |
| 射击/瞄准 | 虚拟摇杆 + 点击开火 | 双摇杆 |
| 平台跳跃 | 虚拟方向键 + 跳跃按钮 | 倾斜移动 |
使用一个专用的 InputSystem,将键盘、陀螺仪和触摸输入合并为一个单一的模拟接口。游戏逻辑读取 moveX/moveZ (-1..1) 并且不知道来源。键盘输入始终作为覆盖层处于活动状态;在移动设备上,系统初始化陀螺仪(需要 iOS 13+ 权限请求)或回退到虚拟摇杆。完整实现请参阅 input-patterns.md,包括 GyroscopeInput、VirtualJoystick 和输入优先级模式。
src/ 子目录中创建一个新模块EventBus.js 的 Events 对象中使用 domain:action 命名定义新事件Constants.jsGameState.jsGame.js 协调器中连接它在认为游戏完成之前,请验证:
GameState.reset() 恢复到干净的状态,所有 Three.js 资源已释放Math.min(clock.getDelta(), 0.1)isMuted 状态被遵守padding-top: max(20px, 8vh) 以适应 Play.fun 小部件(顶部 75px)npm run build 成功且无错误每周安装次数
89
代码仓库
GitHub 星标数
26
首次出现
2026年2月21日
安全审计
安装于
claude-code77
opencode45
github-copilot44
gemini-cli44
codex44
kimi-cli44
You are an expert Three.js game developer. Follow these opinionated patterns when building 3D browser games.
Reference : See
reference/llms.txt(quick guide) andreference/llms-full.txt(full API + TSL) for official Three.js LLM documentation. Prefer patterns from those files when they conflict with this skill.
For detailed reference, see companion files in this directory:
core-patterns.md — Full EventBus, GameState, Constants, and Game.js orchestrator codetsl-guide.md — Three.js Shading Language reference (NodeMaterial classes, when to use TSL)input-patterns.md — Gyroscope input, virtual joystick, unified analog InputSystem, input priority systemthree@0.183.0+, ESM imports)When scaffolding a new Three.js game:
mkdir <game-name> && cd <game-name>
npm init -y
npm install three@^0.183.0
npm install -D vite
Create vite.config.js:
import { defineConfig } from 'vite';
export default defineConfig({
root: '.',
publicDir: 'public',
server: { port: 3000, open: true },
build: { outDir: 'dist' },
});
Add to package.json scripts:
{
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.183.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.183.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
</script>
Use import maps when shipping a single HTML file with no build tooling. Pin the version in the import map URL.
Every Three.js game MUST use this directory structure:
src/
├── core/
│ ├── Game.js # Main orchestrator - init systems, render loop
│ ├── EventBus.js # Singleton pub/sub for all module communication
│ ├── GameState.js # Centralized state singleton
│ └── Constants.js # ALL config values, balance numbers, asset paths
├── systems/ # Low-level engine systems
│ ├── InputSystem.js # Keyboard/mouse/gamepad input
│ ├── PhysicsSystem.js # Collision detection
│ └── ... # Audio, particles, etc.
├── gameplay/ # Game mechanics
│ └── ... # Player, enemies, weapons, etc.
├── level/ # Level/world building
│ ├── LevelBuilder.js # Constructs the game world
│ └── AssetLoader.js # Loads models, textures, audio
├── ui/ # User interface
│ └── ... # Game over, overlays
└── main.js # Entry point - creates Game instance
GameState.reset() must restore a clean slate. Dispose geometries/materials/textures on cleanup. No stale references or leaked listeners across restarts.Every Three.js game requires these four core modules. Full implementation code is in core-patterns.md.
ALL inter-module communication goes through an EventBus (core/EventBus.js). Modules never import each other directly for communication. Provides on, once, off, emit, and clear methods. Events use domain:action naming (e.g., player:hit, game:over). See core-patterns.md for the full implementation.
One singleton (core/GameState.js) holds ALL game state. Systems read from it, events update it. Must include a reset() method that restores a clean slate for restarts. See core-patterns.md for the full implementation.
Every magic number, balance value, asset path, and configuration goes in core/Constants.js. Never hardcode values in game logic. Organize by domain: PLAYER_CONFIG, ENEMY_CONFIG, WORLD, CAMERA, COLORS, ASSET_PATHS. See core-patterns.md for the full implementation.
The Game class (core/Game.js) initializes everything and runs the render loop. Uses renderer.setAnimationLoop() -- the official Three.js pattern (handles WebGPU async correctly and pauses when the tab is hidden). Sets up renderer, scene, camera, systems, UI, and event listeners in init(). See core-patterns.md for the full implementation.
Maximum browser compatibility. Well-established, most examples and tutorials use this. Our templates default to WebGLRenderer.
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
Required for custom node-based materials (TSL), compute shaders, and advanced rendering. Note: import path changes to 'three/webgpu' and init is async.
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init();
When to pick WebGPU : You need TSL custom shaders, compute shaders, or node-based materials. Otherwise, stick with WebGL. See tsl-guide.md for TSL details.
The Play.fun SDK renders a 75px fixed iframe at top: 0; z-index: 9999. All HTML overlay UI (game-over screens, menus, buttons, text) must account for this.
// In Constants.js
export const SAFE_ZONE = {
TOP_PX: 75, // pixels — use for CSS/HTML overlays
TOP_PERCENT: 8, // percent of viewport height
};
All .overlay elements (game-over, pause, menus) must include padding to avoid the widget:
.overlay {
padding-top: max(20px, 8vh); /* Safe zone for Play.fun widget bar */
}
Note : The 3D canvas itself renders behind the widget, which is fine — only HTML overlay UI needs the safe zone offset. In-world 3D elements (HUD textures, floating text) should avoid the top 8% of screen space.
renderer.setAnimationLoop() instead of manual requestAnimationFrame. It pauses when the tab is hidden and handles WebGPU async correctly.Math.min(clock.getDelta(), 0.1) to prevent death spiralsrenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) — avoids GPU overload on high-DPI screensVector3, Box3, temp objects in hot loops to minimize GC. Avoid per-frame allocations — preallocate and reuse.Place static assets in /public/ for Vite
Use GLB format for 3D models (smaller, single file)
Use THREE.TextureLoader, GLTFLoader from three/addons
Show loading progress via callbacks to UI
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
function loadModel(path) { return new Promise((resolve, reject) => { loader.load( path, (gltf) => resolve(gltf.scene), undefined, (error) => reject(error), ); }); }
All games MUST work on desktop AND mobile unless explicitly specified otherwise. Allocate 60% effort to mobile / 40% desktop when making tradeoffs. Choose the best mobile input for each game concept:
| Game Type | Primary Mobile Input | Fallback |
|---|---|---|
| Marble/tilt/balance | Gyroscope (DeviceOrientation) | Virtual joystick |
| Runner/endless | Tap zones (left/right half) | Swipe gestures |
| Puzzle/turn-based | Tap targets (44px min) | Drag & drop |
| Shooter/aim | Virtual joystick + tap-to-fire | Dual joysticks |
| Platformer | Virtual D-pad + jump button | Tilt for movement |
Use a dedicated InputSystem that merges keyboard, gyroscope, and touch into a single analog interface. Game logic reads moveX/moveZ (-1..1) and never knows the source. Keyboard input is always active as an override; on mobile, the system initializes gyroscope (with iOS 13+ permission request) or falls back to a virtual joystick. See input-patterns.md for the full implementation, including GyroscopeInput, VirtualJoystick, and input priority patterns.
src/ subdirectoryEventBus.js Events object using domain:action namingConstants.jsGameState.js if neededGame.js orchestratorBefore considering a game complete, verify:
GameState.reset() restores a clean slate, all Three.js resources disposedMath.min(clock.getDelta(), 0.1) on every frameisMuted state is respectedpadding-top: max(20px, 8vh) for Play.fun widget (75px at top)Weekly Installs
89
Repository
GitHub Stars
26
First Seen
Feb 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
claude-code77
opencode45
github-copilot44
gemini-cli44
codex44
kimi-cli44
2025 Node.js 最佳实践指南:框架选择、架构原则与错误处理
5,100 周安装
jQuery 4.0 迁移指南:破坏性变更、升级步骤与兼容性解决方案
232 周安装
应用程序性能优化全栈指南:从分析、数据库到前端与CDN的端到端优化
236 周安装
React/React Native 组件脚手架生成器 - 自动化创建生产级组件
236 周安装
RWKV架构详解:融合Transformer与RNN优势的高效AI模型安装与使用指南
70 周安装
Hugging Face Jobs:云端运行AI工作负载,无需本地GPU,支持数据处理、批量推理和模型训练
232 周安装
React/Next.js 高级质量保证工具:自动化测试、覆盖率分析与E2E测试脚手架
232 周安装
MeshBasicMaterialMeshStandardMaterialMeshPhysicalMaterialpowerPreference: 'high-performance' on the renderer.dispose() on geometries, materials, textures when removing objectsnpm run build succeeds with no errors