415 lines
11 KiB
JavaScript
415 lines
11 KiB
JavaScript
/**
|
|
* R3.EntityManager
|
|
* @param apiComponent
|
|
* @constructor
|
|
*/
|
|
R3.EntityManager = function(
|
|
apiComponent
|
|
) {
|
|
/**
|
|
* The 'register' array is a register of each component currently loaded - when the linking
|
|
* system starts it also loads all the current components from the entity manager
|
|
* @type {Array}
|
|
*/
|
|
this.register = {};
|
|
|
|
this.idRegister = {};
|
|
|
|
this.defaultEntity = null;
|
|
|
|
this.instanceDisposal = [];
|
|
|
|
R3.Event.Subscribe(
|
|
R3.Event.INSTANCE_CREATED,
|
|
this.instanceCreated.bind(this)
|
|
);
|
|
|
|
R3.Event.Subscribe(
|
|
R3.Event.REMOVE_COMPONENT,
|
|
this.removeComponent.bind(this)
|
|
);
|
|
|
|
R3.Event.Subscribe(
|
|
R3.Event.INSTANCE_DISPOSAL,
|
|
this.removeInstances.bind(this)
|
|
)
|
|
|
|
// R3.Event.Subscribe(
|
|
// R3.Event.ENTITY_LOADED,
|
|
// this.entityLoaded.bind(this)
|
|
// );
|
|
|
|
|
|
};
|
|
|
|
R3.EntityManager.prototype.instanceCreated = function(data) {
|
|
|
|
/**
|
|
* Register this component in componentType to Component register
|
|
*/
|
|
if (R3.Utils.UndefinedOrNull(this.register[data.component.componentType])) {
|
|
this.register[data.component.componentType] = {};
|
|
}
|
|
|
|
if (R3.Utils.UndefinedOrNull(this.register[data.component.componentType][data.component.id])) {
|
|
this.register[data.component.componentType][data.component.id] = data.component;
|
|
}
|
|
|
|
/**
|
|
* Register this component in the Component.id to Component register
|
|
*/
|
|
if (R3.Utils.UndefinedOrNull(this.idRegister[data.component.id])) {
|
|
this.idRegister[data.component.id] = data.component;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* Removes the component from both registries (the component type to component register, and the component ID to
|
|
* component register)
|
|
* @param data
|
|
*/
|
|
R3.EntityManager.prototype.removeComponent = function(data) {
|
|
|
|
/**
|
|
* Sanity Check 1
|
|
*/
|
|
if (R3.Utils.UndefinedOrNull(this.register[data.component.componentType]) ||
|
|
R3.Utils.UndefinedOrNull(this.register[data.component.componentType][data.component.id])
|
|
) {
|
|
throw new Error('EntityManager register out of sync');
|
|
}
|
|
|
|
/**
|
|
* Delete the component from the componentType to Component register
|
|
*/
|
|
delete this.register[data.component.componentType][data.component.id];
|
|
if (R3.Utils.IsEmpty(this.register[data.component.componentType])) {
|
|
delete this.register[data.component.componentType];
|
|
}
|
|
|
|
/**
|
|
* Sanity Check 2
|
|
*/
|
|
if (R3.Utils.UndefinedOrNull(this.idRegister[data.component.id])){
|
|
throw new Error('EntityManager idRegister out of sync');
|
|
}
|
|
|
|
/**
|
|
* Delete the component from the ID to Component register
|
|
*/
|
|
delete this.idRegister[data.component.id];
|
|
|
|
/**
|
|
* Now we have to be quite careful - we only have one copy left of this component - the one in data.component.
|
|
* we need a handle on all instances in this object. We need to store them to allow the 'REMOVE_COMPONENT' event
|
|
* to finish getting handled, then we need to dispose of the instance themselves.
|
|
* This gives our systems and other objects a chance to cleanly stop relying on these components at which point
|
|
* we can safely clean up our instances.
|
|
*/
|
|
|
|
var component = data.component;
|
|
|
|
for (var property in component) {
|
|
|
|
if (property === 'parent') {
|
|
continue;
|
|
}
|
|
|
|
if (component.hasOwnProperty(property)) {
|
|
if (component[property] instanceof R3.Component) {
|
|
R3.Utils.PushUnique(this.instanceDisposal, component[property].instance);
|
|
}
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
R3.EntityManager.prototype.removeInstances = function(data) {
|
|
|
|
this.instanceDisposal = this.instanceDisposal.reduce(
|
|
function(result, instance) {
|
|
if (
|
|
R3.Utils.Defined(instance.dispose) &&
|
|
typeof instance.dispose === 'function'
|
|
) {
|
|
instance.dispose();
|
|
}
|
|
|
|
if (instance instanceof HTMLElement) {
|
|
instance.parentElement.removeChild(instance);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
[]
|
|
);
|
|
|
|
};
|
|
|
|
// R3.EntityManager.prototype.entityLoaded = function(data) {
|
|
// this.defaultEntity = data.entity;
|
|
// };
|
|
|
|
// R3.EntityManager.prototype.registerComponent = function(data) {
|
|
//
|
|
// // var updated = false;
|
|
//
|
|
// if (R3.Utils.UndefinedOrNull(this.register[data.component.componentType])) {
|
|
// this.register[data.component.componentType] = {};
|
|
// // R3.Event.Emit(
|
|
// // R3.Event.COMPONENT_TYPES_UPDATE,
|
|
// // {
|
|
// // componentType : data.component.componentType,
|
|
// // componentTypes : Object.keys(this.register)
|
|
// // }
|
|
// // );
|
|
// // updated = true;
|
|
// }
|
|
//
|
|
// if (R3.Utils.UndefinedOrNull(this.register[data.component.componentType][data.component.id])) {
|
|
// this.register[data.component.componentType][data.component.id] = data.component;
|
|
// // updated = true;
|
|
// }
|
|
//
|
|
// if (R3.Utils.UndefinedOrNull(this.idRegister[data.component.id])) {
|
|
// this.idRegister[data.component.id] = data.component;
|
|
// // updated = true;
|
|
// }
|
|
//
|
|
// // if (updated) {
|
|
// // R3.Event.Emit(
|
|
// // R3.Event.REGISTER_UPDATE,
|
|
// // {
|
|
// // componentType : data.component.componentType,
|
|
// // components : this.register[data.component.componentType],
|
|
// // idRegister : this.idRegister,
|
|
// // register : this.register
|
|
// // }
|
|
// // );
|
|
// // }
|
|
// };
|
|
|
|
|
|
/**
|
|
* Returns an entity by ID or null
|
|
* @param id
|
|
* @returns {*}
|
|
*/
|
|
R3.EntityManager.prototype.findEntityById = function(id) {
|
|
|
|
var entity = this.register[R3.Component.ENTITY][id];
|
|
|
|
if (entity) {
|
|
return entity;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
R3.EntityManager.prototype.findComponentById = function(id) {
|
|
return this.idRegister[id];
|
|
};
|
|
|
|
R3.EntityManager.prototype.findComponentByName = function(name) {
|
|
|
|
return Object.keys(this.idRegister).reduce(
|
|
function(result, componentId) {
|
|
|
|
if (this.idRegister[componentId].name === name) {
|
|
result = this.idRegister[componentId];
|
|
}
|
|
|
|
return result;
|
|
|
|
}.bind(this),
|
|
null
|
|
);
|
|
|
|
};
|
|
|
|
R3.EntityManager.prototype.findHelperByObject = function(object) {
|
|
|
|
if (typeof this.register[R3.Component.HELPER] === 'undefined') {
|
|
return null;
|
|
}
|
|
|
|
return Object.keys(this.register[R3.Component.HELPER]).reduce(
|
|
function(result, helperId) {
|
|
|
|
if (this.register[R3.Component.HELPER][helperId].object === object) {
|
|
result = this.register[R3.Component.HELPER][helperId];
|
|
}
|
|
|
|
return result;
|
|
}.bind(this),
|
|
null
|
|
);
|
|
|
|
};
|
|
|
|
R3.EntityManager.prototype.findSceneByObject = function(object) {
|
|
|
|
return Object.keys(this.register[R3.Component.SCENE]).reduce(
|
|
function(result, sceneId) {
|
|
|
|
if (
|
|
this.register[R3.Component.SCENE][sceneId].meshes.indexOf(object) !== -1 ||
|
|
this.register[R3.Component.SCENE][sceneId].lights.indexOf(object) !== -1
|
|
) {
|
|
result = this.register[R3.Component.SCENE][sceneId];
|
|
}
|
|
|
|
return result;
|
|
}.bind(this),
|
|
null
|
|
);
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* Adds an entity to this manager
|
|
* @param entity R3.Entity
|
|
*/
|
|
R3.EntityManager.prototype.addEntity = function(entity) {
|
|
this.entities.push(entity);
|
|
};
|
|
|
|
/**
|
|
* Returns entity by name
|
|
* @param name
|
|
* @returns {*}
|
|
*/
|
|
R3.EntityManager.prototype.queryByName = function(name) {
|
|
return this.entities.reduce(
|
|
function(result, entity){
|
|
if (entity.name === name) {
|
|
result = entity;
|
|
}
|
|
return result;
|
|
},
|
|
null
|
|
)
|
|
};
|
|
|
|
/**
|
|
* Removes an entity - do we remove all its components as well?
|
|
* @param entity R3.D3.Entity
|
|
* @returns boolean true if successful
|
|
*/
|
|
R3.EntityManager.prototype.removeEntity = function(entity) {
|
|
|
|
var index = this.entities.indexOf(entity);
|
|
|
|
if (index === -1) {
|
|
console.log('failed to remove entity : ', entity);
|
|
return false;
|
|
}
|
|
this.entities.splice(index, 1);
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Returns all the entities with the following components
|
|
* @param components R3.Component[]
|
|
*/
|
|
// R3.EntityManager.prototype.findEntities = function(components) {
|
|
//
|
|
// var entities = this.entities.reduce(
|
|
// function(result, entity) {
|
|
//
|
|
// var hasAllComponents = components.reduce(
|
|
// function(componentResult, component) {
|
|
// if (!entity.hasComponent(component)) {
|
|
// componentResult = false;
|
|
// }
|
|
// return componentResult;
|
|
// },
|
|
// true
|
|
// );
|
|
//
|
|
// if (hasAllComponents) {
|
|
// result.push(entity);
|
|
// }
|
|
//
|
|
// return result;
|
|
// },
|
|
// []
|
|
// );
|
|
//
|
|
// return entities;
|
|
// };
|
|
|
|
/**
|
|
* Returns all actual components of all entities that contain this component
|
|
* More efficient
|
|
* @param componentTypes (array of component types or a single component type)
|
|
*/
|
|
R3.EntityManager.prototype.queryComponents = function(componentTypes) {
|
|
|
|
var result = [];
|
|
|
|
if (componentTypes instanceof Array) {
|
|
componentTypes.map(
|
|
function(componentType) {
|
|
|
|
if (typeof this.register[componentType] === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
Object.keys(this.register[componentType]).map(
|
|
function(componentId) {
|
|
result.push(this.register[componentType][componentId]);
|
|
}.bind(this)
|
|
)
|
|
}.bind(this)
|
|
)
|
|
} else {
|
|
|
|
if (typeof this.register[componentTypes] === 'undefined') {
|
|
return result;
|
|
}
|
|
|
|
Object.keys(this.register[componentTypes]).map(
|
|
function(componentId) {
|
|
result.push(this.register[componentTypes][componentId]);
|
|
}.bind(this)
|
|
)
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**
|
|
* Slower way of retrieving objects
|
|
* @param constructors (array of constructors, or a constructor)
|
|
* @returns {*}
|
|
*/
|
|
R3.EntityManager.prototype.findComponentsByConstructor = function(constructors) {
|
|
return Object.keys(this.idRegister).reduce(
|
|
function(result, componentId) {
|
|
if (constructors instanceof Array) {
|
|
|
|
constructors.map(
|
|
function(constructor) {
|
|
if (this.idRegister[componentId] instanceof constructor) {
|
|
result.push(this.idRegister[componentId]);
|
|
}
|
|
}.bind(this)
|
|
)
|
|
|
|
} else {
|
|
|
|
if (this.idRegister[componentId] instanceof constructors) {
|
|
result.push(this.idRegister[componentId]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}.bind(this),
|
|
[]
|
|
);
|
|
};
|