跳到正文
cccskills
免费在 GitHub 公开

web-3d-threejs-babylon

Use when building a 3D web game with three.js, Babylon.js or PlayCanvas - render loop and delta per library, fixed-step physics with Rapier or Havok, explicit dispose of GPU resources, instancing and draw-call budgets, glTF with Draco/meshopt/KTX2, pixel ratio and resize, WebGPU opt-in, React Three Fiber notes, and headless testing.

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md6.4 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Web 3D: three.js, Babylon.js, PlayCanvas

Overview

three.js is a rendering library with a huge ecosystem; Babylon.js is a full engine (physics plugin, GUI, inspector, a NullEngine for headless tests); PlayCanvas is an engine built around its hosted editor, also usable engine-only. All three hand you the GPU directly — and with it the job of freeing what you allocate, keeping draw calls low, and staying inside a mobile browser's memory.

Core principle: Simulate on a fixed step, render every frame, dispose every GPU resource you create, and count draw calls.

The loop and delta

// three.js — Timer replaces the deprecated Clock (r183+); setAnimationLoop also drives WebXR
const timer = new THREE.Timer();
timer.connect(document);                         // ignores the huge delta after a hidden tab
renderer.setAnimationLoop((t) => {
  timer.update(t);
  const dt = Math.min(timer.getDelta(), 0.25);   // seconds
  stepFixed(dt);                                 // accumulator: physics + rules at 1/60 (game-loop-and-time)
  renderer.render(scene, camera);
});
// Babylon.js — getDeltaTime() is milliseconds
scene.onBeforeRenderObservable.add(() => stepFixed(Math.min(engine.getDeltaTime() / 1000, 0.25)));
engine.runRenderLoop(() => scene.render());
window.addEventListener("resize", () => engine.resize());
// PlayCanvas (engine API / ESM scripts) — dt in seconds
export class Spin extends Script {
  static scriptName = "spin";
  speed = 90;
  update(dt: number) { this.entity.rotate(0, this.speed * dt, 0); }
}

Physics on a fixed step

  • Rapier (WASM, three.js or Babylon): await RAPIER.init(), world.timestep = 1 / 60, call world.step() from the accumulator, copy body transforms to meshes with interpolation. The deterministic build exists for lockstep/rollback.
  • Babylon: Havok via the physics V2 plugin (new HavokPlugin(true, await HavokPhysics())); keep its fixed timestep consistent across devices.
  • PlayCanvas: its rigidbody components (ammo.js) step with the app.
  • Never move a dynamic body by writing mesh.position; set velocities/impulses or use kinematic bodies.

Dispose what you create

// ❌ removing from the scene does not free GPU memory
scene.remove(level);

// ✅ free geometry, materials and textures explicitly (unless shared with something still alive)
level.traverse((o) => {
  if (o instanceof THREE.Mesh) {
    o.geometry.dispose();
    for (const m of Array.isArray(o.material) ? o.material : [o.material]) {
      for (const v of Object.values(m)) if (v instanceof THREE.Texture) v.dispose();
      m.dispose();
    }
  }
});
scene.remove(level);
console.assert(renderer.info.memory.geometries <= baseline.geometries);
  • three.js: also dispose render targets, PMREMGenerator outputs, and the renderer itself on teardown. renderer.info.memory before and after a level change is the leak test.
  • Babylon: mesh.dispose(false, true) frees materials and textures too; load levels into an AssetContainer and dispose() it on exit; scene.dispose() / engine.dispose() on teardown.
  • PlayCanvas: entity.destroy(), and asset.unload() for assets no longer used.

Draw calls and instancing

  • Many copies of one mesh: THREE.InstancedMesh (setMatrixAt + instanceMatrix.needsUpdate = true, count for the live number) or Babylon thin instances (mesh.thinInstanceAdd(matrix)).
  • Merge static scenery (BufferGeometryUtils.mergeGeometries, Babylon Mesh.MergeMeshes) and share materials.
  • Budget draw calls per frame — a few hundred for mobile browsers, more on desktop — and read the actual number (renderer.info.render.calls, Babylon Inspector's statistics) before and after.
  • Shadows are expensive: one shadow-casting light, tight shadow camera bounds, castShadow only where it shows; bake lighting for static scenes.
  • Cap the pixel ratio: renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); resize from the canvas size, not the window, with a ResizeObserver.

Assets

  • glTF/GLB with Draco or meshopt geometry compression and KTX2 textures: three.js GLTFLoader + DRACOLoader / MeshoptDecoder + KTX2Loader (decoder files served from the app's own path); Babylon's glTF loader with its Draco/KTX2 support; LoadAssetContainerAsync to keep a level's assets disposable together.
  • Optimise models offline (npx @gltf-transform/cli optimize) and keep originals out of the shipped bundle (game-asset-pipeline).

WebGPU

three.js WebGPURenderer (from three/webgpu, falling back to WebGL2) and Babylon WebGPUEngine are usable; adopt them only when the repository or task opts in — shader code, post-processing and some materials differ between the paths.

React Three Fiber (if the repo uses it)

useFrame((state, delta) => ...) mutates refs; never setState per frame. Dispose is automatic for objects R3F created declaratively, not for ones you created imperatively. <Instances> / <instancedMesh> for repetition.

Testing

  • Rules and simulation: plain TS with Vitest, no renderer import.
  • Babylon: new NullEngine() + new Scene(engine) runs scene logic headless in Vitest (no pixels).
  • three.js: scene-graph logic can be tested without a renderer; rendering is checked with the seeded Playwright smoke run and screenshots (game-visual-self-review). Headless Chromium uses a software GL — correct images, meaningless frame rates.

Common Mistakes

  • scene.remove without dispose — memory grows every level.
  • Allocating new THREE.Vector3() in the frame loop instead of reusing module-level temporaries.
  • Full device pixel ratio on 3× phones.
  • One mesh per bullet or tree instead of instancing.
  • THREE.Clock in new code on a revision that deprecated it.

Red Flags

  • No dispose() call anywhere in a project that loads more than one level.
  • renderer.info.render.calls in the thousands on a mobile target.
  • Physics stepped with the raw render delta.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月18日 更新

查看 makifbaysal 的全部技能

报告此技能的问题