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
@@ -0,0 +1,20 @@
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>AR Placement</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>AR Placement</h1>
14
+ <p id="xr-status">Checking AR support…</p>
15
+ <div class="controls"><button id="xr-button" disabled>Start AR</button></div>
16
+ </section>
17
+ </div>
18
+ <script type="module" src="/src/main.ts"></script>
19
+ </body>
20
+ </html>
@@ -17,24 +17,36 @@ import {
17
17
  import { ArPlacement } from './ar-placement';
18
18
  import './starter.css';
19
19
 
20
- document.body.insertAdjacentHTML(
21
- 'beforeend',
22
- '<div class="hud"><section class="panel"><h1>AR Placement</h1><p id="xr-status">Checking AR support…</p><div class="controls"><button id="xr-button" disabled>Start AR</button></div></section></div>'
23
- );
24
-
25
20
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
21
+
26
22
  const device = await createGraphicsDevice(canvas);
27
- const options = new AppOptions();
28
- options.graphicsDevice = device;
29
- options.componentSystems = [RenderComponentSystem, CameraComponentSystem, LightComponentSystem, ScriptComponentSystem];
30
- options.xr = XrManager;
23
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
24
+
25
+ const createOptions = new AppOptions();
26
+ createOptions.graphicsDevice = device;
27
+ createOptions.componentSystems = [
28
+ RenderComponentSystem,
29
+ CameraComponentSystem,
30
+ LightComponentSystem,
31
+ ScriptComponentSystem
32
+ ];
33
+ createOptions.xr = XrManager;
31
34
 
32
35
  const app = new AppBase(canvas);
33
- app.init(options);
36
+ app.init(createOptions);
34
37
  app.start();
38
+
39
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
35
40
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
36
41
  app.setCanvasResolution(RESOLUTION_AUTO);
37
42
 
43
+ // Ensure canvas is resized when window changes size
44
+ const resize = () => app.resizeCanvas();
45
+ window.addEventListener('resize', resize);
46
+ app.on('destroy', () => {
47
+ window.removeEventListener('resize', resize);
48
+ });
49
+
38
50
  const camera = new Entity('camera');
39
51
  camera.addComponent('camera', { clearColor: new Color(0.05, 0.08, 0.12, 1) });
40
52
  camera.setPosition(3, 2.4, 4);
@@ -73,5 +85,3 @@ const placement = new Entity('placement');
73
85
  placement.addComponent('script');
74
86
  placement.script!.create(ArPlacement, { properties: { camera, preview } });
75
87
  app.root.addChild(placement);
76
-
77
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -26,6 +26,7 @@ A PlayCanvas Engine app in TypeScript, built with Vite. The scene is built in co
26
26
  - Builds use relative URLs (`base: './'` in `vite.config.ts`), so they run from any folder. Load files in `public/` with relative paths such as `./model.glb`, not `/model.glb`.
27
27
  - The engine ships ready-made scripts, such as camera controls and character controllers, under `playcanvas/scripts/esm/`. Use one before writing the same behavior yourself.
28
28
  - `node_modules/playcanvas/build/playcanvas.d.ts` documents the installed engine's API.
29
+ - For guides, start from the [Engine guides index](https://developer.playcanvas.com/user-manual/engine/llms.txt), which links a Markdown version of every page.
29
30
  - TypeScript is strict, and unused locals and parameters are errors.
30
31
 
31
32
  ## Checking your work
@@ -45,6 +45,6 @@ This project includes [`@playcanvas/skills`](https://github.com/playcanvas/skill
45
45
  ## Further reading
46
46
 
47
47
  - [PlayCanvas Engine manual](https://developer.playcanvas.com/user-manual/engine/)
48
- - [PlayCanvas examples](https://playcanvas.github.io/)
48
+ - [PlayCanvas examples](https://playcanvas.com/examples)
49
49
  - [Vite documentation](https://vite.dev/)
50
50
  - [TypeScript documentation](https://www.typescriptlang.org/docs/)
@@ -0,0 +1,19 @@
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>First-Person Controller</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>First-Person Controller</h1>
14
+ <p>Click the scene, then use WASD, mouse look and Space to jump.</p>
15
+ </section>
16
+ </div>
17
+ <script type="module" src="/src/main.ts"></script>
18
+ </body>
19
+ </html>
@@ -28,16 +28,14 @@ await new Promise<void>((resolve) => {
28
28
  WasmModule.getInstance('Ammo', () => resolve());
29
29
  });
30
30
 
31
- document.body.insertAdjacentHTML(
32
- 'beforeend',
33
- '<div class="hud"><section class="panel"><h1>First-Person Controller</h1><p>Click the scene, then use WASD, mouse look and Space to jump.</p></section></div>'
34
- );
35
-
36
31
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
32
+
37
33
  const device = await createGraphicsDevice(canvas);
38
- const options = new AppOptions();
39
- options.graphicsDevice = device;
40
- options.componentSystems = [
34
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
35
+
36
+ const createOptions = new AppOptions();
37
+ createOptions.graphicsDevice = device;
38
+ createOptions.componentSystems = [
41
39
  RenderComponentSystem,
42
40
  CameraComponentSystem,
43
41
  LightComponentSystem,
@@ -47,11 +45,20 @@ options.componentSystems = [
47
45
  ];
48
46
 
49
47
  const app = new AppBase(canvas);
50
- app.init(options);
48
+ app.init(createOptions);
51
49
  app.start();
50
+
51
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
52
52
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
53
53
  app.setCanvasResolution(RESOLUTION_AUTO);
54
54
 
55
+ // Ensure canvas is resized when window changes size
56
+ const resize = () => app.resizeCanvas();
57
+ window.addEventListener('resize', resize);
58
+ app.on('destroy', () => {
59
+ window.removeEventListener('resize', resize);
60
+ });
61
+
55
62
  const material = (color: Color) => {
56
63
  const result = new StandardMaterial();
57
64
  result.diffuse = color;
@@ -134,5 +141,3 @@ const fill = new Entity('fill');
134
141
  fill.addComponent('light', { type: 'directional', intensity: 0.8 });
135
142
  fill.setEulerAngles(-25, -140, 0);
136
143
  app.root.addChild(fill);
137
-
138
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -0,0 +1,30 @@
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>Product Configurator</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>Product Configurator</h1>
14
+ <p>Choose a paint finish, then drag to orbit.</p>
15
+ <div class="controls variants" id="paints">
16
+ <button aria-pressed="true" data-color="#d6293e" data-shade="#9b1731">
17
+ <span class="finish" style="--color: #d6293e"></span>Crimson
18
+ </button>
19
+ <button data-color="#d8e4e9" data-shade="#8fa8b5">
20
+ <span class="finish" style="--color: #d8e4e9"></span>Arctic
21
+ </button>
22
+ <button data-color="#9acb34" data-shade="#3c742d">
23
+ <span class="finish" style="--color: #9acb34"></span>Volt
24
+ </button>
25
+ </div>
26
+ </section>
27
+ </div>
28
+ <script type="module" src="/src/main.ts"></script>
29
+ </body>
30
+ </html>
@@ -29,33 +29,38 @@ import './product.css';
29
29
  import './starter.css';
30
30
 
31
31
  const PRODUCT_URL = 'https://developer.playcanvas.com/assets/lambo.glb';
32
- const PAINTS = [
33
- { name: 'Crimson', color: '#d6293e', shade: '#9b1731' },
34
- { name: 'Arctic', color: '#d8e4e9', shade: '#8fa8b5' },
35
- { name: 'Volt', color: '#9acb34', shade: '#3c742d' }
36
- ];
37
32
  const CAMERA_FOCUS = new Vec3(0, 0.8, 0);
38
33
  const CAMERA_PITCH = new Vec2(-85, -4);
39
34
 
40
- document.body.insertAdjacentHTML(
41
- 'beforeend',
42
- `<div class="hud"><section class="panel"><h1>Product Configurator</h1><p>Choose a paint finish, then drag to orbit.</p><div class="controls variants" id="paints">${PAINTS.map(({ name, color }, i) => `<button aria-pressed="${i === 0}" data-color="${color}"><span class="finish" style="--color:${color}"></span>${name}</button>`).join('')}</div></section></div>`
43
- );
44
-
45
35
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
36
+
46
37
  const device = await createGraphicsDevice(canvas);
47
- device.maxPixelRatio = Math.min(device.maxPixelRatio, 2);
38
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
48
39
 
49
- const options = new AppOptions();
50
- options.graphicsDevice = device;
51
- options.componentSystems = [RenderComponentSystem, CameraComponentSystem, LightComponentSystem, ScriptComponentSystem];
52
- options.resourceHandlers = [TextureHandler, ContainerHandler];
40
+ const createOptions = new AppOptions();
41
+ createOptions.graphicsDevice = device;
42
+ createOptions.componentSystems = [
43
+ RenderComponentSystem,
44
+ CameraComponentSystem,
45
+ LightComponentSystem,
46
+ ScriptComponentSystem
47
+ ];
48
+ createOptions.resourceHandlers = [TextureHandler, ContainerHandler];
53
49
 
54
50
  const app = new AppBase(canvas);
55
- app.init(options);
51
+ app.init(createOptions);
52
+
53
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
56
54
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
57
55
  app.setCanvasResolution(RESOLUTION_AUTO);
58
56
 
57
+ // Ensure canvas is resized when window changes size
58
+ const resize = () => app.resizeCanvas();
59
+ window.addEventListener('resize', resize);
60
+ app.on('destroy', () => {
61
+ window.removeEventListener('resize', resize);
62
+ });
63
+
59
64
  const product = new Asset('car', 'container', { url: PRODUCT_URL });
60
65
  await new Promise<void>((resolve) => new AssetListLoader([product], app.assets).load(() => resolve()));
61
66
 
@@ -82,10 +87,18 @@ const paint = body[0].material!.clone() as StandardMaterial;
82
87
  const shade = panels[0].material!.clone() as StandardMaterial;
83
88
  body.forEach((mesh) => (mesh.material = paint));
84
89
  panels.forEach((mesh) => (mesh.material = shade));
85
- paint.diffuse.fromString(PAINTS[0].color);
86
- shade.diffuse.fromString(PAINTS[0].shade);
87
- paint.update();
88
- shade.update();
90
+
91
+ // Each paint button in index.html carries its finish's two colors
92
+ const buttons = document.querySelectorAll<HTMLButtonElement>('#paints button');
93
+ const choose = (button: HTMLButtonElement) => {
94
+ paint.diffuse.fromString(button.dataset.color!);
95
+ shade.diffuse.fromString(button.dataset.shade!);
96
+ paint.update();
97
+ shade.update();
98
+ buttons.forEach((item) => (item.ariaPressed = String(item === button)));
99
+ };
100
+ buttons.forEach((button) => (button.onclick = () => choose(button)));
101
+ choose(buttons[0]);
89
102
 
90
103
  const sky = new Entity('sky');
91
104
  sky.addComponent('script');
@@ -141,15 +154,3 @@ light.addComponent('light', {
141
154
  });
142
155
  light.setEulerAngles(45, -145, 0);
143
156
  app.root.addChild(light);
144
-
145
- document.querySelectorAll<HTMLButtonElement>('#paints button').forEach((button, i, buttons) => {
146
- button.onclick = () => {
147
- paint.diffuse.fromString(PAINTS[i].color);
148
- shade.diffuse.fromString(PAINTS[i].shade);
149
- paint.update();
150
- shade.update();
151
- buttons.forEach((item, j) => (item.ariaPressed = String(i === j)));
152
- };
153
- });
154
-
155
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -0,0 +1,33 @@
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>Scene Editor</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>Scene Editor</h1>
14
+ <p>Click an object to select it, then drag its gizmo. Drag anywhere else to orbit.</p>
15
+ <div class="controls">
16
+ <button data-mode="translate" aria-pressed="true">Move</button>
17
+ <button data-mode="rotate" aria-pressed="false">Rotate</button>
18
+ <button data-mode="scale" aria-pressed="false">Scale</button>
19
+ </div>
20
+ <div class="controls">
21
+ <button data-add="box">Add box</button>
22
+ <button data-add="sphere">Add sphere</button>
23
+ <button data-add="cone">Add cone</button>
24
+ </div>
25
+ <div class="controls">
26
+ <button id="delete" disabled>Delete</button>
27
+ <button id="reset">Reset</button>
28
+ </div>
29
+ </section>
30
+ </div>
31
+ <script type="module" src="/src/main.ts"></script>
32
+ </body>
33
+ </html>
@@ -6,52 +6,57 @@ import {
6
6
  Entity,
7
7
  FILLMODE_FILL_WINDOW,
8
8
  LightComponentSystem,
9
- RenderComponentSystem,
10
9
  RESOLUTION_AUTO,
10
+ RenderComponentSystem,
11
11
  ScriptComponentSystem,
12
- StandardMaterial,
13
12
  createGraphicsDevice
14
13
  } from 'playcanvas';
14
+ import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs';
15
15
  import { Grid } from 'playcanvas/scripts/esm/grid.mjs';
16
16
 
17
17
  import { SceneEditor } from './scene-editor';
18
+ import type { Mode, Shape } from './scene-editor';
18
19
  import './starter.css';
19
20
 
20
- document.body.insertAdjacentHTML(
21
- 'beforeend',
22
- '<div class="hud"><section class="panel"><h1>Scene Editor</h1><p>Select an object, then translate, rotate or scale it.</p><div class="controls"><button data-target="0" aria-pressed="true">Cube</button><button data-target="1">Sphere</button></div><div class="controls"><button data-mode="translate" aria-pressed="true">Move</button><button data-mode="rotate">Rotate</button><button data-mode="scale">Scale</button></div></section></div>'
23
- );
24
-
25
21
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
22
+
26
23
  const device = await createGraphicsDevice(canvas);
27
- const options = new AppOptions();
28
- options.graphicsDevice = device;
29
- options.componentSystems = [RenderComponentSystem, CameraComponentSystem, LightComponentSystem, ScriptComponentSystem];
24
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
25
+
26
+ const createOptions = new AppOptions();
27
+ createOptions.graphicsDevice = device;
28
+ createOptions.componentSystems = [
29
+ RenderComponentSystem,
30
+ CameraComponentSystem,
31
+ LightComponentSystem,
32
+ ScriptComponentSystem
33
+ ];
30
34
 
31
35
  const app = new AppBase(canvas);
32
- app.init(options);
36
+ app.init(createOptions);
33
37
  app.start();
38
+
39
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
34
40
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
35
41
  app.setCanvasResolution(RESOLUTION_AUTO);
36
42
 
37
- app.scene.ambientLight = new Color(0.25, 0.28, 0.34);
38
-
39
- const coral = new StandardMaterial();
40
- coral.diffuse = new Color(1, 0.32, 0.27);
41
- coral.update();
42
- const cyan = new StandardMaterial();
43
- cyan.diffuse = new Color(0.14, 0.68, 0.88);
44
- cyan.update();
43
+ // Ensure canvas is resized when window changes size
44
+ const resize = () => app.resizeCanvas();
45
+ window.addEventListener('resize', resize);
46
+ app.on('destroy', () => {
47
+ window.removeEventListener('resize', resize);
48
+ });
45
49
 
46
- const cube = new Entity('cube');
47
- cube.addComponent('render', { type: 'box', material: coral });
48
- cube.setPosition(-1.2, 0.5, 0);
49
- app.root.addChild(cube);
50
+ const light = new Entity('light');
51
+ light.addComponent('light', { type: 'directional', intensity: 2.5 });
52
+ light.setEulerAngles(45, 30, 0);
53
+ app.root.addChild(light);
50
54
 
51
- const sphere = new Entity('sphere');
52
- sphere.addComponent('render', { type: 'sphere', material: cyan });
53
- sphere.setPosition(1.2, 0.5, 0);
54
- app.root.addChild(sphere);
55
+ // A weaker light from behind keeps the sides the first can't reach from going black
56
+ const fill = new Entity('fill');
57
+ fill.addComponent('light', { type: 'directional', intensity: 0.8 });
58
+ fill.setEulerAngles(-25, -140, 0);
59
+ app.root.addChild(fill);
55
60
 
56
61
  const grid = new Entity('grid');
57
62
  grid.addComponent('script');
@@ -59,32 +64,36 @@ grid.script!.create(Grid);
59
64
  grid.setLocalScale(12, 12, 12);
60
65
  app.root.addChild(grid);
61
66
 
67
+ // Create camera entity with orbit controls
62
68
  const camera = new Entity('camera');
63
69
  camera.addComponent('camera', { clearColor: new Color(0.04, 0.06, 0.09) });
64
70
  camera.setPosition(4, 3.2, 5);
65
71
  camera.lookAt(0, 0.5, 0);
72
+ camera.addComponent('script');
73
+ camera.script!.create(CameraControls, { properties: { sceneSize: 5 } });
66
74
  app.root.addChild(camera);
67
75
 
68
- const light = new Entity('light');
69
- light.addComponent('light', {
70
- type: 'directional',
71
- intensity: 2.5,
72
- castShadows: true,
73
- shadowBias: 0.2,
74
- normalOffsetBias: 0.05
75
- });
76
- light.setEulerAngles(45, 30, 0);
77
- app.root.addChild(light);
76
+ // The editor creates the objects under its own entity, from the scene saved in this browser
77
+ const host = new Entity('editor');
78
+ host.addComponent('script');
79
+ const editor = host.script!.create(SceneEditor, { properties: { camera } }) as SceneEditor;
80
+ app.root.addChild(host);
78
81
 
79
- const editor = new Entity('editor');
80
- editor.addComponent('script');
81
- editor.script!.create(SceneEditor, {
82
- properties: {
83
- camera,
84
- targets: [cube, sphere],
85
- layer: app.scene.layers.getLayerByName('World')
86
- }
82
+ // The HUD drives the editor, and follows its selection
83
+ const modes = document.querySelectorAll<HTMLButtonElement>('[data-mode]');
84
+ modes.forEach((button) => {
85
+ button.addEventListener('click', () => {
86
+ editor.mode = button.dataset.mode as Mode;
87
+ modes.forEach((other) => (other.ariaPressed = String(other === button)));
88
+ });
89
+ });
90
+ document.querySelectorAll<HTMLButtonElement>('[data-add]').forEach((button) => {
91
+ button.addEventListener('click', () => editor.add(button.dataset.add as Shape));
87
92
  });
88
- app.root.addChild(editor);
89
93
 
90
- window.addEventListener('resize', () => app.resizeCanvas());
94
+ const remove = document.getElementById('delete') as HTMLButtonElement;
95
+ remove.addEventListener('click', () => editor.remove());
96
+ document.getElementById('reset')!.addEventListener('click', () => editor.reset());
97
+ app.on('editor:select', (selected: Entity | null) => {
98
+ remove.disabled = !selected;
99
+ });
@@ -0,0 +1,19 @@
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>Sprite Game</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>Sprite Game</h1>
14
+ <p>Use A/D or the arrow keys to move and Space to jump.</p>
15
+ </section>
16
+ </div>
17
+ <script type="module" src="/src/main.ts"></script>
18
+ </body>
19
+ </html>
@@ -16,25 +16,35 @@ import {
16
16
  import { SpriteGame } from './sprite-game';
17
17
  import './starter.css';
18
18
 
19
- document.body.insertAdjacentHTML(
20
- 'beforeend',
21
- '<div class="hud"><section class="panel"><h1>Sprite Game</h1><p>Use A/D or the arrow keys to move and Space to jump.</p></section></div>'
22
- );
23
-
24
19
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
20
+
25
21
  const device = await createGraphicsDevice(canvas);
26
22
  device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
27
23
 
28
- const options = new AppOptions();
29
- options.graphicsDevice = device;
30
- options.componentSystems = [RenderComponentSystem, CameraComponentSystem, SpriteComponentSystem, ScriptComponentSystem];
24
+ const createOptions = new AppOptions();
25
+ createOptions.graphicsDevice = device;
26
+ createOptions.componentSystems = [
27
+ RenderComponentSystem,
28
+ CameraComponentSystem,
29
+ SpriteComponentSystem,
30
+ ScriptComponentSystem
31
+ ];
31
32
 
32
33
  const app = new AppBase(canvas);
33
- app.init(options);
34
+ app.init(createOptions);
34
35
  app.start();
36
+
37
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
35
38
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
36
39
  app.setCanvasResolution(RESOLUTION_AUTO);
37
40
 
41
+ // Ensure canvas is resized when window changes size
42
+ const resize = () => app.resizeCanvas();
43
+ window.addEventListener('resize', resize);
44
+ app.on('destroy', () => {
45
+ window.removeEventListener('resize', resize);
46
+ });
47
+
38
48
  const camera = new Entity('camera');
39
49
  camera.addComponent('camera', {
40
50
  projection: 1,
@@ -68,5 +78,3 @@ game.setPosition(0, -2.15, 0);
68
78
  game.addComponent('script');
69
79
  game.script!.create(SpriteGame);
70
80
  app.root.addChild(game);
71
-
72
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -0,0 +1,19 @@
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>Third-Person Controller</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>Third-Person Controller</h1>
14
+ <p>Click the scene, then use WASD to move, mouse to orbit and Space to jump.</p>
15
+ </section>
16
+ </div>
17
+ <script type="module" src="/src/main.ts"></script>
18
+ </body>
19
+ </html>
@@ -49,16 +49,14 @@ await new Promise<void>((resolve) => {
49
49
  WasmModule.getInstance('Ammo', () => resolve());
50
50
  });
51
51
 
52
- document.body.insertAdjacentHTML(
53
- 'beforeend',
54
- '<div class="hud"><section class="panel"><h1>Third-Person Controller</h1><p>Click the scene, then use WASD to move, mouse to orbit and Space to jump.</p></section></div>'
55
- );
56
-
57
52
  const canvas = document.getElementById('application-canvas') as HTMLCanvasElement;
53
+
58
54
  const device = await createGraphicsDevice(canvas);
59
- const options = new AppOptions();
60
- options.graphicsDevice = device;
61
- options.componentSystems = [
55
+ device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
56
+
57
+ const createOptions = new AppOptions();
58
+ createOptions.graphicsDevice = device;
59
+ createOptions.componentSystems = [
62
60
  RenderComponentSystem,
63
61
  CameraComponentSystem,
64
62
  LightComponentSystem,
@@ -68,10 +66,19 @@ options.componentSystems = [
68
66
  ];
69
67
 
70
68
  const app = new AppBase(canvas);
71
- app.init(options);
69
+ app.init(createOptions);
72
70
  app.start();
71
+
72
+ // Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
73
73
  app.setCanvasFillMode(FILLMODE_FILL_WINDOW);
74
74
  app.setCanvasResolution(RESOLUTION_AUTO);
75
+
76
+ // Ensure canvas is resized when window changes size
77
+ const resize = () => app.resizeCanvas();
78
+ window.addEventListener('resize', resize);
79
+ app.on('destroy', () => {
80
+ window.removeEventListener('resize', resize);
81
+ });
75
82
  app.scene.ambientLight = new Color(0.28, 0.32, 0.38);
76
83
 
77
84
  const groundMaterial = new StandardMaterial();
@@ -216,5 +223,3 @@ light.addComponent('light', {
216
223
  });
217
224
  light.setEulerAngles(45, 30, 0);
218
225
  app.root.addChild(light);
219
-
220
- window.addEventListener('resize', () => app.resizeCanvas());
@@ -0,0 +1,20 @@
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>VR Starter</title>
8
+ </head>
9
+ <body>
10
+ <canvas id="application-canvas"></canvas>
11
+ <div class="hud">
12
+ <section class="panel">
13
+ <h1>VR Starter</h1>
14
+ <p id="xr-status">Checking VR support…</p>
15
+ <div class="controls"><button id="xr-button" disabled>Enter VR</button></div>
16
+ </section>
17
+ </div>
18
+ <script type="module" src="/src/main.ts"></script>
19
+ </body>
20
+ </html>