create-playcanvas 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +42 -25
  2. package/dist/index.mjs +49 -46
  3. package/package.json +3 -1
  4. package/skills/add-effects/SKILL.md +81 -0
  5. package/skills/apply-conventions/SKILL.md +73 -0
  6. package/skills/assemble-scene/SKILL.md +53 -0
  7. package/skills/assemble-scene/references/direct-engine.md +16 -0
  8. package/skills/assemble-scene/references/react.md +24 -0
  9. package/skills/assemble-scene/references/web-components.md +19 -0
  10. package/skills/build-app/SKILL.md +41 -0
  11. package/skills/build-app/references/direct-engine.md +12 -0
  12. package/skills/build-app/references/react.md +20 -0
  13. package/skills/build-app/references/web-components.md +55 -0
  14. package/skills/build-hud/SKILL.md +36 -0
  15. package/skills/calibrate-model/SKILL.md +65 -0
  16. package/skills/calibrate-model/references/direct-engine.md +22 -0
  17. package/skills/calibrate-model/references/react.md +27 -0
  18. package/skills/calibrate-model/references/web-components.md +19 -0
  19. package/skills/configure-animation/SKILL.md +50 -0
  20. package/skills/configure-animation/references/direct-engine.md +32 -0
  21. package/skills/configure-animation/references/react.md +32 -0
  22. package/skills/configure-animation/references/web-components.md +13 -0
  23. package/skills/find-examples/SKILL.md +79 -0
  24. package/skills/find-examples/references/direct-engine.md +15 -0
  25. package/skills/find-examples/references/react.md +17 -0
  26. package/skills/find-examples/references/web-components.md +17 -0
  27. package/skills/inspect-glb/SKILL.md +72 -0
  28. package/skills/inspect-glb/scripts/inspect.mjs +239 -0
  29. package/skills/light-scene/SKILL.md +84 -0
  30. package/skills/manage-game-state/SKILL.md +48 -0
  31. package/skills/reuse-scripts/SKILL.md +59 -0
  32. package/skills/reuse-scripts/references/direct-engine.md +41 -0
  33. package/skills/reuse-scripts/references/react.md +28 -0
  34. package/skills/reuse-scripts/references/web-components.md +27 -0
  35. package/templates/_shared/model-viewer/public/playcanvas-cube.glb +0 -0
  36. package/templates/engine/base/.prettierignore +2 -0
  37. package/templates/{vanilla-ts → engine/base}/README.md +7 -19
  38. package/templates/engine/base/eslint.config.mjs +3 -0
  39. package/templates/{vanilla-ts → engine/base}/src/vite-env.d.ts +6 -0
  40. package/templates/{vanilla-ts → engine/interactive-sphere}/src/main.ts +1 -1
  41. package/templates/{vanilla-ts → engine/interactive-sphere}/src/style.css +1 -1
  42. package/templates/{vanilla-ts → engine/interactive-sphere}/src/utils.ts +0 -1
  43. package/templates/engine/model-viewer/index.html +13 -0
  44. package/templates/engine/model-viewer/src/main.ts +81 -0
  45. package/templates/engine/model-viewer/src/style.css +8 -0
  46. package/templates/engine/spinning-cube/index.html +13 -0
  47. package/templates/engine/spinning-cube/src/main.ts +65 -0
  48. package/templates/engine/spinning-cube/src/style.css +8 -0
  49. package/templates/engine/splat-viewer/index.html +13 -0
  50. package/templates/engine/splat-viewer/src/main.ts +74 -0
  51. package/templates/engine/splat-viewer/src/style.css +8 -0
  52. package/templates/index.ts +37 -5
  53. package/templates/react/base/.prettierignore +2 -0
  54. package/templates/{react-ts → react/base}/README.md +6 -19
  55. package/templates/{react-ts → react/base}/eslint.config.mjs +1 -0
  56. package/templates/{react-ts → react/base}/src/vite-env.d.ts +6 -0
  57. package/templates/{react-ts → react/interactive-sphere}/src/Scene.tsx +1 -2
  58. package/templates/react/model-viewer/index.html +13 -0
  59. package/templates/react/model-viewer/src/App.tsx +43 -0
  60. package/templates/react/model-viewer/src/index.css +9 -0
  61. package/templates/react/model-viewer/src/main.tsx +11 -0
  62. package/templates/react/spinning-cube/index.html +13 -0
  63. package/templates/react/spinning-cube/src/App.tsx +36 -0
  64. package/templates/react/spinning-cube/src/index.css +9 -0
  65. package/templates/react/spinning-cube/src/main.tsx +11 -0
  66. package/templates/react/splat-viewer/index.html +13 -0
  67. package/templates/react/splat-viewer/src/App.tsx +39 -0
  68. package/templates/react/splat-viewer/src/index.css +9 -0
  69. package/templates/react/splat-viewer/src/main.tsx +11 -0
  70. package/templates/web-components/base/.prettierignore +2 -0
  71. package/templates/web-components/base/README.md +70 -0
  72. package/templates/web-components/base/_gitignore +24 -0
  73. package/templates/web-components/base/eslint.config.mjs +3 -0
  74. package/templates/web-components/base/package.json +31 -0
  75. package/templates/web-components/base/prettier.config.mjs +1 -0
  76. package/templates/web-components/base/src/assets/playcanvas.png +0 -0
  77. package/templates/web-components/base/src/vite-env.d.ts +19 -0
  78. package/templates/web-components/base/tsconfig.json +25 -0
  79. package/templates/web-components/interactive-sphere/index.html +53 -0
  80. package/templates/web-components/interactive-sphere/src/assets/typescript.svg +1 -0
  81. package/templates/web-components/interactive-sphere/src/main.ts +76 -0
  82. package/templates/web-components/interactive-sphere/src/style.css +111 -0
  83. package/templates/web-components/model-viewer/index.html +34 -0
  84. package/templates/web-components/model-viewer/src/main.ts +26 -0
  85. package/templates/web-components/model-viewer/src/style.css +8 -0
  86. package/templates/web-components/spinning-cube/index.html +29 -0
  87. package/templates/web-components/spinning-cube/src/main.ts +20 -0
  88. package/templates/web-components/spinning-cube/src/style.css +8 -0
  89. package/templates/web-components/splat-viewer/index.html +31 -0
  90. package/templates/web-components/splat-viewer/src/main.ts +14 -0
  91. package/templates/web-components/splat-viewer/src/style.css +8 -0
  92. package/templates/vanilla-ts/eslint.config.mjs +0 -3
  93. package/templates/vanilla-ts/public/environment-map.png +0 -0
  94. /package/templates/{react-ts → _shared/interactive-sphere}/public/environment-map.png +0 -0
  95. /package/templates/{react-ts → engine/base}/_gitignore +0 -0
  96. /package/templates/{vanilla-ts → engine/base}/package.json +0 -0
  97. /package/templates/{react-ts → engine/base}/prettier.config.mjs +0 -0
  98. /package/templates/{react-ts → engine/base}/src/assets/playcanvas.png +0 -0
  99. /package/templates/{vanilla-ts → engine/base}/tsconfig.json +0 -0
  100. /package/templates/{vanilla-ts → engine/interactive-sphere}/index.html +0 -0
  101. /package/templates/{vanilla-ts → engine/interactive-sphere}/src/App.ts +0 -0
  102. /package/templates/{vanilla-ts → engine/interactive-sphere}/src/assets/typescript.svg +0 -0
  103. /package/templates/{vanilla-ts → react/base}/_gitignore +0 -0
  104. /package/templates/{react-ts → react/base}/package.json +0 -0
  105. /package/templates/{vanilla-ts → react/base}/prettier.config.mjs +0 -0
  106. /package/templates/{vanilla-ts → react/base}/src/assets/playcanvas.png +0 -0
  107. /package/templates/{react-ts → react/base}/tsconfig.json +0 -0
  108. /package/templates/{react-ts → react/base}/vite.config.ts +0 -0
  109. /package/templates/{react-ts → react/interactive-sphere}/index.html +0 -0
  110. /package/templates/{react-ts → react/interactive-sphere}/src/App.css +0 -0
  111. /package/templates/{react-ts → react/interactive-sphere}/src/App.tsx +0 -0
  112. /package/templates/{react-ts → react/interactive-sphere}/src/assets/react.svg +0 -0
  113. /package/templates/{react-ts → react/interactive-sphere}/src/index.css +0 -0
  114. /package/templates/{react-ts → react/interactive-sphere}/src/main.tsx +0 -0
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: assemble-scene
3
+ description: Use when composing PlayCanvas Engine, React, or Web Components gameplay entities, model hierarchies, colliders, or physics objects to create one semantic root per object with calibrated visuals and predictable lifecycle ownership.
4
+ ---
5
+
6
+ # Scene assembly
7
+
8
+ Represent one gameplay object with one named semantic root. Put gameplay position, heading,
9
+ rigid-body, collision, and behavior on that root. Put calibrated render transforms below it.
10
+
11
+ Consume the existing tuning record from `calibrate-model` exactly once. If it is missing, stop and
12
+ calibrate the model; do not calculate or adjust asset tuning during assembly.
13
+
14
+ ## Assembly rules
15
+
16
+ - Name roots by gameplay role and use predictable names for repeated instances.
17
+ - Apply model calibration once inside the visual child; never reapply scale, grounding, or authored
18
+ yaw during placement.
19
+ - Space neighbouring roots by their scaled footprints and pivot offsets from `calibrate-model`, not
20
+ by pivot position. A non-90° authored yaw rotates a footprint, so reserve its larger horizontal
21
+ extent as a conservative radius.
22
+ - Put rigid-body and collision components on the same root. Size colliders from measured bounds.
23
+ - Use local transforms below a parent and world transforms only for semantic roots.
24
+ - Keep UI, effects, cameras, and lights outside gameplay model hierarchies.
25
+ - Remove the root through its authoring surface's lifecycle. Unregister external events it owns.
26
+ - Do not scale rigid-body roots or skinned bones.
27
+
28
+ ## Prove placement
29
+
30
+ Declare intentional support, attachment, or containment relationships between roots. After a
31
+ rendered frame, measure each relationship at the supported object's actual position. Use a mount
32
+ point, collision ray, or geometry sample; use a support root's AABB maximum only when its top is flat
33
+ across the full footprint. Record the signed contact gap: positive means floating and negative means
34
+ penetration. A relationship name does not exempt the pair from this check.
35
+
36
+ Union descendant world `meshInstance.aabb` values for each semantic root and compare unrelated peer
37
+ roots. Any intersection with positive depth on X, Y, and Z beyond a small tolerance is an unresolved
38
+ placement failure: move the root from its calibrated footprint, or prove the apparent AABB overlap
39
+ is empty geometry from at least two materially different views. Never accept one model inside
40
+ another from a single screenshot.
41
+
42
+ Also check expected root counts, component descendants, aligned render and collider bounds, and
43
+ role-based names. Compare final dimensions with each calibration record's intended size and reference
44
+ object. Inspect composition and facing from images returned into your context by an image-capable
45
+ browser tool. A saved screenshot path, screen coordinate, AABB, or visibility flag is not visual
46
+ evidence. If you cannot inspect the image, report those claims unverified. Hierarchy and placement
47
+ claims require runtime values logged from the application itself, as the `apply-conventions` skill
48
+ describes.
49
+
50
+ Read exactly one reference matching the code being edited:
51
+ [direct Engine](references/direct-engine.md), [React](references/react.md), or
52
+ [Web Components](references/web-components.md). Choose from imports and markup, not installed
53
+ dependencies alone.
@@ -0,0 +1,16 @@
1
+ # Direct Engine projects
2
+
3
+ Create one root and place a previously calibrated model beneath it:
4
+
5
+ ```ts
6
+ import { Entity } from 'playcanvas';
7
+
8
+ const root = new Entity('Enemy_1');
9
+ root.addChild(instance('enemy'));
10
+ root.setPosition(x, 0, z);
11
+ root.setEulerAngles(0, heading, 0);
12
+ app.root.addChild(root);
13
+ ```
14
+
15
+ Add physics and scripts to `root`, not the calibrated child. Move, rotate, enable, disable, and
16
+ destroy the root as one gameplay object.
@@ -0,0 +1,24 @@
1
+ # React projects
2
+
3
+ Represent the complete gameplay object as one component:
4
+
5
+ ```tsx
6
+ import { Entity } from '@playcanvas/react';
7
+ import { Collision, RigidBody } from '@playcanvas/react/components';
8
+
9
+ // Model is the project's calibrated component from calibrate-model, not a library export
10
+ const Enemy = ({ id, position, heading }) => (
11
+ <Entity name={`Enemy_${id}`} position={position} rotation={[0, heading, 0]}>
12
+ <RigidBody type="dynamic" mass={1} />
13
+ <Collision type="capsule" />
14
+ <Model id="enemy" src="/models/Enemy.glb" />
15
+ </Entity>
16
+ );
17
+ ```
18
+
19
+ Use stable React keys when mapping repeated objects. Mount and unmount the component root to own the
20
+ whole Engine hierarchy; do not manually destroy entities created by JSX.
21
+
22
+ Before using physics, set `usePhysics` on `<Application>` and install the `sync-ammo` peer
23
+ dependency. Missing either one leaves `RigidBody` and `Collision` inert with only a console warning,
24
+ so confirm bodies actually move rather than assuming the markup took effect.
@@ -0,0 +1,19 @@
1
+ # Web Components projects
2
+
3
+ Represent the whole gameplay object below one semantic root:
4
+
5
+ ```html
6
+ <pc-entity name="Enemy_1" position="4 0 -3" rotation="0 90 0">
7
+ <pc-rigidbody type="dynamic" mass="1"></pc-rigidbody>
8
+ <pc-collision type="capsule"></pc-collision>
9
+ <pc-entity position="0 0.8 0" rotation="0 180 0" scale="0.01 0.01 0.01">
10
+ <pc-model asset="enemy"></pc-model>
11
+ </pc-entity>
12
+ </pc-entity>
13
+ ```
14
+
15
+ Keep component tags as direct children of the owning `<pc-entity>`. Register the model with a
16
+ direct-child `<pc-asset>` under `<pc-app>`. Before using rigid bodies, load Ammo with a direct-child
17
+ `<pc-module name="Ammo">` carrying its `glue`, `wasm`, and `fallback` attributes; `<pc-app>` awaits
18
+ its direct-child modules before creating the graphics device, so every body in the document depends
19
+ on that one element. Add and remove the semantic root as one DOM subtree.
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: build-app
3
+ description: Use when creating or restructuring a PlayCanvas application with the direct Engine API, @playcanvas/react, or @playcanvas/web-components to choose the active authoring surface and apply its bootstrap, lifecycle, ownership, asset-loading, and Engine interop patterns.
4
+ ---
5
+
6
+ # Build an Engine application
7
+
8
+ Read `package.json` and the application entry point before changing its structure. Choose the
9
+ authoring surface from the code being edited:
10
+
11
+ - read [references/react.md](references/react.md) for `@playcanvas/react` imports and JSX;
12
+ - read [references/web-components.md](references/web-components.md) for `pc-*` elements or
13
+ `@playcanvas/web-components` imports;
14
+ - read [references/direct-engine.md](references/direct-engine.md) for direct `playcanvas` bootstrap
15
+ code without a wrapper-owned lifecycle.
16
+
17
+ Installed dependencies alone are insufficient when a project contains more than one surface.
18
+ Preserve the surface that owns the current entry point and lifecycle.
19
+
20
+ ## Render at an intentional density
21
+
22
+ PlayCanvas defaults `graphicsDevice.maxPixelRatio` to 1 for predictable fill-rate cost. In a
23
+ quality-oriented browser app, set it deliberately after the application exists and before its
24
+ initial automatic resize; `Math.min(window.devicePixelRatio, 2)` is a practical high-DPI starting
25
+ point. Keep 1 only as a measured performance decision, and resize the canvas again after changing
26
+ the value.
27
+
28
+ Verify the real backbuffer in a browser: for automatic resolution, `canvas.width / canvas.clientWidth`
29
+ and the height ratio should match the chosen pixel ratio. CSS dimensions and a screenshot filepath
30
+ do not prove rendering density.
31
+
32
+ For every surface:
33
+
34
+ - verify version-sensitive APIs against installed declarations or source;
35
+ - keep one owner for application creation, assets, entities, update callbacks, and teardown;
36
+ - prefer the surface's declarative or lifecycle primitives before reaching into the Engine;
37
+ - put per-entity and per-frame behavior in an Engine `Script` when page or view state is not its
38
+ natural owner;
39
+ - clamp the per-frame delta before advancing gameplay, timers, physics, or a state clock, so a
40
+ backgrounded tab or a slow frame cannot inject one large integration step;
41
+ - clean up external events and resources in the lifecycle that created them.
@@ -0,0 +1,12 @@
1
+ # Direct Engine applications
2
+
3
+ Keep the project's existing `Application` or `AppBase` bootstrap, graphics-device setup, component
4
+ systems, resource handlers, resize behavior, and teardown. Do not introduce a wrapper lifecycle
5
+ around an established direct Engine application.
6
+
7
+ Use named imports from `playcanvas`. Let the application own the root hierarchy, asset registry,
8
+ update loop, canvas resizing, and destruction. Register loaded assets before attaching them to
9
+ components, and unregister external events when their owning entity or application is destroyed.
10
+
11
+ Put reusable per-entity behavior in an Engine `Script`. Use application update events for small
12
+ application-level loops, and remove each callback during teardown.
@@ -0,0 +1,20 @@
1
+ # React applications
2
+
3
+ Let React own application, asset, entity, and component lifecycles. Use `<Application>`, `<Entity>`,
4
+ exports from `@playcanvas/react/components`, and installed asset hooks instead of creating matching
5
+ Engine objects manually.
6
+
7
+ Respect the subpath split, which is the most common import error: `Application`, `Entity`, and
8
+ `Container` come from the package root, components from `@playcanvas/react/components`, and hooks
9
+ from `@playcanvas/react/hooks`. The package is ESM only.
10
+
11
+ Keep hooks unconditional and asset URLs stable. Mount and unmount JSX to create and destroy the
12
+ owned Engine hierarchy; do not manually destroy entities created by React. Clean up imperative
13
+ resources in the effect that created them.
14
+
15
+ Use `useApp()` or refs only when no declarative component covers the Engine API. Put per-frame work
16
+ in an Engine `Script` or `useAppEvent('update', cb)` instead of React state. Keep `Script` classes at
17
+ module scope so renders do not create new constructor identities.
18
+
19
+ Verify components, hooks, props, and required peer dependencies against the installed
20
+ `@playcanvas/react` declarations before using them.
@@ -0,0 +1,55 @@
1
+ # Web Components applications
2
+
3
+ Install `playcanvas` and `@playcanvas/web-components`, register the component module, and map both
4
+ module specifiers when using import maps. Pin package versions in production CDN URLs; do not use
5
+ `@latest`.
6
+
7
+ ```html
8
+ <script type="importmap">
9
+ {
10
+ "imports": {
11
+ "playcanvas": "/node_modules/playcanvas/build/playcanvas.mjs",
12
+ "@playcanvas/web-components": "/node_modules/@playcanvas/web-components/dist/pwc.mjs"
13
+ }
14
+ }
15
+ </script>
16
+ <script type="module" src="/node_modules/@playcanvas/web-components/dist/pwc.mjs"></script>
17
+ ```
18
+
19
+ Build supported application structure declaratively:
20
+
21
+ ```html
22
+ <pc-app>
23
+ <pc-asset id="ship" src="/models/ship.glb"></pc-asset>
24
+ <pc-scene>
25
+ <pc-entity name="Ship" position="0 0 -4">
26
+ <pc-model asset="ship"></pc-model>
27
+ </pc-entity>
28
+ </pc-scene>
29
+ </pc-app>
30
+ ```
31
+
32
+ - Put assets directly below `<pc-app>` and component elements directly below their `<pc-entity>`.
33
+ - Give every `pc-*` element an explicit closing tag; self-closing syntax is unsupported.
34
+ - Write vectors as space-separated numbers, rotations in degrees, and enums by name.
35
+ - Treat a present boolean attribute as true unless its value is `false`; remove it to restore the
36
+ Engine default.
37
+
38
+ Load Engine `Script` modules with `<pc-asset>`, then attach them through `<pc-scripts>` and
39
+ `<pc-script>`. Match the element's `name` to the class's static `scriptName`. Extra kebab-case
40
+ attributes map to camelCase properties; use the `attributes` JSON attribute for nested or reserved
41
+ values.
42
+
43
+ Use `whenReady` for occasional page-level Engine access:
44
+
45
+ ```js
46
+ import { whenReady } from '@playcanvas/web-components';
47
+
48
+ const { app } = await whenReady('pc-app');
49
+ ```
50
+
51
+ Use element properties such as `.app`, `.entity`, `.scene`, and `.component` only when no
52
+ declarative element covers the API. Put per-entity and per-frame behavior in an Engine `Script`, not
53
+ page glue.
54
+
55
+ Verify elements and attributes against the installed `@playcanvas/web-components` declarations.
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: build-hud
3
+ description: Use when building a PlayCanvas game's on-screen interface such as ready, pause, and victory overlays, menus, and live indicators like gauges, timers, or reload meters, so the interface is driven by game state, readable over the scene, and separate from the 3D hierarchy.
4
+ ---
5
+
6
+ # Game interface
7
+
8
+ A HUD is a view of game state, not a source of it. Render it from the current state each frame or on
9
+ each transition; never store gameplay truth in the DOM or a UI element.
10
+
11
+ ## Choose the UI layer
12
+
13
+ - Build the interface in the natural UI layer for the surface — a DOM or React overlay, or the
14
+ Engine `screen` and `element` UI — decided with the `build-app` skill. Keep it outside gameplay
15
+ model hierarchies, as `assemble-scene` requires.
16
+ - For a screen-space Engine UI, adapt a user-interface example with `find-examples` rather than
17
+ guessing element anchoring, scaling, and font handling.
18
+
19
+ ## Drive from state
20
+
21
+ - Map each overlay to a game state and each indicator to a snapshot field. A transition shows or
22
+ hides overlays; it does not duplicate the state the game already owns (see `manage-game-state`).
23
+ - Anchor and scale elements to the viewport so the layout holds at the target resolution and on
24
+ resize. Verify at the acceptance resolution, not the default window size.
25
+ - Keep copy minimal and legible over the scene: back text with a plate or shadow where the frame is
26
+ bright, and unify interface colour with the scene's grade from `light-scene`.
27
+
28
+ ## Prove it reflects state
29
+
30
+ Drive real input through every transition and confirm with screenshots that the correct overlay
31
+ shows and each indicator tracks the snapshot — a reload meter empties and refills, a timer advances,
32
+ a menu appears only in its state. A saved filepath or a visibility flag is not visual evidence;
33
+ inspect the returned pixels as the local agent guide requires.
34
+
35
+ Choose the authoring surface with the `build-app` skill and build the interface through its own UI
36
+ primitives before reaching into the Engine.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: calibrate-model
3
+ description: Use before repeatedly placing GLB models in PlayCanvas Engine, React, or Web Components to measure each asset once and record its uniform scale, grounding offset, and yaw correction in a stable nested transform.
4
+ ---
5
+
6
+ # Model calibration
7
+
8
+ Use the `inspect-glb` skill to measure every unique GLB before mass placement. Rely on `dims` and
9
+ `groundOffset` for contact-accurate placement only when it reports `boundsSource: vertices`. Store
10
+ one tuning record per asset:
11
+
12
+ ```ts
13
+ const ASSET_TUNING = {
14
+ model: {
15
+ boundsSource: 'vertices',
16
+ aabb: { min: [-4, -1, -9], max: [4, 6, 9] },
17
+ dims: [8, 7, 18],
18
+ center: [0, 2.5, 0],
19
+ groundOffset: 1,
20
+ intended: { dimension: 'length', size: 18 },
21
+ scale: 0.9,
22
+ y: 0.9,
23
+ yaw: 180
24
+ }
25
+ } as const;
26
+ ```
27
+
28
+ ## Calculate the record
29
+
30
+ 1. Pick and record the intended dimension and size: character height, building footprint, or
31
+ vehicle length. Base it on world units or an already calibrated reference model.
32
+ 2. Calculate `scale = intendedSize / measuredDimension`.
33
+ 3. For a floor-resting model, calculate `y = groundOffset * scale`. Record a deliberate offset for
34
+ waterlines, embedded objects, or airborne models.
35
+ 4. Confirm directional facing once in the running app, as the `apply-conventions` skill describes.
36
+ PlayCanvas entities face -Z while glTF convention is +Z, but asset packs vary.
37
+ 5. Retain `boundsSource`, `aabb`, `dims`, `center`, `groundOffset`, and the intended size with
38
+ `{ scale, y, yaw }`. Use the scaled footprint and centre for initial spacing; do not re-derive or
39
+ add per-instance nudges.
40
+
41
+ Keep gameplay position and heading on an outer semantic root, and seat the model on one predictable
42
+ reference point beneath it so a root position means the same thing for every asset: by default the
43
+ footprint centre over the base. Apply the authored yaw on a wrapper, then the scale and the full
44
+ offset on the render child inside it, so an off-centre pivot is compensated in the authored frame and
45
+ never re-rotated by the yaw or by gameplay heading:
46
+
47
+ ```ts
48
+ const yaw = new Entity('yaw');
49
+ yaw.setLocalEulerAngles(0, t.yaw, 0);
50
+ const visual = instantiate(asset);
51
+ visual.setLocalScale(t.scale, t.scale, t.scale);
52
+ visual.setLocalPosition(-t.center[0] * t.scale, t.y, -t.center[2] * t.scale);
53
+ yaw.addChild(visual);
54
+ root.addChild(yaw);
55
+ ```
56
+
57
+ Keep `y` as the local correction that brings the measured minimum to the root plane; the X and Z
58
+ terms bring the footprint centre onto the root axis. Place that root at a measured support point. A support name or global AABB maximum is not
59
+ a surface measurement. Use an authored mount point or runtime support query for curved or stepped
60
+ geometry. Treat skinned bounds as bind-pose estimates and confirm foot contact in the active poses.
61
+
62
+ Read exactly one reference matching the code being edited:
63
+ [direct Engine](references/direct-engine.md), [React](references/react.md), or
64
+ [Web Components](references/web-components.md). Choose from imports and markup, not installed
65
+ dependencies alone.
@@ -0,0 +1,22 @@
1
+ # Direct Engine projects
2
+
3
+ Return a calibrated pivot whose parent can be placed without knowing how the asset was authored:
4
+
5
+ ```ts
6
+ import { Entity } from 'playcanvas';
7
+
8
+ const instance = (name: keyof typeof ASSET_TUNING) => {
9
+ const t = ASSET_TUNING[name];
10
+ const model = containers[name].resource.instantiateRenderEntity({
11
+ castShadows: true
12
+ });
13
+ model.setLocalScale(t.scale, t.scale, t.scale);
14
+ model.setLocalPosition(0, t.y, 0);
15
+ model.setLocalEulerAngles(0, t.yaw, 0);
16
+ const root = new Entity(name);
17
+ root.addChild(model);
18
+ return root;
19
+ };
20
+ ```
21
+
22
+ Only position and rotate the returned root. Keep the calibration inside `instance()`.
@@ -0,0 +1,27 @@
1
+ # React projects
2
+
3
+ Keep semantic transforms on the outer entity and calibration on the rendered child:
4
+
5
+ ```tsx
6
+ import { Entity } from '@playcanvas/react';
7
+ import { Render } from '@playcanvas/react/components';
8
+ import { useModel } from '@playcanvas/react/hooks';
9
+
10
+ const Model = ({ id, src, heading = 0, position = [0, 0, 0] }) => {
11
+ const { asset, error } = useModel(src);
12
+ const t = ASSET_TUNING[id];
13
+ if (error) throw new Error(error);
14
+ if (!asset) return null;
15
+
16
+ return (
17
+ <Entity name={id} position={position} rotation={[0, heading, 0]}>
18
+ <Entity position={[0, t.y, 0]} rotation={[0, t.yaw, 0]} scale={[t.scale, t.scale, t.scale]}>
19
+ <Render type="asset" asset={asset} />
20
+ </Entity>
21
+ </Entity>
22
+ );
23
+ };
24
+ ```
25
+
26
+ Add project-specific prop types without changing the two-level transform structure. Keep hooks
27
+ unconditional and asset URLs stable.
@@ -0,0 +1,19 @@
1
+ # Web Components projects
2
+
3
+ Keep gameplay placement on the outer entity and calibration on a visual child:
4
+
5
+ ```html
6
+ <pc-app>
7
+ <pc-asset id="ship" src="/models/ship.glb"></pc-asset>
8
+ <pc-scene>
9
+ <pc-entity name="Ship" position="12 0 -4" rotation="0 90 0">
10
+ <pc-entity position="0 1.2 0" rotation="0 180 0" scale="0.9 0.9 0.9">
11
+ <pc-model asset="ship"></pc-model>
12
+ </pc-entity>
13
+ </pc-entity>
14
+ </pc-scene>
15
+ </pc-app>
16
+ ```
17
+
18
+ Generate repeated markup from one `{ scale, y, yaw }` record per asset. Keep all vector attributes
19
+ space-separated and use degrees for rotations.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: configure-animation
3
+ description: Use before playing, blending, or retargeting GLB animation clips in PlayCanvas Engine, React, or Web Components to inspect clip and joint names, attach animation to the rendered hierarchy, keep blend inputs valid, and diagnose T-pose, frozen playback, or binding failures.
4
+ ---
5
+
6
+ # Animation setup
7
+
8
+ Use the `inspect-glb` skill first. Never guess clip or joint names.
9
+
10
+ ## Choose the smallest playback model
11
+
12
+ - Use the selected authoring surface's simple playback path for a single clip.
13
+ - Use an `AnimStateGraph` for locomotion blending, transitions, or event-driven actions.
14
+ - Adapt the official animation examples with the `find-examples` skill instead of writing a state
15
+ graph from memory.
16
+
17
+ ## Keep blend inputs valid
18
+
19
+ Clamp every 1D blend parameter to the first and last child points before updating it. PlayCanvas
20
+ does not clamp values outside that span: every child can receive zero weight and synchronized clips
21
+ can receive non-finite speeds, freezing the pose. Exercise the first, interior, and last blend
22
+ points with real gameplay input.
23
+
24
+ ## Retarget only compatible rigs
25
+
26
+ For each source `animationTargets` entry, remove its final `.translation`, `.rotation`, `.scale`, or
27
+ `.weights` suffix and match the remaining path against the destination's `nodePaths`. The top-level
28
+ model root may differ; the remaining parent chain must match. Equal joint counts or similar bone
29
+ names are insufficient. Confirm the result in the running app as the `apply-conventions` skill
30
+ describes; frozen, partial, or exploded motion means the rigs are incompatible.
31
+
32
+ ## Prove playback
33
+
34
+ An assigned clip, active state, or advancing state time does not prove that the skeleton is moving.
35
+ For clips expected to move, sample the local position or rotation of two non-root joints over
36
+ several rendered frames. Require a finite animation speed, active-state time, and progress, and
37
+ confirm pose changes at idle and at both ends of each blend tree. Zero joint motion indicates a
38
+ binding, playback, or blend-weight failure even when reported state names look correct.
39
+
40
+ ## Diagnose failures
41
+
42
+ - A T-pose usually means no clip was assigned, target paths differ, or animation is attached outside
43
+ the rendered hierarchy.
44
+ - Put scale on a model wrapper, not skinned bones.
45
+ - Match playback rate to real movement speed when visible foot sliding matters.
46
+
47
+ Read exactly one reference matching the code being edited:
48
+ [direct Engine](references/direct-engine.md), [React](references/react.md), or
49
+ [Web Components](references/web-components.md). Choose from imports and markup, not installed
50
+ dependencies alone.
@@ -0,0 +1,32 @@
1
+ # Direct Engine projects
2
+
3
+ Use clips exposed by the loaded container resource and attach animation to the entity owning the
4
+ render hierarchy:
5
+
6
+ ```ts
7
+ import type { Asset, ContainerResource } from 'playcanvas';
8
+
9
+ const res = container.resource as ContainerResource;
10
+ const entity = res.instantiateRenderEntity();
11
+ entity.addComponent('anim', { activate: true });
12
+ const clip = (res.animations as Asset[]).find((asset) => asset.name.includes('Idle'));
13
+ if (!clip) throw new Error(`no Idle clip; have ${res.animations.map((asset) => asset.name).join(', ')}`);
14
+ entity.anim?.assignAnimation('Idle', clip.resource);
15
+ ```
16
+
17
+ Fail at the missing clip instead of optional-chaining past it. A silent missing assignment appears
18
+ later as a T-pose with little diagnostic context.
19
+
20
+ Place the anim component on or above the complete skinned render hierarchy before it initializes.
21
+ Attaching it to an unrelated pivot does not bind the clip.
22
+
23
+ Clamp 1D state-graph parameters to their authored child range. For children at `0.5`, `3.5`, and
24
+ `8`, do not pass an unclamped movement speed:
25
+
26
+ ```ts
27
+ entity.anim?.setFloat('locomotion', Math.max(0.5, Math.min(8, speed)));
28
+ ```
29
+
30
+ After assignment, drive the real gameplay inputs through the range and sample expected moving
31
+ joints across multiple update frames. Treat non-finite `activeStateCurrentTime`,
32
+ `activeStateProgress`, or component speed as a playback failure.
@@ -0,0 +1,32 @@
1
+ # React projects
2
+
3
+ Let React own the asset and component lifecycle for simple playback:
4
+
5
+ ```tsx
6
+ import { Entity } from '@playcanvas/react';
7
+ import { Anim, Render } from '@playcanvas/react/components';
8
+ import { useModel } from '@playcanvas/react/hooks';
9
+
10
+ const Character = () => {
11
+ const { asset, error } = useModel('/models/Character.glb');
12
+ if (error) throw new Error(error);
13
+ if (!asset) return null;
14
+
15
+ return (
16
+ <Entity name="Character">
17
+ <Render type="asset" asset={asset} />
18
+ <Anim asset={asset} activate />
19
+ </Entity>
20
+ );
21
+ };
22
+ ```
23
+
24
+ Use `Anim` only when inspection confirms the asset has one clip. It takes `asset` plus
25
+ `AnimComponent` properties such as `speed`, `activate`, `playing`, `stateGraph`, and `targets`, and
26
+ it has no way to name a clip. Published React examples show `clip="Walk" loop`; those props do not
27
+ exist, so the component drops them with a development warning and plays the assigned track anyway.
28
+ Verify props against the installed `@playcanvas/react` declarations rather than an example.
29
+
30
+ Use a module-scoped Engine `Script` attached with `<Script>` for multiple clips, explicit selection,
31
+ state graphs, transitions, or retargeting. Keep per-frame animation state in the Engine component or
32
+ script rather than React state.
@@ -0,0 +1,13 @@
1
+ # Web Components projects
2
+
3
+ `<pc-model>` already covers the simplest case without any script: when the loaded container has
4
+ animations it adds an `anim` component and assigns `animations[0]` under the state name `animation`.
5
+ It always takes the first clip and has no attribute for choosing another.
6
+
7
+ There is no animation component beyond that. Put explicit clip selection, multiple clips, state
8
+ graphs, and retargeting in a module-scoped Engine `Script`, load it with `<pc-asset>`, and attach it
9
+ to the same `<pc-entity>` hierarchy as the rendered model using `<pc-scripts>` and `<pc-script>`.
10
+
11
+ Pass clip names and simple configuration as `<pc-script>` attributes. Use its `attributes` JSON
12
+ attribute for nested state-graph configuration. Keep scale on a visual child `<pc-entity>`, not on
13
+ skinned bones, and confirm playback after `pc-app` is ready.
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: find-examples
3
+ description: Use when implementing an unfamiliar PlayCanvas feature such as animation, particles, shaders, physics, UI, splats, or XR to find the official Engine, React, or Web Components example matching the installed package version and adapt it to the project.
4
+ ---
5
+
6
+ # Official examples
7
+
8
+ Use official examples as version-matched recipes. Engine and Web Components examples are standalone
9
+ applications to adapt, not modules to import.
10
+
11
+ Use the `reuse-scripts` skill instead when the installed Engine ships a production script for the
12
+ required behavior.
13
+
14
+ ## Find the matching example
15
+
16
+ When `node_modules/playcanvas` is a local source checkout or linked build — a symlink, a workspace,
17
+ or a path dependency — its `examples/src/examples/**` are already on disk. Read them directly and
18
+ skip the GitHub fetch below. A linked engine can sit ahead of any published tag, and a pre-release
19
+ version (`-alpha`, `-beta`) may have no matching public tree, so the on-disk source is the ground
20
+ truth. Fall back to GitHub only when the examples are absent locally; the published npm package
21
+ omits `examples/`.
22
+
23
+ ```sh
24
+ ls node_modules/playcanvas/examples/src/examples 2>/dev/null && echo "linked engine: read local examples, skip the fetch"
25
+ ```
26
+
27
+ Search the active surface's own catalog first, then the Engine catalog. Engine examples are the
28
+ largest set and port to every surface; the per-surface catalogs are smaller but need no translation.
29
+
30
+ | Surface | Repository | Package | Ref | Example paths |
31
+ | --- | --- | --- | --- | --- |
32
+ | direct Engine | `playcanvas/engine` | `playcanvas` | `v<version>` | `examples/src/examples/<category>/<name>.example.mjs` |
33
+ | Web Components | `playcanvas/web-components` | `@playcanvas/web-components` | `v<version>` | `examples/<name>.html` |
34
+ | React | `playcanvas/developer-site` | `@playcanvas/react` | `main` | `docs/user-manual/react/examples/<name>.mdx` |
35
+
36
+ Resolve the installed version instead of assuming the newest API:
37
+
38
+ ```sh
39
+ PKG=playcanvas # or @playcanvas/web-components
40
+ TAG="v$(node -p "JSON.parse(require('fs').readFileSync('node_modules/$PKG/package.json')).version")" || exit 1
41
+ gh api "repos/playcanvas/engine/git/trees/$TAG?recursive=1" \
42
+ | grep -o '"examples/src/examples/[^"]*\.example\.mjs"'
43
+ ```
44
+
45
+ Read the resolved package rather than the dependency list; `playcanvas` is often a transitive
46
+ dependency of `@playcanvas/react` or `@playcanvas/web-components`. Stop on a failed read instead of
47
+ requesting an invalid tag: an unresolved version produces an empty example list, not an error. When
48
+ the package is not below the project root's `node_modules`, run the command from the workspace that
49
+ depends on it.
50
+
51
+ List a different surface by substituting its repository and path pattern, such as
52
+ `'"examples/[^"]*\.html"'` for Web Components. Fetch a candidate from the same ref:
53
+
54
+ ```sh
55
+ gh api "repos/playcanvas/engine/contents/examples/src/examples/<category>/<name>.example.mjs?ref=$TAG" \
56
+ --jq .content | base64 --decode
57
+ ```
58
+
59
+ Use `main` only when the installed version has no tag. It may contain unsupported APIs. Example
60
+ asset URLs refer to `examples/assets/`; fetch assets from the same ref or substitute project assets.
61
+
62
+ The React catalog is documentation rather than a tagged release: it is small, tracks the latest
63
+ package, and its snippets are page fragments. Verify every prop and hook it uses against the
64
+ installed `@playcanvas/react` declarations, and port an Engine example when no React example covers
65
+ the feature.
66
+
67
+ ## Adapt the recipe
68
+
69
+ Keep component options, scene construction, materials, shaders, assets, and update logic. Drop the
70
+ example browser's controls and context imports.
71
+
72
+ When one example integrates several required systems, reproduce that integration as one baseline
73
+ before splitting or customizing it. Copying one script's parameters while omitting its layers,
74
+ depth map, camera frame, sky, or assets is not an adaptation of the example.
75
+
76
+ Read exactly one reference matching the code being edited:
77
+ [direct Engine](references/direct-engine.md), [React](references/react.md), or
78
+ [Web Components](references/web-components.md). Choose from imports and markup, not installed
79
+ dependencies alone.
@@ -0,0 +1,15 @@
1
+ # Direct Engine projects
2
+
3
+ Keep the project application's existing graphics-device, application, component-system, resource
4
+ handler, resize, and teardown setup. Do not copy the example bootstrap over it.
5
+
6
+ Port the useful setup into the existing application function:
7
+
8
+ - create entities with named imports from `playcanvas`;
9
+ - add only the component systems or resource handlers the feature requires;
10
+ - register loaded assets with the existing asset registry;
11
+ - attach per-frame work to the application update event or an Engine `Script`;
12
+ - unregister external events when the application or owning entity is destroyed.
13
+
14
+ Examples may use `Application`, `AppBase`, or helpers specific to the examples browser. Match the
15
+ application class already used by the project.