UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

291 lines (290 loc) 9.82 kB
var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); import { Debug } from "../..//core/debug.js"; import { EventHandler } from "../../core/event-handler.js"; import { SCRIPT_INITIALIZE, SCRIPT_POST_INITIALIZE } from "./constants.js"; class Script extends EventHandler { /** * Create a new Script instance. * * @param {object} args - The input arguments object. * @param {AppBase} args.app - The {@link AppBase} that is running the script. * @param {Entity} args.entity - The {@link Entity} that the script is attached to. */ constructor(args) { super(); /** * The {@link AppBase} that the instance of this script belongs to. * * @type {AppBase} */ __publicField(this, "app"); /** * The {@link Entity} that the instance of this script belongs to. * * @type {Entity} */ __publicField(this, "entity"); /** @private */ __publicField(this, "_enabled"); /** @private */ __publicField(this, "_enabledOld"); /** @private */ __publicField(this, "_initialized"); /** @private */ __publicField(this, "_postInitialized"); /** @private */ __publicField(this, "__destroyed"); /** @private */ __publicField(this, "__scriptType"); /** * The order in the script component that the methods of this script instance will run * relative to other script instances in the component. * * @type {number} * @private */ __publicField(this, "__executionOrder"); this.initScript(args); } /** * Sets the enabled state of the script instance. When disabled, no update methods will be * called on each tick. `initialize` and `postInitialize` methods will run once when the script * instance is next in the `enabled` state during an app tick. * * @type {boolean} */ set enabled(value) { this._enabled = !!value; if (this.enabled === this._enabledOld) return; this._enabledOld = this.enabled; this.fire(this.enabled ? "enable" : "disable"); this.fire("state", this.enabled); if (!this._initialized && this.enabled) { this._initialized = true; this.fire("preInitialize"); if (this.initialize) { this.entity.script._scriptMethod(this, SCRIPT_INITIALIZE); } } if (this._initialized && !this._postInitialized && this.enabled && !this.entity.script._beingEnabled) { this._postInitialized = true; if (this.postInitialize) { this.entity.script._scriptMethod(this, SCRIPT_POST_INITIALIZE); } } } /** * Gets the running state of the script instance. Returns true when the script instance is * enabled and its owning {@link Entity} (and all ancestors) and {@link ScriptComponent} are * also enabled; otherwise false. * * @type {boolean} */ get enabled() { return this._enabled && !this._destroyed && this.entity.script.enabled && this.entity.enabled; } /** * @typedef {object} ScriptInitializationArgs * @property {boolean} [enabled] - True if the script instance is in running state. * @property {AppBase} app - The {@link AppBase} that is running the script. * @property {Entity} entity - The {@link Entity} that the script is attached to. */ /** * @param {ScriptInitializationArgs} args - The input arguments object. * @protected */ initScript(args) { const script = this.constructor; Debug.assert(args && args.app && args.entity, `script [${script.__name}] has missing arguments in constructor`); this.app = args.app; this.entity = args.entity; this._enabled = typeof args.enabled === "boolean" ? args.enabled : true; this._enabledOld = this.enabled; this.__destroyed = false; this.__scriptType = script; this.__executionOrder = -1; } /** * Sets the unique name of the script. * * @type {string|null} */ static set scriptName(value) { this.__name = value; } /** * Gets the unique name of the script. * * @type {string|null} */ static get scriptName() { return this.__name; } /** * @function * @name Script#[initialize] * @description Called when script is about to run for the first time. */ /** * @function * @name Script#[postInitialize] * @description Called after all initialize methods are executed in the same tick or enabling chain of actions. */ /** * @function * @name Script#[update] * @description Called for enabled (running state) scripts on each tick. * @param {number} dt - The delta time in seconds since the last frame. */ /** * @function * @name Script#[postUpdate] * @description Called for enabled (running state) scripts on each tick, after update. * @param {number} dt - The delta time in seconds since the last frame. */ /** * @function * @name Script#[swap] * @description Called when a Script that already exists in the registry gets redefined. If the * new Script has a `swap` method, then it will be executed to perform hot-reload at runtime. * @param {Script} old - Old instance of the scriptType to copy data to the new instance. */ } /** * Fired when a script instance becomes enabled. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('enable', () => { * // Script Instance is now enabled * }); * } * }; */ __publicField(Script, "EVENT_ENABLE", "enable"); /** * Fired when a script instance becomes disabled. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('disable', () => { * // Script Instance is now disabled * }); * } * }; */ __publicField(Script, "EVENT_DISABLE", "disable"); /** * Fired when a script instance changes state to enabled or disabled. The handler is passed a * boolean parameter that states whether the script instance is now enabled or disabled. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('state', (enabled) => { * console.log(`Script Instance is now ${enabled ? 'enabled' : 'disabled'}`); * }); * } * }; */ __publicField(Script, "EVENT_STATE", "state"); /** * Fired when a script instance is destroyed and removed from component. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('destroy', () => { * // no longer part of the entity * // this is a good place to clean up allocated resources used by the script * }); * } * }; */ __publicField(Script, "EVENT_DESTROY", "destroy"); /** * Fired when script attributes have changed. This event is available in two forms. They are as * follows: * * 1. `attr` - Fired for any attribute change. The handler is passed the name of the attribute * that changed, the value of the attribute before the change and the value of the attribute * after the change. * 2. `attr:[name]` - Fired for a specific attribute change. The handler is passed the value of * the attribute before the change and the value of the attribute after the change. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('attr', (name, newValue, oldValue) => { * console.log(`Attribute '${name}' changed from '${oldValue}' to '${newValue}'`); * }); * } * }; * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('attr:speed', (newValue, oldValue) => { * console.log(`Attribute 'speed' changed from '${oldValue}' to '${newValue}'`); * }); * } * }; */ __publicField(Script, "EVENT_ATTR", "attr"); /** * Fired when a script instance had an exception. The script instance will be automatically * disabled. The handler is passed an Error object containing the details of the * exception and the name of the method that threw the exception. * * @event * @example * export class PlayerController extends Script { * static scriptName = 'playerController'; * initialize() { * this.on('error', (err, method) => { * // caught an exception * console.log(err.stack); * }); * } * }; */ __publicField(Script, "EVENT_ERROR", "error"); /** * @type {string|null} * @private */ __publicField(Script, "__name", null); // Will be assigned when calling createScript or registerScript. /** * @param {*} constructorFn - The constructor function of the script type. * @returns {string} The script name. * @private */ __publicField(Script, "__getScriptName", getScriptName); const funcNameRegex = /^\s*function(?:\s|\s*\/\*.*\*\/\s*)+([^(\s\/]*)\s*/; function getScriptName(constructorFn) { if (typeof constructorFn !== "function") return void 0; if (constructorFn.scriptName) return constructorFn.scriptName; if ("name" in Function.prototype) return constructorFn.name; if (constructorFn === Function || constructorFn === Function.prototype.constructor) return "Function"; const match = `${constructorFn}`.match(funcNameRegex); return match ? match[1] : void 0; } export { Script, getScriptName };