create-playcanvas 0.4.0 → 0.5.1
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.
- package/README.md +5 -0
- package/dist/index.mjs +42 -41
- package/package.json +2 -1
- package/skills/add-effects/SKILL.md +81 -0
- package/skills/apply-conventions/SKILL.md +73 -0
- package/skills/assemble-scene/SKILL.md +53 -0
- package/skills/assemble-scene/references/direct-engine.md +16 -0
- package/skills/assemble-scene/references/react.md +24 -0
- package/skills/assemble-scene/references/web-components.md +19 -0
- package/skills/build-app/SKILL.md +41 -0
- package/skills/build-app/references/direct-engine.md +12 -0
- package/skills/build-app/references/react.md +20 -0
- package/skills/build-app/references/web-components.md +55 -0
- package/skills/build-hud/SKILL.md +36 -0
- package/skills/calibrate-model/SKILL.md +65 -0
- package/skills/calibrate-model/references/direct-engine.md +22 -0
- package/skills/calibrate-model/references/react.md +27 -0
- package/skills/calibrate-model/references/web-components.md +19 -0
- package/skills/configure-animation/SKILL.md +50 -0
- package/skills/configure-animation/references/direct-engine.md +32 -0
- package/skills/configure-animation/references/react.md +32 -0
- package/skills/configure-animation/references/web-components.md +13 -0
- package/skills/find-examples/SKILL.md +79 -0
- package/skills/find-examples/references/direct-engine.md +15 -0
- package/skills/find-examples/references/react.md +17 -0
- package/skills/find-examples/references/web-components.md +17 -0
- package/skills/inspect-glb/SKILL.md +72 -0
- package/skills/inspect-glb/scripts/inspect.mjs +239 -0
- package/skills/light-scene/SKILL.md +84 -0
- package/skills/manage-game-state/SKILL.md +48 -0
- package/skills/reuse-scripts/SKILL.md +59 -0
- package/skills/reuse-scripts/references/direct-engine.md +41 -0
- package/skills/reuse-scripts/references/react.md +28 -0
- package/skills/reuse-scripts/references/web-components.md +27 -0
- package/templates/engine/base/.prettierignore +2 -0
- package/templates/engine/base/README.md +4 -0
- package/templates/engine/base/eslint.config.mjs +1 -1
- package/templates/engine/base/package.json +2 -2
- package/templates/react/base/.prettierignore +2 -0
- package/templates/react/base/README.md +4 -0
- package/templates/react/base/eslint.config.mjs +1 -0
- package/templates/react/base/package.json +2 -2
- package/templates/web-components/base/.prettierignore +2 -0
- package/templates/web-components/base/README.md +4 -0
- package/templates/web-components/base/eslint.config.mjs +1 -1
- package/templates/web-components/base/package.json +3 -3
|
@@ -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.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# React projects
|
|
2
|
+
|
|
3
|
+
Drop graphics-device, application, component-system, and resource-handler setup because
|
|
4
|
+
`<Application>` owns it.
|
|
5
|
+
|
|
6
|
+
- Express entities and components with `<Entity>` and exports from `@playcanvas/react/components`.
|
|
7
|
+
- Load assets with hooks such as `useModel`, `useTexture`, and `useEnvAtlas` when available in the
|
|
8
|
+
installed package.
|
|
9
|
+
- Attach Engine `Script` subclasses with React's `<Script script={Type} />`.
|
|
10
|
+
- Put frame callbacks in a `Script` or `useAppEvent('update', cb)` instead of updating React state
|
|
11
|
+
every frame.
|
|
12
|
+
- Use `useApp()` only when no declarative component covers the Engine API. Clean up resources in the
|
|
13
|
+
effect that created them.
|
|
14
|
+
- Keep hooks unconditional and asset URLs stable.
|
|
15
|
+
|
|
16
|
+
Verify component and hook names against the installed `@playcanvas/react` declarations before
|
|
17
|
+
translating the example.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Web Components projects
|
|
2
|
+
|
|
3
|
+
A `playcanvas/web-components` example needs no translation: it is already a `pc-*` document, so copy
|
|
4
|
+
its markup and adjust asset paths and attributes.
|
|
5
|
+
|
|
6
|
+
Translating an Engine example instead, keep its feature options, assets, materials, shaders, and
|
|
7
|
+
update logic. Drop its graphics device and application bootstrap because `<pc-app>` owns them.
|
|
8
|
+
|
|
9
|
+
- Express supported entities and components with `pc-*` elements.
|
|
10
|
+
- Register assets as direct children of `<pc-app>` with `<pc-asset>`.
|
|
11
|
+
- Put per-entity behavior in an Engine `Script` loaded through `<pc-asset>` and attached with
|
|
12
|
+
`<pc-scripts>` and `<pc-script>`.
|
|
13
|
+
- Use `whenReady` for page-level glue or Engine APIs with no declarative element.
|
|
14
|
+
- Fetch example assets from the same Engine tag as the source example.
|
|
15
|
+
|
|
16
|
+
Verify element and attribute names against the installed `@playcanvas/web-components` package. Do
|
|
17
|
+
not translate an unsupported Engine component into a guessed custom element.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: inspect-glb
|
|
3
|
+
description: Use before loading, placing, scaling, or animating GLB assets in PlayCanvas to measure bounds, grounding offset, clips, joints, and hierarchy offline with the bundled zero-dependency inspector.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Inspect GLBs
|
|
7
|
+
|
|
8
|
+
Resolve `scripts/inspect.mjs` relative to this skill directory and run it before choosing entity
|
|
9
|
+
transforms or animation names:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
node <skill-directory>/scripts/inspect.mjs public/models/Character.glb
|
|
13
|
+
node <skill-directory>/scripts/inspect.mjs public/models/{Ship,Enemy}_*.glb
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The output provides:
|
|
17
|
+
|
|
18
|
+
- `dims` for calculating a uniform scale;
|
|
19
|
+
- `groundOffset` for the model wrapper's local Y position;
|
|
20
|
+
- `center` for the pivot offset from the geometry centre: a non-zero X or Z means the root's
|
|
21
|
+
position is not where the model appears, and Y feeds grounding;
|
|
22
|
+
- `boundsSource` for proving how precisely the bounds were measured;
|
|
23
|
+
- `nodePaths`, `clips`, `joints`, and `animationTargets` for animation setup and rig comparison;
|
|
24
|
+
- `skinned` to flag bind-pose bounds that need runtime confirmation.
|
|
25
|
+
|
|
26
|
+
## Bounds precision
|
|
27
|
+
|
|
28
|
+
`aabb`, `dims`, `center`, and `groundOffset` are decoded from actual vertex positions and transformed
|
|
29
|
+
by each node's world matrix, so they are exact and can place meshes touching without a gap. Confirm
|
|
30
|
+
`boundsSource` before relying on that precision:
|
|
31
|
+
|
|
32
|
+
- `vertices` means every primitive was decoded and the bounds are exact.
|
|
33
|
+
- `accessor-minmax` means at least one primitive could not be decoded, so its bounds fall back to the
|
|
34
|
+
node-transformed local box. That over-estimates a mesh which does not fill its own box on a node
|
|
35
|
+
rotated off-axis, by up to 41 percent for a 45-degree yaw. `boundsNotes` gives the cause.
|
|
36
|
+
|
|
37
|
+
Carry `boundsSource`, `aabb`, `dims`, `center`, and `groundOffset` into the asset tuning record.
|
|
38
|
+
`groundOffset` seats the base in Y; `center.x` and `center.z` are the horizontal pivot offset that
|
|
39
|
+
`calibrate-model` must compensate, so a placed root sits where the mesh appears, not where the
|
|
40
|
+
authored pivot happens to be. Running the inspector alone is not placement evidence. Its global AABB supports plane grounding and
|
|
41
|
+
broad-phase spacing, but cannot locate a non-planar support surface or attachment point. Use an
|
|
42
|
+
authored mount point or confirm the support in the running application before placing another model.
|
|
43
|
+
|
|
44
|
+
## Extract from any container with gltf-transform
|
|
45
|
+
|
|
46
|
+
The bundled inspector decodes plain vertex buffers only. When a primitive is draco- or
|
|
47
|
+
meshopt-compressed, sparse, or in an external buffer it falls back to `accessor-minmax` or reports
|
|
48
|
+
nothing, and `boundsNotes` names the cause. Use gltf-transform as the precursor that normalizes any
|
|
49
|
+
container into a plain GLB the inspector can then measure exactly. Decompress a scratch copy with the
|
|
50
|
+
passes that would alter geometry or hierarchy disabled, and inspect the copy rather than shipping it:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npx @gltf-transform/cli optimize in.glb scratch.glb \
|
|
54
|
+
--compress false --simplify false --flatten false --join false
|
|
55
|
+
node <skill-directory>/scripts/inspect.mjs scratch.glb
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The Engine has no `EXT_meshopt_compression` support, so a meshopt asset will not render as shipped.
|
|
59
|
+
If it must appear in the scene, transcode it to an uncompressed GLB the same way — subject to the
|
|
60
|
+
project's rules on modifying assets — so it both loads and measures.
|
|
61
|
+
|
|
62
|
+
These exact bounds intentionally disagree with the Engine at runtime. `MeshInstance.aabb` transforms
|
|
63
|
+
the mesh's declared local box, so for a node rotated off-axis it reads larger than the geometry
|
|
64
|
+
really is. Treat that difference as expected and place from these bounds. A skinned mesh node's own
|
|
65
|
+
transform is ignored here because glTF requires it; the Engine resolves to the same result.
|
|
66
|
+
|
|
67
|
+
Shortlist files first; do not dump an entire asset pack into context. Bounds cannot prove facing.
|
|
68
|
+
glTF convention is +Z forward while PlayCanvas entities face -Z, and asset packs vary. Confirm each
|
|
69
|
+
directional model once in the running app as the `apply-conventions` skill describes, and store its
|
|
70
|
+
yaw correction with its scale and grounding.
|
|
71
|
+
|
|
72
|
+
Use `npx @gltf-transform/cli inspect` only when vertex or texture statistics justify extra tooling.
|