/** * The components an {@link Entity} can hold, keyed by the name passed to * {@link Entity#addComponent}: `'camera'` maps to {@link CameraComponent}, `'light'` to * {@link LightComponent} and so on. The map is derived from the component properties declared on * `Entity`, so an application that registers its own {@link ComponentSystem} extends it - and * with it the typing of {@link Entity#addComponent}, {@link Entity#findComponent} and * {@link Entity#findComponents} - by declaring the matching property on `Entity`: * * ```ts * declare module 'playcanvas' { * interface Entity { * readonly mything: MyComponent | undefined; * } * } * ``` */ export type ComponentMap = { [K in keyof Entity as NonNullable extends Component ? K : never]: NonNullable; }; /** * The name of a component an {@link Entity} can hold, such as `'camera'` or `'light'`: the keys of * {@link ComponentMap}. This is what {@link Entity#addComponent}, {@link Entity#findComponent}, * {@link Entity#findComponents} and {@link Entity#removeComponent} take, and what * {@link ComponentOptions} is indexed by. */ export type ComponentName = keyof ComponentMap & string; /** * The options {@link Entity#addComponent} accepts for the component named `K`, for example * `ComponentOptions<'camera'>`. These are the public, settable, non-function properties of the * component class (see {@link ComponentMap}), all optional, plus the extras the component's system * understands: option names that are not component properties, callbacks, and properties that * also accept a plain array in place of a math object, such as `clearColor: [0, 0, 0, 1]`. * Application-defined components get the same derivation from their component class. */ export type ComponentOptions = { [P in keyof MergedComponentOptions]: MergedComponentOptions[P]; }; /** * An Entity is the core primitive of a PlayCanvas application. Every object in a scene (a camera, a * light, a 3D model, a sound source, a piece of UI, or your own gameplay object) is represented by * an Entity. On its own, an Entity is simply a named node in the scene graph; it gains behavior * from the {@link Component}s attached to it. * * An Entity therefore brings together two things: * * - A transform: Entity extends {@link GraphNode}, so it has a position, rotation and scale, and * can be parented to other entities to form a hierarchy. The root of that hierarchy is * {@link AppBase#root}, and child entities inherit the transforms of their ancestors. * - A set of components: each {@link Component} adds a single capability. For example, a * {@link CameraComponent} renders the scene, a {@link LightComponent} lights it, a * {@link RenderComponent} draws a 3D mesh, and a {@link ScriptComponent} runs your own code. * * Add a capability with {@link Entity#addComponent}, access it later through the matching property * (such as {@link Entity#camera} or {@link Entity#render}), and remove it with * {@link Entity#removeComponent}. An entity, together with all of its descendants and their * components, can be enabled or disabled as a group via {@link GraphNode#enabled}, and removed from * the scene with {@link Entity#destroy}. * * @example * // Create an entity, give it a camera component, position it, and add it to the scene * const camera = new Entity('camera'); * camera.addComponent('camera', { * clearColor: new Color(0.1, 0.1, 0.1) * }); * camera.setPosition(0, 0, 10); * app.root.addChild(camera); * @example * // Entities form a hierarchy: a child inherits its parent's transform * const parent = new Entity('parent'); * const child = new Entity('child'); * parent.addChild(child); * parent.setLocalPosition(5, 0, 0); // moves both parent and child * @category Framework */ export class Entity extends GraphNode { /** * Fired after the entity is destroyed. * * @event * @example * entity.on('destroy', (e) => { * console.log(`Entity ${e.name} has been destroyed`); * }); */ static EVENT_DESTROY: string; /** * Create a new Entity. * * @param {string} [name] - The non-unique name of the entity, default is "Untitled". * @param {AppBase} [app] - The application the entity belongs to, default is the current * application. * @example * const entity = new Entity(); * * // Add a Component to the Entity * entity.addComponent('camera', { * fov: 45, * nearClip: 1, * farClip: 10000 * }); * * // Add the Entity into the scene graph * app.root.addChild(entity); * * // Move the entity * entity.translate(10, 0, 0); * * // Or translate it by setting its position directly * const p = entity.getPosition(); * entity.setPosition(p.x + 10, p.y, p.z); * * // Change the entity's rotation in local space * const e = entity.getLocalEulerAngles(); * entity.setLocalEulerAngles(e.x, e.y + 90, e.z); * * // Or use rotateLocal * entity.rotateLocal(0, 90, 0); */ constructor(name?: string, app?: AppBase); /** * Gets the {@link AnimComponent} attached to this entity. * * @type {AnimComponent|undefined} * @readonly */ readonly anim: AnimComponent | undefined; /** * Gets the {@link AnimationComponent} attached to this entity. * * @type {AnimationComponent|undefined} * @readonly */ readonly animation: AnimationComponent | undefined; /** * Gets the {@link AudioListenerComponent} attached to this entity. * * @type {AudioListenerComponent|undefined} * @readonly */ readonly audiolistener: AudioListenerComponent | undefined; /** * Gets the {@link ButtonComponent} attached to this entity. * * @type {ButtonComponent|undefined} * @readonly */ readonly button: ButtonComponent | undefined; /** * Gets the {@link CameraComponent} attached to this entity. * * @type {CameraComponent|undefined} * @readonly */ readonly camera: CameraComponent | undefined; /** * Gets the {@link CollisionComponent} attached to this entity. * * @type {CollisionComponent|undefined} * @readonly */ readonly collision: CollisionComponent | undefined; /** * Gets the {@link ElementComponent} attached to this entity. * * @type {ElementComponent|undefined} * @readonly */ readonly element: ElementComponent | undefined; /** * Gets the {@link GSplatComponent} attached to this entity. * * @type {GSplatComponent|undefined} * @readonly */ readonly gsplat: GSplatComponent | undefined; /** * Gets the {@link JointComponent} attached to this entity. * * @type {JointComponent|undefined} * @readonly * @alpha */ readonly joint: JointComponent | undefined; /** * Gets the {@link LayoutChildComponent} attached to this entity. * * @type {LayoutChildComponent|undefined} * @readonly */ readonly layoutchild: LayoutChildComponent | undefined; /** * Gets the {@link LayoutGroupComponent} attached to this entity. * * @type {LayoutGroupComponent|undefined} * @readonly */ readonly layoutgroup: LayoutGroupComponent | undefined; /** * Gets the {@link LightComponent} attached to this entity. * * @type {LightComponent|undefined} * @readonly */ readonly light: LightComponent | undefined; /** * Gets the {@link ModelComponent} attached to this entity. * * @type {ModelComponent|undefined} * @readonly */ readonly model: ModelComponent | undefined; /** * Gets the {@link ParticleSystemComponent} attached to this entity. * * @type {ParticleSystemComponent|undefined} * @readonly */ readonly particlesystem: ParticleSystemComponent | undefined; /** * Gets the {@link RenderComponent} attached to this entity. * * @type {RenderComponent|undefined} * @readonly */ readonly render: RenderComponent | undefined; /** * Gets the {@link RigidBodyComponent} attached to this entity. * * @type {RigidBodyComponent|undefined} * @readonly */ readonly rigidbody: RigidBodyComponent | undefined; /** * Gets the {@link ScreenComponent} attached to this entity. * * @type {ScreenComponent|undefined} * @readonly */ readonly screen: ScreenComponent | undefined; /** * Gets the {@link ScriptComponent} attached to this entity. * * @type {ScriptComponent|undefined} * @readonly */ readonly script: ScriptComponent | undefined; /** * Gets the {@link ScrollbarComponent} attached to this entity. * * @type {ScrollbarComponent|undefined} * @readonly */ readonly scrollbar: ScrollbarComponent | undefined; /** * Gets the {@link ScrollViewComponent} attached to this entity. * * @type {ScrollViewComponent|undefined} * @readonly */ readonly scrollview: ScrollViewComponent | undefined; /** * Gets the {@link SoundComponent} attached to this entity. * * @type {SoundComponent|undefined} * @readonly */ readonly sound: SoundComponent | undefined; /** * Gets the {@link SpriteComponent} attached to this entity. * * @type {SpriteComponent|undefined} * @readonly */ readonly sprite: SpriteComponent | undefined; /** * Component storage. * * @type {Object} * @ignore */ c: { [x: string]: Component; }; /** * @type {AppBase} * @private */ private _app; /** * Used by component systems to speed up destruction. * * @ignore */ _destroying: boolean; /** * @type {string|null} * @private */ private _guid; /** * Used to differentiate between the entities of a template root instance, which have it set to * true, and the cloned instance entities (set to false). * * @ignore */ _template: boolean; /** * Create a new component and add it to the entity. Use this to add functionality to the entity * like rendering a model, playing sounds and so on. * * For the built-in components the `type` also types the options and the result: * `entity.addComponent('camera', { fov: 45 })` accepts any settable property of * {@link CameraComponent} and returns `CameraComponent | null`. See {@link ComponentOptions} for * the rule and {@link ComponentMap} for extending this to application-defined components. * * @template {ComponentName | (string & {})} K * @param {K} type - The name of the component to add (a {@link ComponentName}). Valid strings are: * * - "anim" - see {@link AnimComponent} * - "animation" - see {@link AnimationComponent} * - "audiolistener" - see {@link AudioListenerComponent} * - "button" - see {@link ButtonComponent} * - "camera" - see {@link CameraComponent} * - "collision" - see {@link CollisionComponent} * - "element" - see {@link ElementComponent} * - "gsplat" - see {@link GSplatComponent} * - "layoutchild" - see {@link LayoutChildComponent} * - "layoutgroup" - see {@link LayoutGroupComponent} * - "light" - see {@link LightComponent} * - "model" - see {@link ModelComponent} * - "particlesystem" - see {@link ParticleSystemComponent} * - "render" - see {@link RenderComponent} * - "rigidbody" - see {@link RigidBodyComponent} * - "screen" - see {@link ScreenComponent} * - "script" - see {@link ScriptComponent} * - "scrollbar" - see {@link ScrollbarComponent} * - "scrollview" - see {@link ScrollViewComponent} * - "sound" - see {@link SoundComponent} * - "sprite" - see {@link SpriteComponent} * * @param {K extends ComponentName ? ComponentOptions : object} [data] - The * initialization data for the specific component type: the settable properties of the component * class plus the extras its system understands (see {@link ComponentOptions}). Any object is * accepted for a component name that is not in {@link ComponentMap}. * @returns {(K extends ComponentName ? ComponentMap[K] : Component) | null} The new * Component that was attached to the entity or null if there was an error. * @example * const entity = new Entity(); * * // Add a light component with default properties * entity.addComponent("light"); * * // Add a camera component with some specified properties * entity.addComponent("camera", { * fov: 45, * clearColor: new Color(1, 0, 0) * }); */ addComponent(type: K, data?: K extends ComponentName ? ComponentOptions : object): (K extends ComponentName ? ComponentMap[K] : Component) | null; /** * Remove a component from the Entity. * * @param {ComponentName | (string & {})} type - The name of the Component type. * @example * const entity = new Entity(); * entity.addComponent("light"); // add new light component * * entity.removeComponent("light"); // remove light component */ removeComponent(type: ComponentName | (string & {})): void; /** * Search the entity and all of its descendants for the first component of specified type. * * @template {ComponentName | (string & {})} K * @param {K} type - The name of the component type to retrieve. * @returns {(K extends ComponentName ? ComponentMap[K] : Component) | null} A component of * specified type, if the entity or any of its descendants has one. Returns null otherwise. * @example * // Get the first found light component in the hierarchy tree that starts with this entity * const light = entity.findComponent("light"); */ findComponent(type: K): (K extends ComponentName ? ComponentMap[K] : Component) | null; /** * Search the entity and all of its descendants for all components of specified type. * * @template {ComponentName | (string & {})} K * @param {K} type - The name of the component type to retrieve. * @returns {(K extends ComponentName ? ComponentMap[K] : Component)[]} All components of * specified type in the entity or any of its descendants. Returns empty array if none found. * @example * // Get all light components in the hierarchy tree that starts with this entity * const lights = entity.findComponents("light"); */ findComponents(type: K): (K extends ComponentName ? ComponentMap[K] : Component)[]; /** * Search the entity and all of its descendants for the first script instance of the specified * class. The result is typed as an instance of that class, so no cast is needed. * * @template {Script} T * @overload * @param {new (...args: any[]) => T} type - The script class to search for. * @returns {T|undefined} A script instance of the specified class, if the entity or any of its * descendants has one. Returns undefined otherwise. * @example * // Get the first PlayerController instance in the hierarchy tree that starts with this entity * const controller = entity.findScript(PlayerController); // PlayerController | undefined */ findScript(type: new (...args: any[]) => T): T | undefined; /** * Search the entity and all of its descendants for the first script instance with the * specified name. * * @overload * @param {string} name - The name of the script to search for. * @returns {Script|undefined} A script instance with the specified name, if the entity or any * of its descendants has one. Returns undefined otherwise. * @example * // Get the first found "playerController" instance in the hierarchy tree that starts with this entity * const controller = entity.findScript("playerController"); */ findScript(name: string): Script | undefined; /** * Search the entity and all of its descendants for all script instances of the specified * class. The result is typed as an array of that class, so no cast is needed. * * @template {Script} T * @overload * @param {new (...args: any[]) => T} type - The script class to search for. * @returns {T[]} All script instances of the specified class in the entity or any of its * descendants. Returns an empty array if none are found. * @example * // Get all PlayerController instances in the hierarchy tree that starts with this entity * const controllers = entity.findScripts(PlayerController); // PlayerController[] */ findScripts(type: new (...args: any[]) => T): T[]; /** * Search the entity and all of its descendants for all script instances with the specified * name. * * @overload * @param {string} name - The name of the script to search for. * @returns {Script[]} All script instances with the specified name in the entity or any of its * descendants. Returns an empty array if none are found. * @example * // Get all "playerController" instances in the hierarchy tree that starts with this entity * const controllers = entity.findScripts("playerController"); */ findScripts(name: string): Script[]; /** * Sets the GUID for this Entity. Note that it is unlikely that you should need to change the * GUID value of an Entity at run-time. Doing so will corrupt the graph this Entity is in. * * @type {string} * @ignore */ set guid(value: string); /** * Gets the GUID for this Entity. * * @type {string} */ get guid(): string; /** * Get the GUID value for this Entity. * * @returns {string} The GUID of the Entity. * @ignore * @deprecated Use {@link Entity#guid} instead. */ getGuid(): string; /** * Set the GUID value for this Entity. Note that it is unlikely that you should need to change * the GUID value of an Entity at run-time. Doing so will corrupt the graph this Entity is in. * * @param {string} guid - The GUID to assign to the Entity. * @ignore * @deprecated Use {@link Entity#guid} instead. */ setGuid(guid: string): void; /** @private */ private _onHierarchyStatePostChanged; /** * Find a descendant of this entity with the GUID. * * @param {string} guid - The GUID to search for. * @returns {Entity|null} The entity with the matching GUID or null if no entity is found. */ findByGuid(guid: string): Entity | null; /** * Create a deep copy of the Entity. Duplicate the full Entity hierarchy, with all Components * and all descendants. Note, this Entity is not in the hierarchy and must be added manually. * * @returns {this} A new Entity which is a deep copy of the original. * @example * const e = this.entity.clone(); * * // Add clone as a sibling to the original * this.entity.parent.addChild(e); */ clone(): this; _getSortedComponents(): Component[]; /** * @param {Object} duplicatedIdsMap - A map of original entity GUIDs to cloned * entities. * @returns {this} A new Entity which is a deep copy of the original. * @private */ private _cloneRecursively; } import type { Component } from './components/component.js'; import type { MergedComponentOptions } from './components/component.js'; import { GraphNode } from '../scene/graph-node.js'; import type { AnimComponent } from './components/anim/component.js'; import type { AnimationComponent } from './components/animation/component.js'; import type { AudioListenerComponent } from './components/audio-listener/component.js'; import type { ButtonComponent } from './components/button/component.js'; import type { CameraComponent } from './components/camera/component.js'; import type { CollisionComponent } from './components/collision/component.js'; import type { ElementComponent } from './components/element/component.js'; import type { GSplatComponent } from './components/gsplat/component.js'; import type { JointComponent } from './components/joint/component.js'; import type { LayoutChildComponent } from './components/layout-child/component.js'; import type { LayoutGroupComponent } from './components/layout-group/component.js'; import type { LightComponent } from './components/light/component.js'; import type { ModelComponent } from './components/model/component.js'; import type { ParticleSystemComponent } from './components/particle-system/component.js'; import type { RenderComponent } from './components/render/component.js'; import type { RigidBodyComponent } from './components/rigid-body/component.js'; import type { ScreenComponent } from './components/screen/component.js'; import type { ScriptComponent } from './components/script/component.js'; import type { ScrollbarComponent } from './components/scrollbar/component.js'; import type { ScrollViewComponent } from './components/scroll-view/component.js'; import type { SoundComponent } from './components/sound/component.js'; import type { SpriteComponent } from './components/sprite/component.js'; import type { Script } from './script/script.js'; import type { AppBase } from './app-base.js';