Added docstrings and some exception handling
This commit is contained in:
parent
6e000492b0
commit
22a75f5559
@ -21,21 +21,79 @@ import java.util.ArrayList;
|
|||||||
|
|
||||||
|
|
||||||
class ComponentArray{
|
class ComponentArray{
|
||||||
List<Object> componentArray = new ArrayList<>();
|
// The object data array
|
||||||
|
private List<Object> componentArray = new ArrayList<>();
|
||||||
|
// The mappings between data and entity
|
||||||
|
private Map<Integer, Integer> entityComponentDataMap = new HashMap<>();
|
||||||
|
private Map<Integer, Integer> componentDataEntityMap = new HashMap<>();
|
||||||
|
|
||||||
Map<Integer, Integer> entityComponentDataMap = new HashMap<>();
|
/**
|
||||||
Map<Integer, Integer> componentDataEntityMap = new HashMap<>();
|
* Gets the data Object associated with the entity.
|
||||||
|
* @param entity the entity to find data for
|
||||||
|
* @return the Object if the data exists, else null
|
||||||
|
*/
|
||||||
|
protected Object getData(int entity){
|
||||||
|
try{
|
||||||
|
Object result = componentArray.get(entityComponentDataMap.get(entity));
|
||||||
|
if (result == null) ECS.writeErr("Attempted to retrieve non-existent data");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (IndexOutOfBoundsException e){
|
||||||
|
ECS.writeErr("Index out-of-bounds");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void entityDestroyed(int entity){
|
/**
|
||||||
|
* Inserts the provided component and associates it with the entity.
|
||||||
|
* @param entity the entity to associate data to
|
||||||
|
* @param component the component data
|
||||||
|
* @return true if successful, false if the entity is already subscribed to the component
|
||||||
|
*/
|
||||||
|
protected boolean insertData(int entity, Object component){
|
||||||
if (entityComponentDataMap.containsKey(entity)){
|
if (entityComponentDataMap.containsKey(entity)){
|
||||||
removeData(entity);
|
ECS.writeErr("Entity is already subscribed to the component");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Put data at the end of the componentArray
|
||||||
|
int index = componentArray.size();
|
||||||
|
entityComponentDataMap.put(entity, index);
|
||||||
|
componentDataEntityMap.put(index, entity);
|
||||||
|
componentArray.add(component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves component data to another entity. (Copies, deletes and inserts data)
|
||||||
|
* @param sourceEntity
|
||||||
|
* @param destinationEntity
|
||||||
|
* @return 0 if successful, -1 if the object is null, -2 if a NullPointerException occurred, and -3 if inserting data failed
|
||||||
|
*/
|
||||||
|
protected int moveData(int sourceEntity, int destinationEntity){
|
||||||
|
try{
|
||||||
|
Object data = entityComponentDataMap.get(sourceEntity);
|
||||||
|
if (data == null) return -1;
|
||||||
|
else if (insertData(destinationEntity, data))
|
||||||
|
{
|
||||||
|
removeData(sourceEntity);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else return -3;
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeData(int entity){
|
/**
|
||||||
|
* Removes the data associated with the entity.
|
||||||
|
* @param entity the entity to remove the data from
|
||||||
|
* @return true if the data was removed. If the data isn't found then returns false
|
||||||
|
*/
|
||||||
|
protected boolean removeData(int entity){
|
||||||
if (!entityComponentDataMap.containsKey(entity)){
|
if (!entityComponentDataMap.containsKey(entity)){
|
||||||
ECS.writeErr("Attempted to remove non-existent entity");
|
ECS.writeErr("Attempted to remove non-existent entity");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// Get the componentData index of the entity
|
// Get the componentData index of the entity
|
||||||
int removedComponentDataIndex = entityComponentDataMap.get(entity);
|
int removedComponentDataIndex = entityComponentDataMap.get(entity);
|
||||||
@ -48,40 +106,7 @@ class ComponentArray{
|
|||||||
// Finally, remomve the last elements
|
// Finally, remomve the last elements
|
||||||
entityComponentDataMap.remove(entity);
|
entityComponentDataMap.remove(entity);
|
||||||
componentDataEntityMap.remove(componentArray.size() -1);
|
componentDataEntityMap.remove(componentArray.size() -1);
|
||||||
|
|
||||||
componentArray.remove(componentArray.size() -1);
|
componentArray.remove(componentArray.size() -1);
|
||||||
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
public void insertData(int entity, Object component){
|
|
||||||
if (entityComponentDataMap.containsKey(entity)){
|
|
||||||
ECS.writeErr("Entity is already subscribed to the component");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int index = componentArray.size();
|
|
||||||
entityComponentDataMap.put(entity, index);
|
|
||||||
componentDataEntityMap.put(index, entity);
|
|
||||||
|
|
||||||
componentArray.add(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves component data to another entity. (Copies, deletes and inserts data)
|
|
||||||
* @param sourceEntity
|
|
||||||
* @param destinationEntity
|
|
||||||
*/
|
|
||||||
public void moveData(int sourceEntity, int destinationEntity){
|
|
||||||
Object data = entityComponentDataMap.get(sourceEntity);
|
|
||||||
removeData(sourceEntity);
|
|
||||||
insertData(destinationEntity, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getData(int entity){
|
|
||||||
if (!entityComponentDataMap.containsKey(entity)){
|
|
||||||
ECS.writeErr("Attempted to retrieve non-existent data");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return componentArray.get(entityComponentDataMap.get(entity));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,54 +10,123 @@ package nz.ac.massey.javaecs;
|
|||||||
*/
|
*/
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.BitSet;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
class ComponentManager{
|
class ComponentManager{
|
||||||
Map<Type, ComponentArray> componentArrays = new HashMap<>();
|
private Map<Type, ComponentArray> componentArrays = new HashMap<>();
|
||||||
Map<Type, Integer> componentPosIndex = new HashMap<>();
|
private Map<Type, Integer> componentPosIndex = new HashMap<>();
|
||||||
int componentPos = 0;
|
private Map<Integer, Type> indexComponentType = new HashMap<>();
|
||||||
|
|
||||||
public boolean registerComponent(Type type){
|
/**
|
||||||
|
* Adds the specified component to the provided entity
|
||||||
|
* @param componentType the class type of the component to add
|
||||||
|
* @param componentData the component data to associate
|
||||||
|
* @param entity the entity to associate data to
|
||||||
|
*/
|
||||||
|
protected boolean addComponentToEntity(Type componentType, Object componentData, int entity){
|
||||||
|
return componentArrays.get(componentType).insertData(entity, componentData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signals to the ComponentManager the entity was destroyed. All component data references should be removed.
|
||||||
|
* @param entity the entity that was destroyed.
|
||||||
|
*/
|
||||||
|
public void entityDestroyed(int entity){
|
||||||
|
for (Type key : componentArrays.keySet()) {
|
||||||
|
componentArrays.get(key).removeData(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the component data associated with the entity
|
||||||
|
* @param componentType the class type of data to look for
|
||||||
|
* @param entity the entity to find data for
|
||||||
|
* @return the Object data found, or null if it was not found
|
||||||
|
*/
|
||||||
|
public Object getComponent(Type componentType, int entity){
|
||||||
|
return componentArrays.get(componentType).getData(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the registration index of the component type
|
||||||
|
* @param type the class type of the component
|
||||||
|
* @return the index of the component type, or -1 if it isn't found
|
||||||
|
*/
|
||||||
|
protected Integer getComponentIndex(Type type){
|
||||||
|
try{
|
||||||
|
return componentPosIndex.get(type);
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of the component at the provided index
|
||||||
|
* @param index the index of the component
|
||||||
|
* @return the class type of the index. `null` if not found
|
||||||
|
*/
|
||||||
|
protected Type getComponentType(Integer index){
|
||||||
|
try{
|
||||||
|
return indexComponentType.get(index);
|
||||||
|
}
|
||||||
|
catch (NullPointerException e){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves a single component data from one entity to another
|
||||||
|
* @param sourceEntity the entity to move data from
|
||||||
|
* @param destinationEntity the entity to move data to
|
||||||
|
* @param component the component class type to consider
|
||||||
|
* @return true if the component was moved successfully, else false
|
||||||
|
*/
|
||||||
|
protected boolean moveComponentData(int sourceEntity, int destinationEntity, Type component){
|
||||||
|
if (componentArrays.get(component).moveData(sourceEntity, destinationEntity) == 0){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves all component data from one entity to another
|
||||||
|
* @param sourceEntity the entity to move data from
|
||||||
|
* @param destinationEntity the entity to move data to
|
||||||
|
* @param sourceRegistrations the component registrations of the source entity
|
||||||
|
*/
|
||||||
|
protected void moveAllComponentData(int sourceEntity, int destinationEntity, BitSet sourceRegistrations){
|
||||||
|
int result = sourceRegistrations.nextSetBit(0);
|
||||||
|
while (result != -1){
|
||||||
|
Type key = indexComponentType.get(result);
|
||||||
|
componentArrays.get(key).moveData(sourceEntity, destinationEntity);
|
||||||
|
sourceRegistrations.nextSetBit(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the component type
|
||||||
|
* @param type the class type to register
|
||||||
|
* @return true if the component was registered successfully, else false
|
||||||
|
*/
|
||||||
|
protected boolean registerComponent(Type type){
|
||||||
if (componentArrays.containsKey(type)){
|
if (componentArrays.containsKey(type)){
|
||||||
ECS.writeErr("Component " + type.getTypeName() + " is already registered");
|
ECS.writeErr("Component " + type.getTypeName() + " is already registered");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
componentArrays.put(type, new ComponentArray());
|
componentArrays.put(type, new ComponentArray());
|
||||||
componentPosIndex.put(type, componentPos++);
|
indexComponentType.put(componentPosIndex.size(), type);
|
||||||
|
componentPosIndex.put(type, componentPosIndex.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addComponentToEntity(Type componentName, Object componentData, int entity){
|
|
||||||
componentArrays.get(componentName).insertData(entity, componentData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeComponentFromEntity(Type componentName, int entity){
|
|
||||||
componentArrays.get(componentName).removeData(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Java does not allow reflective typing, so must cast this in the retrieving function.
|
|
||||||
public Object getComponent(Type componentType, int entity){
|
|
||||||
return componentArrays.get(componentType).getData(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void entityDestroyed(int entity){
|
|
||||||
for (Type key : componentArrays.keySet()) {
|
|
||||||
componentArrays.get(key).entityDestroyed(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getComponentIndex(Type type){
|
|
||||||
return componentPosIndex.get(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves component data from one entity to another
|
* Removes the specified component from the entity
|
||||||
* @param sourceEntity
|
* @param componentType the class type of the component to remove
|
||||||
* @param destinationEntity
|
* @param entity the entity to remove the component from
|
||||||
*/
|
*/
|
||||||
public void moveComponentData(int sourceEntity, int destinationEntity){
|
public boolean removeComponentFromEntity(Type componentType, int entity){
|
||||||
for (Type key : componentArrays.keySet()) {
|
return componentArrays.get(componentType).removeData(entity);
|
||||||
componentArrays.get(key).moveData(sourceEntity, destinationEntity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ import java.util.BitSet;
|
|||||||
/**
|
/**
|
||||||
* The ECS manager.
|
* The ECS manager.
|
||||||
* <p>
|
* <p>
|
||||||
* See <href src="https://git.software.kauripeak.co.nz/BrychanD/JavaECS">https://git.software.kauripeak.co.nz/BrychanD/JavaECS</href>
|
* See https://git.software.kauripeak.co.nz/BrychanD/JavaECS
|
||||||
* for documentation and more information.
|
* for documentation and more information.
|
||||||
*/
|
*/
|
||||||
public class ECS {
|
public class ECS {
|
||||||
@ -153,7 +153,7 @@ public class ECS {
|
|||||||
systemManager.setRegistrationSignature(system, signature);
|
systemManager.setRegistrationSignature(system, signature);
|
||||||
}
|
}
|
||||||
Integer getMaxEntities(){
|
Integer getMaxEntities(){
|
||||||
return entityManager.currentSize;
|
return entityManager.getMaxSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encapsulate syserr writes so they may be redirected out of the lib
|
// Encapsulate syserr writes so they may be redirected out of the lib
|
||||||
|
@ -18,7 +18,15 @@ import java.util.HashSet;
|
|||||||
abstract class ECSSystem{
|
abstract class ECSSystem{
|
||||||
Set<Integer> entities = new HashSet<>();
|
Set<Integer> entities = new HashSet<>();
|
||||||
|
|
||||||
// Abstractions are included to remind the library user these two implementations (and/or variations) should be implemented
|
/**
|
||||||
|
* Implement additional parameterised init() functions as required.
|
||||||
|
* These should run once, when the system is first initialised.
|
||||||
|
*/
|
||||||
abstract void init();
|
abstract void init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement additional parameterised update() functions as required.
|
||||||
|
* These should be run each game loop or otherwise sensible regular interval
|
||||||
|
*/
|
||||||
abstract void update();
|
abstract void update();
|
||||||
}
|
}
|
@ -1,10 +1,7 @@
|
|||||||
package nz.ac.massey.javaecs;
|
package nz.ac.massey.javaecs;
|
||||||
/**
|
/**
|
||||||
* Entity Manager
|
* Entity Manager
|
||||||
* This class manages entity allocations; keeping a list
|
* Controls adding and removing entities, and registration and unregistration of components to specific entities.
|
||||||
* of all unassigned entities values.
|
|
||||||
* Additionally, handles setting the registered component
|
|
||||||
* flags for an entity.
|
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Brychan Dempsey - brychand@hotmail.com
|
* Brychan Dempsey - brychand@hotmail.com
|
||||||
@ -24,48 +21,83 @@ import java.util.ArrayList;
|
|||||||
* I.e. Controls adding and removing entities, and registration and deregistration of components.
|
* I.e. Controls adding and removing entities, and registration and deregistration of components.
|
||||||
*/
|
*/
|
||||||
class EntityManager{
|
class EntityManager{
|
||||||
Queue<Integer> unusedEntities;
|
private Queue<Integer> unusedEntities;
|
||||||
List<BitSet> entityRegistrations;
|
private List<BitSet> entityRegistrations;
|
||||||
int currentSize;
|
private int maxSize = 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the EntityManager with the default max size of 1024
|
||||||
|
*/
|
||||||
public EntityManager(){
|
public EntityManager(){
|
||||||
currentSize = 1024;
|
|
||||||
unusedEntities = new LinkedList<>();
|
unusedEntities = new LinkedList<>();
|
||||||
entityRegistrations = new ArrayList<>();
|
entityRegistrations = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < 1024; i++) {
|
for (int i = 0; i < maxSize; i++) {
|
||||||
unusedEntities.add(i);
|
unusedEntities.add(i);
|
||||||
entityRegistrations.add(new BitSet());
|
entityRegistrations.add(null); // Leave bitsets out as if they are null the entity isn't initialised properly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Initialise the EntityManager with the provided maximum size
|
||||||
|
* @param maxEntities the maximum number of entities to allow
|
||||||
|
*/
|
||||||
public EntityManager(int maxEntities){
|
public EntityManager(int maxEntities){
|
||||||
currentSize = maxEntities;
|
maxSize = maxEntities;
|
||||||
unusedEntities = new LinkedList<>();
|
unusedEntities = new LinkedList<>();
|
||||||
entityRegistrations = new ArrayList<>();
|
entityRegistrations = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < maxEntities; i++) {
|
for (int i = 0; i < maxEntities; i++) {
|
||||||
unusedEntities.add(i);
|
unusedEntities.add(i);
|
||||||
entityRegistrations.add(new BitSet());
|
entityRegistrations.add(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
public Integer addEntity(){
|
* Creates a new entity
|
||||||
|
* @return the index of the new entity, or -1 if there is no more available entities
|
||||||
|
*/
|
||||||
|
protected Integer addEntity(){
|
||||||
if (unusedEntities.size() == 0){
|
if (unusedEntities.size() == 0){
|
||||||
ECS.writeErr("No available space to create a new entity");
|
ECS.writeErr("No available space to create a new entity");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return unusedEntities.remove();
|
int result = unusedEntities.remove();
|
||||||
|
entityRegistrations.set(result, new BitSet());
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeEntity(int entity){
|
/**
|
||||||
unusedEntities.add(entity);
|
* Gets the current maximum size
|
||||||
entityRegistrations.get(entity).clear();
|
* @return the value of currentSize
|
||||||
|
*/
|
||||||
|
protected Integer getMaxSize(){
|
||||||
|
return maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean registerComponent(int component, int entity){
|
/**
|
||||||
if (entity >= currentSize){
|
* Gets the BitSet containing the registrations of the entity
|
||||||
|
* @param entity the entity whose BitSet to retrieve
|
||||||
|
* @return the BitSet of the provided entity
|
||||||
|
*/
|
||||||
|
protected BitSet getRegistrations(int entity){
|
||||||
|
try{
|
||||||
|
return entityRegistrations.get(entity);
|
||||||
|
}
|
||||||
|
catch (IndexOutOfBoundsException e){
|
||||||
|
ECS.writeErr("Index out of bounds error getting registrations for " + entity + ";\nThe entity might not exist");
|
||||||
|
return new BitSet(); // Using a blank BitSet will retain data safety (that is, no data will be modified)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the specified component index to the entity
|
||||||
|
* @param component the index of the component to register
|
||||||
|
* @param entity the entity to register
|
||||||
|
* @return true if the operation was successful
|
||||||
|
*/
|
||||||
|
protected boolean registerComponent(int component, int entity){
|
||||||
|
if (entity >= maxSize){
|
||||||
ECS.writeErr("Attempted to assign a component to non-existent entity: " + entity);
|
ECS.writeErr("Attempted to assign a component to non-existent entity: " + entity);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -80,45 +112,67 @@ class EntityManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean unregisterComponent(int component, int entity){
|
/**
|
||||||
|
* Adds the entity index back into unusedEntities, and sets the registrations to null
|
||||||
|
* <p>
|
||||||
|
* <b>Does not handle associated data</b> Use the method in ECS to remove entities cleanly
|
||||||
|
* @param entity the entity to remove
|
||||||
|
*/
|
||||||
|
protected void removeEntity(int entity){
|
||||||
|
unusedEntities.add(entity);
|
||||||
|
entityRegistrations.set(entity, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the entity's registrations to the provided BitSet
|
||||||
|
* @param entity the entity to set
|
||||||
|
* @param registrations the preset registrations
|
||||||
|
*/
|
||||||
|
protected void setRegistrations(int entity, BitSet registrations){
|
||||||
|
entityRegistrations.set(entity, registrations);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the specified component from the entity
|
||||||
|
* <p>
|
||||||
|
* <b>Does not handle component data</b> Use the method in ECS to remove components cleanly
|
||||||
|
* @param component the component index to remove
|
||||||
|
* @param entity the entity to remove
|
||||||
|
* @return true if successful
|
||||||
|
*/
|
||||||
|
protected boolean unregisterComponent(int component, int entity){
|
||||||
try{
|
try{
|
||||||
if (entityRegistrations.get(entity).get(component))
|
|
||||||
{
|
|
||||||
entityRegistrations.get(entity).clear(component);
|
entityRegistrations.get(entity).clear(component);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
catch (IndexOutOfBoundsException e)
|
catch (IndexOutOfBoundsException e)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BitSet getRegistrations(int entity){
|
/**
|
||||||
return entityRegistrations.get(entity);
|
* Resizes the currentSize of the entity manager.
|
||||||
}
|
* @param newSize the new maximum size
|
||||||
|
* @param systemManager reference to the instanced SystemManager
|
||||||
public void setRegistrations(int entity, BitSet registrations){
|
* @param componentManager reference to the insanced ComponentManager
|
||||||
entityRegistrations.set(entity, registrations);
|
* @return true if the operation succeeded, otherwise false
|
||||||
}
|
*/
|
||||||
|
protected boolean resize(int newSize, SystemManager systemManager, ComponentManager componentManager){
|
||||||
public boolean resize(int newSize, SystemManager systemManager, ComponentManager componentManager){
|
if (newSize < maxSize - unusedEntities.size()){
|
||||||
if (newSize < currentSize - unusedEntities.size()){
|
|
||||||
ECS.writeErr("Attempted to resize the maximum entity count to a number smaller than the current assigned entity count.");
|
ECS.writeErr("Attempted to resize the maximum entity count to a number smaller than the current assigned entity count.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (newSize == currentSize){
|
else if (newSize == maxSize){
|
||||||
ECS.writeErr("Attempted to set the newSize to the current size");
|
ECS.writeErr("Attempted to set the newSize to the current size");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// Consistency should be maintained.
|
// Consistency should be maintained.
|
||||||
// This is computationally expensive; we must re-order every assigned entity above newSize, if the newSize is smaller
|
// This is computationally expensive; we must re-order every assigned entity above newSize, if the newSize is smaller
|
||||||
if (newSize < currentSize){
|
if (newSize < maxSize){
|
||||||
List<Integer> outOfBounds = new ArrayList<>();
|
List<Integer> outOfBounds = new ArrayList<>();
|
||||||
for (int i = newSize; i < currentSize; i++) {
|
for (int i = newSize; i < maxSize; i++) {
|
||||||
if (!unusedEntities.remove(i)){
|
if (!unusedEntities.remove(i)){
|
||||||
// Could not remove element, as it didn't exist (already assigned).
|
// Could not remove element, as it didn't exist (already assigned).
|
||||||
// must find it and reassign it a new in-bounds value
|
// must find it and reassign it a new in-bounds value
|
||||||
@ -132,23 +186,23 @@ class EntityManager{
|
|||||||
systemManager.entityDestroyed(integer);
|
systemManager.entityDestroyed(integer);
|
||||||
systemManager.entityRegistrationsChanged(newPos, getRegistrations(newPos));
|
systemManager.entityRegistrationsChanged(newPos, getRegistrations(newPos));
|
||||||
// Invoke the change in the components
|
// Invoke the change in the components
|
||||||
componentManager.moveComponentData(integer, newPos);
|
componentManager.moveAllComponentData(integer, newPos, getRegistrations(integer));
|
||||||
componentManager.entityDestroyed(integer);
|
componentManager.entityDestroyed(integer);
|
||||||
}
|
}
|
||||||
for (int i = newSize; i < currentSize; i++) {
|
for (int i = newSize; i < maxSize; i++) {
|
||||||
// Remove out-of-bounds data
|
// Remove out-of-bounds data
|
||||||
entityRegistrations.remove(newSize);
|
entityRegistrations.remove(newSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// Init unassigned values
|
// Init unassigned values
|
||||||
for (int i = currentSize; i < newSize; i++) {
|
for (int i = maxSize; i < newSize; i++) {
|
||||||
unusedEntities.add(i);
|
unusedEntities.add(i);
|
||||||
entityRegistrations.add(new BitSet());
|
entityRegistrations.add(new BitSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finally, set the current size
|
// Finally, set the current size
|
||||||
currentSize = newSize;
|
maxSize = newSize;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,39 +15,27 @@ import java.util.Map;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
class SystemManager{
|
class SystemManager{
|
||||||
Map<Type, BitSet> registrationSignatures = new HashMap<>();
|
private Map<Type, BitSet> registrationSignatures = new HashMap<>();
|
||||||
Map<Type, ECSSystem> systems = new HashMap<>();
|
private Map<Type, ECSSystem> systems = new HashMap<>();
|
||||||
|
|
||||||
// Registering the system adds it to the array of systems.
|
/**
|
||||||
// In Austin Morlan's implementation, this also creates an instance of the
|
* Signals the SystemManager that an entity was destroyed.
|
||||||
// system that can be called from the main thread.
|
* Removes the entity from each system's tracked entities
|
||||||
// It returns this object.
|
* @param entity the destroyed entity
|
||||||
// In Java, we need to initialise the object first (before this is called),
|
*/
|
||||||
// then register that object. Enactment of the system is performed on that
|
protected void entityDestroyed(int entity){
|
||||||
// instance.
|
|
||||||
// I.e., create an object that represents the system class; then store that class in the system
|
|
||||||
// table.
|
|
||||||
public boolean registerSystem(Type systemType, ECSSystem system){
|
|
||||||
if (systems.containsKey(systemType)){
|
|
||||||
ECS.writeErr("System already registered");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
systems.put(systemType, system);
|
|
||||||
registrationSignatures.put(systemType, new BitSet());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegistrationSignature(Type systemType, BitSet registrations){
|
|
||||||
registrationSignatures.put(systemType, registrations);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void entityDestroyed(int entity){
|
|
||||||
for (Type key : systems.keySet()) {
|
for (Type key : systems.keySet()) {
|
||||||
systems.get(key).entities.remove(entity);
|
systems.get(key).entities.remove(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void entityRegistrationsChanged(int entity, BitSet entityRegistrations){
|
/**
|
||||||
|
* Signals the SystemManager that an entity had its registrations changed, so
|
||||||
|
* evaluate if the entity is still relevant to each system
|
||||||
|
* @param entity the entity that was modified
|
||||||
|
* @param entityRegistrations the new registrations of the entity
|
||||||
|
*/
|
||||||
|
protected void entityRegistrationsChanged(int entity, BitSet entityRegistrations){
|
||||||
for (Type key : systems.keySet()) {
|
for (Type key : systems.keySet()) {
|
||||||
// Check if the signature is null
|
// Check if the signature is null
|
||||||
if (!entityRegistrations.equals(null)){
|
if (!entityRegistrations.equals(null)){
|
||||||
@ -62,4 +50,31 @@ class SystemManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the specified system name and system reference
|
||||||
|
* @param systemType the class type of the system
|
||||||
|
* @param system the instance of the system
|
||||||
|
* @return true if the system was added successfully. False if it was already registered; with an error message written to the log
|
||||||
|
*/
|
||||||
|
protected boolean registerSystem(Type systemType, ECSSystem system){
|
||||||
|
if (systems.containsKey(systemType)){
|
||||||
|
ECS.writeErr("System already registered");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
systems.put(systemType, system);
|
||||||
|
registrationSignatures.put(systemType, new BitSet());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the registrations the system requires
|
||||||
|
* @param systemType the class type of the system
|
||||||
|
* @param registrations the BitSet containing the required registrations set to true
|
||||||
|
*/
|
||||||
|
protected void setRegistrationSignature(Type systemType, BitSet registrations){
|
||||||
|
registrationSignatures.put(systemType, registrations);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ package nz.ac.massey.javaecs;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
@ -10,10 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
|||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
@ -193,15 +189,15 @@ class AppTest {
|
|||||||
@Test
|
@Test
|
||||||
void testEqualResize(){
|
void testEqualResize(){
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
PrintStream orig = System.err;
|
PrintStream orig = ECS.getErr();
|
||||||
PrintStream newErr = new PrintStream(bytes);
|
PrintStream newErr = new PrintStream(bytes);
|
||||||
System.setErr(newErr);
|
ECS.setErr(newErr);
|
||||||
assertTrue(gameEngine.resizeMaximum(1024));
|
assertTrue(gameEngine.resizeMaximum(1024));
|
||||||
System.setErr(orig);
|
ECS.setErr(orig);
|
||||||
newErr.flush(); // ensure the bytes are recieved
|
newErr.flush(); // ensure the bytes are recieved
|
||||||
byte[] errBytes = bytes.toByteArray();
|
byte[] errBytes = bytes.toByteArray();
|
||||||
String result = new String(errBytes);
|
String result = new String(errBytes);
|
||||||
System.err.println("Captured in redirect: " + result);
|
ECS.writeErr("Captured in redirect: " + result);
|
||||||
assertTrue(result.trim().equals("Attempted to set the newSize to the current size"));
|
assertTrue(result.trim().equals("Attempted to set the newSize to the current size"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user