create-playcanvas 0.15.0 → 0.16.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.
Files changed (43) hide show
  1. package/README.md +69 -24
  2. package/dist/index.mjs +11 -11
  3. package/package.json +1 -1
  4. package/templates/_shared/scene-editor/AGENTS.md +8 -0
  5. package/templates/_shared/scene-editor/README.md +15 -2
  6. package/templates/_shared/scene-editor/src/scene-editor.ts +186 -36
  7. package/templates/engine/ar-placement/index.html +20 -0
  8. package/templates/engine/ar-placement/src/main.ts +22 -12
  9. package/templates/engine/base/AGENTS.md +1 -0
  10. package/templates/engine/base/README.md +1 -1
  11. package/templates/engine/first-person-controller/index.html +19 -0
  12. package/templates/engine/first-person-controller/src/main.ts +16 -11
  13. package/templates/engine/product-configurator/index.html +30 -0
  14. package/templates/engine/product-configurator/src/main.ts +33 -32
  15. package/templates/engine/scene-editor/index.html +33 -0
  16. package/templates/engine/scene-editor/src/main.ts +56 -47
  17. package/templates/engine/sprite-game/index.html +19 -0
  18. package/templates/engine/sprite-game/src/main.ts +19 -11
  19. package/templates/engine/third-person-controller/index.html +19 -0
  20. package/templates/engine/third-person-controller/src/main.ts +16 -11
  21. package/templates/engine/vr-starter/index.html +20 -0
  22. package/templates/engine/vr-starter/src/main.ts +22 -12
  23. package/templates/index.ts +1 -14
  24. package/templates/react/base/AGENTS.md +1 -0
  25. package/templates/react/scene-editor/src/App.tsx +73 -40
  26. package/templates/web-components/base/AGENTS.md +1 -0
  27. package/templates/web-components/scene-editor/index.html +32 -25
  28. package/templates/web-components/scene-editor/src/main.ts +33 -11
  29. package/templates/_shared/interactive-sphere/README.md +0 -7
  30. package/templates/_shared/interactive-sphere/public/environment-map.png +0 -0
  31. package/templates/_shared/interactive-sphere/src/counter.css +0 -19
  32. package/templates/_shared/physics-playground/README.md +0 -7
  33. package/templates/engine/interactive-sphere/src/App.ts +0 -182
  34. package/templates/engine/interactive-sphere/src/main.ts +0 -16
  35. package/templates/engine/interactive-sphere/src/utils.ts +0 -16
  36. package/templates/engine/physics-playground/src/main.ts +0 -131
  37. package/templates/react/interactive-sphere/src/App.tsx +0 -21
  38. package/templates/react/interactive-sphere/src/Scene.tsx +0 -70
  39. package/templates/react/physics-playground/src/App.tsx +0 -85
  40. package/templates/web-components/interactive-sphere/index.html +0 -36
  41. package/templates/web-components/interactive-sphere/src/main.ts +0 -76
  42. package/templates/web-components/physics-playground/index.html +0 -46
  43. package/templates/web-components/physics-playground/src/main.ts +0 -32
@@ -1,131 +0,0 @@
1
- import {
2
- AppBase,
3
- AppOptions,
4
- CameraComponentSystem,
5
- CollisionComponentSystem,
6
- Color,
7
- Entity,
8
- FILLMODE_FILL_WINDOW,
9
- LightComponentSystem,
10
- RenderComponentSystem,
11
- RESOLUTION_AUTO,
12
- RigidBodyComponentSystem,
13
- ScriptComponentSystem,
14
- StandardMaterial,
15
- Vec3,
16
- WasmModule,
17
- createGraphicsDevice
18
- } from 'playcanvas';
19
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
20
-
21
- import './starter.css';
22
-
23
- const COLORS = [new Color(0.16, 0.68, 0.9), new Color(0.95, 0.34, 0.22), new Color(0.98, 0.68, 0.18)];
24
- const TYPES = ['box', 'sphere', 'capsule', 'cylinder'] as const;
25
- const SCALES: [number, number, number][] = [
26
- [0.9, 0.9, 0.9],
27
- [0.9, 0.9, 0.9],
28
- [0.7, 1.1, 0.7],
29
- [0.8, 1.1, 0.8]
30
- ];
31
-
32
- WasmModule.setConfig('Ammo', {
33
- glueUrl: './ammo/ammo.wasm.js',
34
- wasmUrl: './ammo/ammo.wasm.wasm'
35
- });
36
- await new Promise<void>((resolve) => {
37
- WasmModule.getInstance('Ammo', () => resolve());
38
- });
39
-
40
- document.body.insertAdjacentHTML(
41
- 'beforeend',
42
- '<div class="hud"><section class="panel"><h1>Physics Playground</h1><p>Rigid bodies, collisions and runtime spawning.</p><div class="controls"><button id="spawn">Spawn object</button><button id="reset">Reset</button></div></section></div>'
43
- );
44
-
45
- const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
46
- const device = await createGraphicsDevice(canvas);
47
- const options = new AppOptions();
48
- options.graphicsDevice = device;
49
- options.componentSystems = [
50
- RenderComponentSystem,
51
- CameraComponentSystem,
52
- LightComponentSystem,
53
- ScriptComponentSystem,
54
- CollisionComponentSystem,
55
- RigidBodyComponentSystem
56
- ];
57
-
58
- const app = new AppBase(canvas);
59
- app.init(options);
60
- app.start();
61
- app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
62
- app.setCanvasResolution(RESOLUTION_AUTO);
63
- app.systems.rigidbody!.gravity.set(0, -9.81, 0);
64
-
65
- const materials = COLORS.map((color) => {
66
- const material = new StandardMaterial();
67
- material.diffuse = color;
68
- material.gloss = 0.45;
69
- material.update();
70
- return material;
71
- });
72
-
73
- const floorMaterial = new StandardMaterial();
74
- floorMaterial.diffuse = new Color(0.18, 0.2, 0.24);
75
- floorMaterial.gloss = 0.2;
76
- floorMaterial.update();
77
-
78
- const floor = new Entity('floor');
79
- floor.setLocalScale(10, 0.2, 10);
80
- floor.setPosition(0, -0.1, 0);
81
- floor.addComponent('render', { type: 'box', material: floorMaterial });
82
- floor.addComponent('collision', { type: 'box', halfExtents: new Vec3(5, 0.1, 5) });
83
- floor.addComponent('rigidbody', { type: 'static' });
84
- app.root.addChild(floor);
85
-
86
- const bodies: Entity[] = [];
87
- const spawn = () => {
88
- const i = bodies.length;
89
- const type = TYPES[i % TYPES.length];
90
- const entity = new Entity(type);
91
- entity.setPosition(((i % 4) - 1.5) * 1.1, 0.7 + Math.floor(i / 4) * 1.1, (Math.floor(i / 4) - 1) * 1.4);
92
- entity.setEulerAngles(i * 13, i * 29, 0);
93
- entity.setLocalScale(...SCALES[i % SCALES.length]);
94
- entity.addComponent('render', { type, material: materials[i % materials.length] });
95
- entity.addComponent('collision', {
96
- type,
97
- halfExtents: new Vec3(0.5, 0.5, 0.5),
98
- radius: 0.5
99
- });
100
- entity.addComponent('rigidbody', { type: 'dynamic', mass: 1, restitution: 0.35 });
101
- app.root.addChild(entity);
102
- bodies.push(entity);
103
- };
104
- const reset = () => {
105
- bodies.splice(0).forEach((entity) => entity.destroy());
106
- for (let i = 0; i < 12; i++) spawn();
107
- };
108
-
109
- const camera = new Entity('camera');
110
- camera.setPosition(10, 8, 10);
111
- camera.lookAt(0, 2, 0);
112
- camera.addComponent('camera', { clearColor: new Color(0.05, 0.07, 0.11) });
113
- camera.addComponent('script');
114
- camera.script!.create(CameraControls, { properties: { sceneSize: 10 } });
115
- app.root.addChild(camera);
116
-
117
- const light = new Entity('light');
118
- light.addComponent('light', {
119
- type: 'directional',
120
- intensity: 2.5,
121
- castShadows: true,
122
- shadowBias: 0.2,
123
- normalOffsetBias: 0.05
124
- });
125
- light.setEulerAngles(45, 35, 0);
126
- app.root.addChild(light);
127
-
128
- document.getElementById('spawn')!.onclick = spawn;
129
- document.getElementById('reset')!.onclick = reset;
130
- reset();
131
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -1,21 +0,0 @@
1
- import { Application } from '@playcanvas/react';
2
- import { useState } from 'react';
3
-
4
- import './counter.css';
5
- import Scene from './Scene';
6
- import './starter.css';
7
-
8
- function App() {
9
- const [count, setCount] = useState(0);
10
-
11
- return (
12
- <>
13
- <Application className="playcanvas-app">
14
- <Scene onClick={() => setCount((count) => count + 1)} />
15
- </Application>
16
- <p className="counter">Click count: {count}</p>
17
- </>
18
- );
19
- }
20
-
21
- export default App;
@@ -1,70 +0,0 @@
1
- import { Entity } from '@playcanvas/react';
2
- import { Camera, Environment, Render, Script } from '@playcanvas/react/components';
3
- import { useEnvAtlas, useMaterial } from '@playcanvas/react/hooks';
4
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
5
- import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
6
- import { useEffect, useState } from 'react';
7
-
8
- /**
9
- * The Scene renders a sphere with a grid and camera controls
10
- */
11
- function Scene({ onClick }: SceneProps) {
12
- const [hovering, setHovering] = useState(false);
13
-
14
- // Set a material color based on the hover state
15
- const diffuse = hovering ? '#40ccff' : '#e6edf9';
16
-
17
- // Create a material for the sphere
18
- const material = useMaterial({ diffuse });
19
-
20
- // Load the environment map
21
- const { asset: envMap } = useEnvAtlas('./environment-map.png');
22
-
23
- // Change the mouse cursor based on the hover state
24
- useEffect(() => {
25
- document.body.style.cursor = hovering ? 'pointer' : 'default';
26
-
27
- return () => {
28
- document.body.style.cursor = '';
29
- };
30
- }, [hovering]);
31
-
32
- // Don't render until the environment map is loaded
33
- if (!envMap) return null;
34
-
35
- return (
36
- <>
37
- {/* Render some environment lighting using the environment map */}
38
- <Environment envAtlas={envMap} showSkybox={false} />
39
-
40
- {/* Render a background grid */}
41
- <Entity scale={[12, 12, 12]}>
42
- <Script script={Grid} />
43
- </Entity>
44
-
45
- {/* Create a camera entity with camera controls */}
46
- <Entity name="camera" position={[3.2, 1.5, 3.2]} rotation={[-10, 45, 0]}>
47
- <Camera clearColor="#07101a" />
48
- <Script script={CameraControls} sceneSize={3} />
49
- </Entity>
50
-
51
- {/* Create and position entity with pointer events */}
52
- <Entity
53
- position={[0, 0.7, 0]}
54
- scale={[1.25, 1.25, 1.25]}
55
- onClick={onClick}
56
- onPointerOver={() => setHovering(true)}
57
- onPointerOut={() => setHovering(false)}
58
- >
59
- {/* Render a sphere with the material */}
60
- <Render type="sphere" material={material} />
61
- </Entity>
62
- </>
63
- );
64
- }
65
-
66
- type SceneProps = {
67
- onClick: () => void;
68
- };
69
-
70
- export default Scene;
@@ -1,85 +0,0 @@
1
- import { Application, Entity } from '@playcanvas/react';
2
- import { Camera, Collision, Light, Render, RigidBody, Script } from '@playcanvas/react/components';
3
- import { useMaterial } from '@playcanvas/react/hooks';
4
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
5
- import { useState } from 'react';
6
-
7
- import './starter.css';
8
-
9
- const COLORS = ['#29ade6', '#f25738', '#f9ad2e'];
10
- const TYPES = ['box', 'sphere', 'capsule', 'cylinder'] as const;
11
- const SCALES: [number, number, number][] = [
12
- [0.9, 0.9, 0.9],
13
- [0.9, 0.9, 0.9],
14
- [0.7, 1.1, 0.7],
15
- [0.8, 1.1, 0.8]
16
- ];
17
-
18
- function Body({ i }: { i: number }) {
19
- const type = TYPES[i % TYPES.length];
20
- const material = useMaterial({ diffuse: COLORS[i % COLORS.length], gloss: 0.45 });
21
- return (
22
- <Entity
23
- position={[((i % 4) - 1.5) * 1.1, 0.7 + Math.floor(i / 4) * 1.1, (Math.floor(i / 4) - 1) * 1.4]}
24
- rotation={[i * 13, i * 29, 0]}
25
- scale={SCALES[i % SCALES.length]}
26
- >
27
- <Render type={type} material={material} />
28
- <Collision type={type} />
29
- <RigidBody type="dynamic" mass={1} restitution={0.35} />
30
- </Entity>
31
- );
32
- }
33
-
34
- function Floor() {
35
- const material = useMaterial({ diffuse: '#2e333d', gloss: 0.2 });
36
- return (
37
- <Entity name="floor" position={[0, -0.1, 0]} scale={[10, 0.2, 10]}>
38
- <Render type="box" material={material} />
39
- <Collision type="box" halfExtents={[5, 0.1, 5]} />
40
- <RigidBody type="static" />
41
- </Entity>
42
- );
43
- }
44
-
45
- function App() {
46
- const [count, setCount] = useState(12);
47
- const [reset, setReset] = useState(0);
48
-
49
- return (
50
- <>
51
- <Application usePhysics>
52
- <Entity name="camera" position={[10, 8, 10]} rotation={[-23, 45, 0]}>
53
- <Camera clearColor="#0d121c" />
54
- <Script script={CameraControls} sceneSize={10} />
55
- </Entity>
56
- <Entity name="light" rotation={[45, 35, 0]}>
57
- <Light type="directional" intensity={2.5} castShadows shadowBias={0.2} normalOffsetBias={0.05} />
58
- </Entity>
59
- <Floor />
60
- {Array.from({ length: count }, (_, i) => (
61
- <Body key={`${reset}-${i}`} i={i} />
62
- ))}
63
- </Application>
64
- <div className="hud">
65
- <section className="panel">
66
- <h1>Physics Playground</h1>
67
- <p>Rigid bodies, collisions and runtime spawning.</p>
68
- <div className="controls">
69
- <button onClick={() => setCount((value) => value + 1)}>Spawn object</button>
70
- <button
71
- onClick={() => {
72
- setCount(12);
73
- setReset((value) => value + 1);
74
- }}
75
- >
76
- Reset
77
- </button>
78
- </div>
79
- </section>
80
- </div>
81
- </>
82
- );
83
- }
84
-
85
- export default App;
@@ -1,36 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>PlayCanvas + Web Components</title>
8
- </head>
9
- <body>
10
- <pc-app>
11
- <!-- Materials are declared once and referenced by id -->
12
- <pc-material id="sphere-material" diffuse="#e6edf9"></pc-material>
13
-
14
- <pc-scene>
15
- <!-- Camera. Orbit controls are attached in src/main.ts -->
16
- <pc-entity name="camera" position="3.2 1.5 3.2" rotation="-10 45 0">
17
- <pc-camera clear-color="#07101a"></pc-camera>
18
- </pc-entity>
19
-
20
- <!-- Lighting comes from the environment map, which is applied in src/main.ts -->
21
-
22
- <!-- Background grid. The grid script is attached in src/main.ts -->
23
- <pc-entity name="grid" scale="12 12 12"></pc-entity>
24
-
25
- <!-- The sphere. pc-app dispatches pointer events onto whatever the pointer hits -->
26
- <pc-entity name="sphere" position="0 0.7 0" scale="1.25 1.25 1.25">
27
- <pc-render type="sphere" material="sphere-material"></pc-render>
28
- </pc-entity>
29
- </pc-scene>
30
- </pc-app>
31
-
32
- <p id="counter" class="counter">Click count: 0</p>
33
-
34
- <script type="module" src="/src/main.ts"></script>
35
- </body>
36
- </html>
@@ -1,76 +0,0 @@
1
- import type { EntityElement } from '@playcanvas/web-components';
2
- import { whenReady } from '@playcanvas/web-components';
3
- import { Asset, TEXTURETYPE_RGBP } from 'playcanvas';
4
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
5
- import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
6
-
7
- import './counter.css';
8
- import './starter.css';
9
-
10
- const HOVER_COLOR = '#40ccff';
11
- const DEFAULT_COLOR = '#e6edf9';
12
-
13
- // The camera is awaited as <pc-camera> rather than the <pc-entity> holding it - an entity becomes
14
- // ready before its component children do, and CameraControls needs the camera component to exist
15
- const [appElement, cameraComponent, grid, sphere] = await Promise.all([
16
- whenReady('pc-app'),
17
- whenReady('pc-camera'),
18
- whenReady<EntityElement>('pc-entity[name="grid"]'),
19
- whenReady<EntityElement>('pc-entity[name="sphere"]')
20
- ]);
21
-
22
- // This is a pre-generated environment atlas rather than an equirectangular image, so it is applied
23
- // straight to the scene instead of through <pc-sky>
24
- const app = appElement.app!;
25
- const envAtlas = new Asset(
26
- 'env-atlas',
27
- 'texture',
28
- { url: './environment-map.png' },
29
- {
30
- type: TEXTURETYPE_RGBP,
31
- mipmaps: false
32
- }
33
- );
34
-
35
- app.assets.add(envAtlas);
36
- await new Promise<void>((resolve) => {
37
- envAtlas.ready(() => resolve());
38
- app.assets.load(envAtlas);
39
- });
40
-
41
- // Light the scene from the environment, with the skybox itself left hidden
42
- app.scene.envAtlas = envAtlas.resource!;
43
- const skyboxLayer = app.scene.layers.getLayerByName('Skybox');
44
- if (skyboxLayer) {
45
- skyboxLayer.enabled = false;
46
- }
47
-
48
- // Engine scripts are resolved by the bundler, so they are attached through the entity rather than
49
- // with <pc-script-instance name="...">, which only resolves scripts fetched at runtime by <pc-asset>
50
- const camera = cameraComponent.closestEntity;
51
- camera?.entity?.addComponent('script');
52
- camera?.entity?.script?.create(CameraControls, { properties: { sceneSize: 3 } });
53
- grid.entity?.addComponent('script');
54
- grid.entity?.script?.create(Grid);
55
-
56
- const material = document.querySelector('pc-material')!;
57
- const counterElement = document.getElementById('counter')!;
58
-
59
- let count = 0;
60
-
61
- // pc-app picks whatever is under the pointer and dispatches a regular DOM PointerEvent on it, so
62
- // the sphere is wired up like any other element
63
- sphere.addEventListener('pointerenter', () => {
64
- material.setAttribute('diffuse', HOVER_COLOR);
65
- document.body.style.cursor = 'pointer';
66
- });
67
-
68
- sphere.addEventListener('pointerleave', () => {
69
- material.setAttribute('diffuse', DEFAULT_COLOR);
70
- document.body.style.cursor = 'default';
71
- });
72
-
73
- sphere.addEventListener('pointerup', () => {
74
- count++;
75
- counterElement.textContent = `Click count: ${count}`;
76
- });
@@ -1,46 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/png" href="/src/assets/playcanvas.png" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Physics Playground</title>
8
- </head>
9
- <body>
10
- <pc-app>
11
- <pc-wasm name="Ammo" glue="./ammo/ammo.wasm.js" wasm="./ammo/ammo.wasm.wasm"></pc-wasm>
12
- <pc-material id="floor-material" diffuse="#2e333d" gloss="0.2"></pc-material>
13
- <pc-material id="cyan-material" diffuse="#29ade6" gloss="0.45"></pc-material>
14
- <pc-material id="coral-material" diffuse="#f25738" gloss="0.45"></pc-material>
15
- <pc-material id="yellow-material" diffuse="#f9ad2e" gloss="0.45"></pc-material>
16
- <pc-scene>
17
- <pc-entity name="camera" position="10 8 10" rotation="-23 45 0"
18
- ><pc-camera clear-color="#0d121c"></pc-camera
19
- ></pc-entity>
20
- <pc-entity name="light" rotation="45 35 0"
21
- ><pc-light
22
- type="directional"
23
- intensity="2.5"
24
- cast-shadows
25
- shadow-bias="0.2"
26
- normal-offset-bias="0.05"
27
- ></pc-light
28
- ></pc-entity>
29
- <pc-entity name="floor" position="0 -0.1 0" scale="10 0.2 10"
30
- ><pc-render type="box" material="floor-material"></pc-render
31
- ><pc-collision type="box" half-extents="5 0.1 5"></pc-collision
32
- ><pc-rigid-body type="static"></pc-rigid-body
33
- ></pc-entity>
34
- <pc-entity id="bodies"></pc-entity>
35
- </pc-scene>
36
- </pc-app>
37
- <div class="hud">
38
- <section class="panel">
39
- <h1>Physics Playground</h1>
40
- <p>Rigid bodies, collisions and runtime spawning.</p>
41
- <div class="controls"><button id="spawn">Spawn object</button><button id="reset">Reset</button></div>
42
- </section>
43
- </div>
44
- <script type="module" src="/src/main.ts"></script>
45
- </body>
46
- </html>
@@ -1,32 +0,0 @@
1
- import { whenReady } from '@playcanvas/web-components';
2
- import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
3
-
4
- import './starter.css';
5
-
6
- const MATERIALS = ['cyan-material', 'coral-material', 'yellow-material'];
7
- const TYPES = ['box', 'sphere', 'capsule', 'cylinder'];
8
- const SCALES = ['0.9 0.9 0.9', '0.9 0.9 0.9', '0.7 1.1 0.7', '0.8 1.1 0.8'];
9
-
10
- const camera = (await whenReady('pc-camera')).closestEntity!.entity!;
11
- camera.addComponent('script');
12
- camera.script!.create(CameraControls, { properties: { sceneSize: 10 } });
13
-
14
- const bodies = document.getElementById('bodies')!;
15
- let count = 0;
16
- const spawn = () => {
17
- const i = count++;
18
- const type = TYPES[i % TYPES.length];
19
- bodies.insertAdjacentHTML(
20
- 'beforeend',
21
- `<pc-entity data-body position="${((i % 4) - 1.5) * 1.1} ${0.7 + Math.floor(i / 4) * 1.1} ${(Math.floor(i / 4) - 1) * 1.4}" rotation="${i * 13} ${i * 29} 0" scale="${SCALES[i % SCALES.length]}"><pc-render type="${type}" material="${MATERIALS[i % MATERIALS.length]}"></pc-render><pc-collision type="${type}"></pc-collision><pc-rigid-body type="dynamic" mass="1" restitution="0.35"></pc-rigid-body></pc-entity>`
22
- );
23
- };
24
- const reset = () => {
25
- bodies.replaceChildren();
26
- count = 0;
27
- for (let i = 0; i < 12; i++) spawn();
28
- };
29
-
30
- document.getElementById('spawn')!.onclick = spawn;
31
- document.getElementById('reset')!.onclick = reset;
32
- reset();