UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

228 lines (225 loc) 7.29 kB
import { guid } from '../core/guid.js'; import { GraphNode } from '../scene/graph-node.js'; import { getApplication } from './globals.js'; const cmpStaticOrder = (a, b)=>a.constructor.order - b.constructor.order; const sortStaticOrder = (arr)=>arr.sort(cmpStaticOrder); const _enableList = []; const tmpPool = []; const getTempArray = ()=>{ return tmpPool.pop() ?? []; }; const releaseTempArray = (a)=>{ a.length = 0; tmpPool.push(a); }; class Entity extends GraphNode { static{ this.EVENT_DESTROY = 'destroy'; } constructor(name, app = getApplication()){ super(name), this.c = {}, this._destroying = false, this._guid = null, this._template = false; this._app = app; } addComponent(type, data) { const system = this._app.systems[type]; if (!system) { return null; } if (this.c[type]) { return null; } return system.addComponent(this, data); } removeComponent(type) { const system = this._app.systems[type]; if (!system) { return; } if (!this.c[type]) { return; } system.removeComponent(this); } findComponent(type) { const entity = this.findOne((entity)=>entity.c?.[type]); return entity && entity.c[type]; } findComponents(type) { return this.find((entity)=>entity.c?.[type]).map((entity)=>entity.c[type]); } findScript(nameOrType) { const entity = this.findOne((node)=>node.c?.script?.has(nameOrType)); return entity?.c.script.get(nameOrType); } findScripts(nameOrType) { const entities = this.find((node)=>node.c?.script?.has(nameOrType)); return entities.map((entity)=>entity.c.script.get(nameOrType)); } getGuid() { if (!this._guid) { this.setGuid(guid.create()); } return this._guid; } setGuid(guid) { const index = this._app._entityIndex; if (this._guid) { delete index[this._guid]; } this._guid = guid; index[this._guid] = this; } _notifyHierarchyStateChanged(node, enabled) { let enableFirst = false; if (node === this && _enableList.length === 0) { enableFirst = true; } node._beingEnabled = true; node._onHierarchyStateChanged(enabled); if (node._onHierarchyStatePostChanged) { _enableList.push(node); } const c = node._children; for(let i = 0, len = c.length; i < len; i++){ if (c[i]._enabled) { this._notifyHierarchyStateChanged(c[i], enabled); } } node._beingEnabled = false; if (enableFirst) { for(let i = 0; i < _enableList.length; i++){ _enableList[i]._onHierarchyStatePostChanged(); } _enableList.length = 0; } } _onHierarchyStateChanged(enabled) { super._onHierarchyStateChanged(enabled); const components = this._getSortedComponents(); for(let i = 0; i < components.length; i++){ const component = components[i]; if (component.enabled) { if (enabled) { component.onEnable(); } else { component.onDisable(); } } } releaseTempArray(components); } _onHierarchyStatePostChanged() { const components = this._getSortedComponents(); for(let i = 0; i < components.length; i++){ components[i].onPostStateChange(); } releaseTempArray(components); } findByGuid(guid) { if (this._guid === guid) return this; const e = this._app._entityIndex[guid]; if (e && (e === this || e.isDescendantOf(this))) { return e; } return null; } destroy() { this._destroying = true; for(const name in this.c){ this.c[name].enabled = false; } for(const name in this.c){ this.c[name].system.removeComponent(this); } super.destroy(); if (this._guid) { delete this._app._entityIndex[this._guid]; } this._destroying = false; } clone() { const duplicatedIdsMap = {}; const clone = this._cloneRecursively(duplicatedIdsMap); duplicatedIdsMap[this.getGuid()] = clone; resolveDuplicatedEntityReferenceProperties(this, this, clone, duplicatedIdsMap); return clone; } _getSortedComponents() { const components = this.c; const sortedArray = getTempArray(); let needSort = 0; for(const type in components){ if (components.hasOwnProperty(type)) { const component = components[type]; needSort |= component.constructor.order !== 0; sortedArray.push(component); } } if (needSort && sortedArray.length > 1) { sortStaticOrder(sortedArray); } return sortedArray; } _cloneRecursively(duplicatedIdsMap) { const clone = new this.constructor(undefined, this._app); super._cloneInternal(clone); for(const type in this.c){ const component = this.c[type]; component.system.cloneComponent(this, clone); } for(let i = 0; i < this._children.length; i++){ const oldChild = this._children[i]; if (oldChild instanceof Entity) { const newChild = oldChild._cloneRecursively(duplicatedIdsMap); clone.addChild(newChild); duplicatedIdsMap[oldChild.getGuid()] = newChild; } } return clone; } } function resolveDuplicatedEntityReferenceProperties(oldSubtreeRoot, oldEntity, newEntity, duplicatedIdsMap) { if (oldEntity instanceof Entity) { const components = oldEntity.c; for(const componentName in components){ const component = components[componentName]; const entityProperties = component.system.getPropertiesOfType('entity'); for(let i = 0, len = entityProperties.length; i < len; i++){ const propertyDescriptor = entityProperties[i]; const propertyName = propertyDescriptor.name; const oldEntityReferenceId = component[propertyName]; const entityIsWithinOldSubtree = !!oldSubtreeRoot.findByGuid(oldEntityReferenceId); if (entityIsWithinOldSubtree) { const newEntityReferenceId = duplicatedIdsMap[oldEntityReferenceId].getGuid(); if (newEntityReferenceId) { newEntity.c[componentName][propertyName] = newEntityReferenceId; } } } } if (components.script) { newEntity.script.resolveDuplicatedEntityReferenceProperties(components.script, duplicatedIdsMap); } if (components.render) { newEntity.render.resolveDuplicatedEntityReferenceProperties(components.render, duplicatedIdsMap); } if (components.button) { newEntity.button.resolveDuplicatedEntityReferenceProperties(components.button, duplicatedIdsMap); } if (components.scrollview) { newEntity.scrollview.resolveDuplicatedEntityReferenceProperties(components.scrollview, duplicatedIdsMap); } if (components.scrollbar) { newEntity.scrollbar.resolveDuplicatedEntityReferenceProperties(components.scrollbar, duplicatedIdsMap); } if (components.anim) { newEntity.anim.resolveDuplicatedEntityReferenceProperties(components.anim, duplicatedIdsMap); } const _old = oldEntity.children.filter((e)=>e instanceof Entity); const _new = newEntity.children.filter((e)=>e instanceof Entity); for(let i = 0, len = _old.length; i < len; i++){ resolveDuplicatedEntityReferenceProperties(oldSubtreeRoot, _old[i], _new[i], duplicatedIdsMap); } } } export { Entity };