com.artemis.ComponentManager Maven / Gradle / Ivy
package com.artemis;
import java.util.BitSet;
import com.artemis.utils.Bag;
import com.artemis.utils.ImmutableBag;
import com.artemis.utils.IntBag;
import com.artemis.utils.reflect.ClassReflection;
import com.artemis.utils.reflect.Constructor;
import com.artemis.utils.reflect.ReflectionException;
/**
* Handles the association between entities and their components.
*
* Only one component manager exists per {@link World} instance,
* managed by the world.
*
*
* @author Arni Arent
*/
public class ComponentManager extends Manager {
/** Holds all components grouped by type. */
private final Bag> componentsByType;
/** Holds all packed components sorted by type index. */
private final Bag packedComponents;
private final Bag packedComponentOwners;
/** Collects all Entites marked for deletion from this ComponentManager. */
private final IntBag deleted;
private final ComponentPool pooledComponents;
private int highestSeenEntityId;
protected final ComponentTypeFactory typeFactory;
/**
* Creates a new instance of {@link ComponentManager}.
*/
protected ComponentManager(int entityContainerSize) {
this.highestSeenEntityId = entityContainerSize;
componentsByType = new Bag>();
packedComponents = new Bag();
packedComponentOwners = new Bag();
pooledComponents = new ComponentPool();
deleted = new IntBag();
typeFactory = new ComponentTypeFactory();
}
protected T create(Entity owner, Class componentClass) {
ComponentType type = typeFactory.getTypeFor(componentClass);
T component = create(owner, type);
return component;
}
@SuppressWarnings("unchecked")
T create(Entity owner, ComponentType type) {
Class componentClass = (Class)type.getType();
T component = null;
switch (type.getTaxonomy())
{
case BASIC:
component = newInstance(componentClass, false);
break;
case PACKED:
PackedComponent packedComponent = packedComponents.safeGet(type.getIndex());
if (packedComponent == null) {
packedComponent = (PackedComponent)newInstance(
componentClass, type.packedHasWorldConstructor);
packedComponents.set(type.getIndex(), packedComponent);
}
getPackedComponentOwners(type).set(owner.getId());
ensurePackedComponentCapacity(owner.id);
packedComponent.forEntity(owner.id);
component = (T)packedComponent;
break;
case POOLED:
try {
reclaimPooled(owner, type);
component = (T)pooledComponents.obtain((Class)componentClass, type);
break;
} catch (ReflectionException e) {
throw new InvalidComponentException(componentClass, "Unable to instantiate component.", e);
}
default:
throw new InvalidComponentException(componentClass, " unknown component type: " + type.getTaxonomy());
}
addComponent(owner, type, component);
return component;
}
private void reclaimPooled(Entity owner, ComponentType type) {
Bag components = componentsByType.safeGet(type.getIndex());
if (components == null)
return;
Component old = components.safeGet(owner.id);
if (old != null)
pooledComponents.free((PooledComponent)old, type);
}
private void ensurePackedComponentCapacity(int entityId) {
if ((highestSeenEntityId - 1) < entityId) {
highestSeenEntityId = entityId;
for (int i = 0, s = packedComponents.size(); s > i; i++) {
PackedComponent component = packedComponents.get(i);
if (component == null)
continue;
component.ensureCapacity(entityId + 1);
}
}
}
protected BitSet getPackedComponentOwners(ComponentType type)
{
BitSet owners = packedComponentOwners.safeGet(type.getIndex());
if (owners == null) {
owners = new BitSet();
packedComponentOwners.set(type.getIndex(), owners);
}
return owners;
}
@SuppressWarnings("unchecked")
T newInstance(Class componentClass, boolean constructorHasWorldParameter) {
try {
if (constructorHasWorldParameter) {
Constructor constructor = ClassReflection.getConstructor(componentClass, World.class);
return (T) constructor.newInstance(world);
} else {
return ClassReflection.newInstance(componentClass);
}
} catch (ReflectionException e) {
throw new InvalidComponentException(componentClass, "Unable to instantiate component.", e);
}
}
/**
* Removes all components from the entity associated in this manager.
*
* @param entityId
* the entity to remove components from
*/
private void removeComponents(int entityId) {
BitSet componentBits = world.getEntityManager().componentBits(entityId);
for (int i = componentBits.nextSetBit(0); i >= 0; i = componentBits.nextSetBit(i+1)) {
switch (typeFactory.getTaxonomy(i)) {
case BASIC:
componentsByType.get(i).set(entityId, null);
break;
case POOLED:
Component pooled = componentsByType.get(i).get(entityId);
pooledComponents.free((PooledComponent)pooled, i);
componentsByType.get(i).set(entityId, null);
break;
case PACKED:
PackedComponent pc = packedComponents.get(i);
pc.forEntity(entityId);
pc.reset();
break;
default:
throw new InvalidComponentException(Component.class, " unknown component type: " + typeFactory.getTaxonomy(i));
}
}
}
@Override
protected void dispose() {
for (int i = 0, s = packedComponents.size(); s > i; i++) {
PackedComponent component = packedComponents.get(i);
if (component == null)
continue;
if (component instanceof PackedComponent.DisposedWithWorld) {
((PackedComponent.DisposedWithWorld)component).free(world);
}
}
}
/**
* Adds the component of the given type to the entity.
*
* Only one component of given type can be associated with a entity at the
* same time.
*
*
* @param e
* the entity to add to
* @param type
* the type of component being added
* @param component
* the component to add
*/
protected void addComponent(Entity e, ComponentType type, Component component) {
if (type.isPackedComponent())
addPackedComponent(type, (PackedComponent)component);
else
addBasicComponent(e, type, component); // pooled components are handled the same
}
protected void addComponents(Entity e, Archetype archetype) {
ComponentType[] types = archetype.types;
for (int i = 0, s = types.length; s > i; i++) {
create(e, types[i]);
}
}
private void addPackedComponent(ComponentType type, PackedComponent component) {
PackedComponent packed = packedComponents.safeGet(type.getIndex());
if (packed == null) {
packedComponents.set(type.getIndex(), component);
}
}
private void addBasicComponent(Entity e, ComponentType type, Component component)
{
Bag components = componentsByType.safeGet(type.getIndex());
if (components == null) {
components = new Bag(highestSeenEntityId);
componentsByType.set(type.getIndex(), components);
}
components.set(e.id, component);
}
/**
* Removes the component of given type from the entity.
*
* @param e
* the entity to remove from
* @param type
* the type of component being removed
*/
protected void removeComponent(Entity e, ComponentType type) {
int index = type.getIndex();
switch (type.getTaxonomy()) {
case BASIC:
componentsByType.get(index).set(e.id, null);
break;
case POOLED:
Component pooled = componentsByType.get(index).get(e.id);
pooledComponents.free((PooledComponent)pooled, type);
componentsByType.get(index).set(e.id, null);
break;
case PACKED:
PackedComponent pc = packedComponents.get(index);
pc.forEntity(e.id);
pc.reset();
getPackedComponentOwners(type).clear(e.id);
break;
default:
throw new InvalidComponentException(type.getType(), " unknown component type: " + type.getTaxonomy());
}
}
/**
* Get all components from all entities for a given type.
*
* @param type
* the type of components to get
* @return a bag containing all components of the given type
*/
protected Bag getComponentsByType(ComponentType type) {
if (type.isPackedComponent())
throw new InvalidComponentException(type.getType(), "PackedComponent types aren't supported.");
Bag components = componentsByType.safeGet(type.getIndex());
if(components == null) {
components = new Bag();
componentsByType.set(type.getIndex(), components);
}
return components;
}
public ImmutableBag getComponentTypes() {
return typeFactory.types;
}
/**
* Get a component of an entity.
*
* @param e
* the entity associated with the component
* @param type
* the type of component to get
* @return the component of given type
*/
protected Component getComponent(Entity e, ComponentType type) {
if (type.isPackedComponent()) {
PackedComponent component = packedComponents.safeGet(type.getIndex());
if (component != null) component.forEntity(e.id);
return component;
} else {
Bag components = componentsByType.safeGet(type.getIndex());
if (components != null && components.isIndexWithinBounds(e.id)) {
return components.get(e.id);
}
}
return null;
}
/**
* Get all component associated with an entity.
*
* @param e
* the entity to get components from
* @param fillBag
* a bag to be filled with components
* @return the {@code fillBag}, filled with the entities components
*/
public Bag getComponentsFor(Entity e, Bag fillBag) {
BitSet componentBits = e.getComponentBits();
for (int i = componentBits.nextSetBit(0); i >= 0; i = componentBits.nextSetBit(i+1)) {
if (typeFactory.isPackedComponent(i)) {
fillBag.add(packedComponents.get(i));
} else {
fillBag.add(componentsByType.get(i).get(e.id));
}
}
return fillBag;
}
@Override
public void deleted(int entityId) {
deleted.add(entityId);
}
@Override
public void added(int entityId) {
if ((highestSeenEntityId - 1) < entityId) {
ensurePackedComponentCapacity(entityId);
}
}
/**
* Removes all components from entities marked for deletion.
*/
protected void clean() {
int s = deleted.size();
if(s > 0) {
int[] ids = deleted.getData();
for(int i = 0; s > i; i++) {
removeComponents(ids[i]);
}
deleted.setSize(0);
}
}
public ComponentTypeFactory getTypeFactory() {
return typeFactory;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy