Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ npm run develop
```
Visit the url mentioned in your terminal to view the examples browser.

To disable automatic browser and example reloads:
```
npm run develop:no-reload
```

You can also run the examples browser with a specific version of the engine by running the following command:

```
Expand Down Expand Up @@ -55,7 +60,7 @@ export { app };

This is the only file that's required to run an example. The code defined in this function is executed each time the example play button is pressed. It takes the example's canvas element from the DOM and usually begins by creating a new PlayCanvas `Application` or `AppBase` using that canvas.

Examples can also contain comments which allow you to define the default configuration for your examples as well as overrides to particular settings such as `deviceType`. Check the possible values to set in `ExampleConfig` in `scripts/utils.mjs` file for the full list.
Examples can also contain comments which allow you to define the default configuration for your examples as well as overrides to particular settings such as `deviceType`. Check the possible values to set in `ExampleConfig` in `utils/example-source.mjs` file for the full list.

```js
// @config DESCRIPTION This is a description
Expand Down Expand Up @@ -131,12 +136,12 @@ const data = localImport('data.mjs');


### Testing your example
Ensure you have a locally built version of the examples browser by running the commands in the `Local examples browser development` section. Then run `npm run serve` to serve the examples browser.
Run `npm run develop` from the `Local examples browser development` section to serve the examples browser with Vite.

You can view the full collection of example iframes by visiting [http://localhost:5000/iframe/]() in your browser.
You can view an individual iframe directly, for example [http://localhost:5555/iframe/misc_hello-world.html]().

### Debug and performance engine development
By default, the examples app uses the local version of the playcanvas engine located at `../build/playcanvas.js`. If you'd like to test the examples browser with the debug or performance versions of the engine instead, you can run `npm run watch:debug` or `npm run watch:profiler` commands.
By default, `npm run develop` serves the engine from `../src/index.js`. To test against a built ESM engine instead, pass `ENGINE_PATH`, for example `ENGINE_PATH=../build/playcanvas.mjs npm run develop`.

## Example Modules

Expand Down
71 changes: 71 additions & 0 deletions examples/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import fs from 'node:fs';
import { parseArgs } from 'node:util';

import { buildProd } from './utils/build-prod.mjs';
import { buildMetadata } from './utils/metadata.mjs';
import { buildThumbnails } from './utils/thumbnails.mjs';

const USAGE = `Usage: node build.mjs [options]

Options:
--metadata Generate cache/metadata.mjs
--thumbnails Generate thumbnails
--clean Remove dist and cache
--debug Enable thumbnail debug logging
--help, -h Show help`;

const { values } = parseArgs({
args: process.argv.slice(2),
options: {
metadata: { type: 'boolean' },
thumbnails: { type: 'boolean' },
clean: { type: 'boolean' },
debug: { type: 'boolean' },
help: { type: 'boolean', short: 'h' }
},
allowPositionals: false
});

/**
* @returns {Promise<void>} completion promise.
*/
const clean = async () => {
await Promise.all([
fs.promises.rm('dist', { recursive: true, force: true }),
fs.promises.rm('cache', { recursive: true, force: true })
]);
};

/**
* @returns {Promise<void>} completion promise.
*/
const main = async () => {
if (values.help) {
console.log(USAGE);
return;
}

if (values.metadata) {
await buildMetadata();
return;
}

if (values.thumbnails) {
await buildMetadata();
await buildThumbnails({
clean: values.clean,
debug: values.debug
});
return;
}

if (values.clean) {
await clean();
return;
}

await buildMetadata();
await buildProd();
};

await main();
82 changes: 71 additions & 11 deletions examples/iframe/loader.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { deviceType as selectedDeviceType, updateDeviceType, fetchFile, localImp

import MiniStats from './ministats.mjs';

/** @import { AppBase } from 'playcanvas' */

class ExampleLoader {
/**
* @type {Record<string, any>}
Expand All @@ -12,7 +14,25 @@ class ExampleLoader {
_config;

/**
* @type {import('playcanvas').AppBase}
* @type {Record<string, any>}
* @private
*/
_baseConfig = {};

/**
* @type {string[]}
* @private
*/
_fileNames = [];

/**
* @type {string}
* @private
*/
_name = '';

/**
* @type {AppBase}
* @private
*/
_app;
Expand Down Expand Up @@ -102,33 +122,51 @@ class ExampleLoader {
return locations;
}

_clearFiles() {
for (const name of Object.keys(files)) {
delete files[name];
}
}

/**
* @param {{ engineUrl: string, fileNames: string[] }} options - Options to start the loader
* @param {string} [stamp] - The cache-busting stamp.
*/
async start({ engineUrl, fileNames }) {
window.pc = await import(engineUrl);

// @ts-ignore
window.top.pc = window.pc;

async _fetchFiles(stamp = '') {
const suffix = stamp ? `?t=${stamp}` : '';
// extracts example category and name from the URL
const match = /([^/]+)\.html$/.exec(new URL(location.href).pathname);
if (!match) {
return;
}
this._name = match[1];

// loads each files
/**
* @type {Record<string, string>}
*/
const unorderedFiles = {};
await Promise.all(fileNames.map(async (name) => {
unorderedFiles[name] = await fetchFile(`./${match[1]}.${name}`);
await Promise.all(this._fileNames.map(async (name) => {
unorderedFiles[name] = await fetchFile(`./${this._name}.${name}${suffix}`);
}));
this._clearFiles();
for (const name of Object.keys(unorderedFiles).sort()) {
files[name] = unorderedFiles[name];
}
}

/**
* @param {{ engineUrl: string, fileNames: string[], config?: Record<string, any> }} options - Options to start the loader
*/
async start({ engineUrl, fileNames, config = {} }) {
this._baseConfig = config;
this._fileNames = fileNames;

window.pc = await import(engineUrl);

// @ts-ignore
window.top.pc = window.pc;

await this._fetchFiles();

await this.load();
}
Expand All @@ -140,7 +178,10 @@ class ExampleLoader {
refresh();

// parse config
this._config = parseConfig(files['example.mjs']);
this._config = {
...this._baseConfig,
...parseConfig(files['example.mjs'])
};

// update device type
updateDeviceType(this._config);
Expand Down Expand Up @@ -192,6 +233,25 @@ class ExampleLoader {
fire('requestedFiles', { files });
}

/**
* @param {{ stamp?: string, config?: Record<string, any>, files?: string[] }} [options] - Reload options.
*/
async reloadFiles({ stamp = '', config = null, files: names = null } = {}) {
if (!this._allowRestart) {
console.warn('Dropping restart while still restarting');
return;
}
if (config) {
this._baseConfig = config;
}
if (names) {
this._fileNames = names;
}
await this._fetchFiles(stamp);
this.sendRequestedFiles();
this.hotReload();
}

/**
* @param {boolean} enabled - The enabled state of ministats
*/
Expand Down
6 changes: 4 additions & 2 deletions examples/iframe/ministats.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { getQueryParams } from 'examples/utils';

/** @import { AppBase, MiniStats as PcMiniStats } from 'playcanvas' */

const params = getQueryParams(window.top?.location.href ?? '');

export default class MiniStats {
/** @type {import('playcanvas').MiniStats | null} */
/** @type {PcMiniStats | null} */
static instance = null;

/**
* @param {import('playcanvas').AppBase} app - The app instance.
* @param {AppBase} app - The app instance.
* @param {any} state - The enabled state.
*/
static enable(app, state) {
Expand Down
16 changes: 0 additions & 16 deletions examples/jsconfig.json

This file was deleted.

Loading