diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java index 2f573df..876f1f2 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java @@ -24,15 +24,15 @@ class ComponentArray{ // The object data array private List componentArray = new ArrayList<>(); // The mappings between data and entity - private Map entityComponentDataMap = new HashMap<>(); - private Map componentDataEntityMap = new HashMap<>(); + private Map entityComponentDataMap = new HashMap<>(); + private Map 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){ + protected Object getData(Entity entity){ try{ return componentArray.get(entityComponentDataMap.get(entity)); } @@ -52,7 +52,7 @@ class ComponentArray{ * @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){ + protected boolean insertData(Entity entity, Object component){ if (entityComponentDataMap.containsKey(entity)){ ECS.writeErr("Entity is already subscribed to the component"); return false; @@ -71,7 +71,7 @@ class ComponentArray{ * @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){ + protected int moveData(Entity sourceEntity, Entity destinationEntity){ try{ Object data = entityComponentDataMap.get(sourceEntity); if (data == null) return -1; @@ -92,7 +92,7 @@ class ComponentArray{ * @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){ + protected boolean removeData(Entity entity){ if (!entityComponentDataMap.containsKey(entity)){ ECS.writeErr("Attempted to remove non-existent entity"); return false; @@ -102,7 +102,7 @@ class ComponentArray{ // Replace the removed component with the last component in the array componentArray.set(removedComponentDataIndex, componentArray.get(componentArray.size() -1)); // update the data positions in the map - int lastEntity = componentDataEntityMap.get(componentArray.size()-1); + Entity lastEntity = componentDataEntityMap.get(componentArray.size()-1); entityComponentDataMap.replace(lastEntity, removedComponentDataIndex); componentDataEntityMap.replace(removedComponentDataIndex, lastEntity); // Finally, remomve the last elements diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java index 1a0611d..0689694 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java @@ -24,7 +24,7 @@ class ComponentManager{ * @param componentData the component data to associate * @param entity the entity to associate data to */ - protected boolean addComponentToEntity(Type componentType, Object componentData, int entity){ + protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){ return componentArrays.get(componentType).insertData(entity, componentData); } @@ -32,7 +32,7 @@ class ComponentManager{ * 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){ + public void entityDestroyed(Entity entity){ for (Type key : componentArrays.keySet()) { componentArrays.get(key).removeData(entity); } @@ -44,7 +44,7 @@ class ComponentManager{ * @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){ + public Object getComponent(Type componentType, Entity entity){ return componentArrays.get(componentType).getData(entity); } @@ -83,7 +83,7 @@ class ComponentManager{ * @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){ + protected boolean moveComponentData(Entity sourceEntity, Entity destinationEntity, Type component){ if (componentArrays.get(component).moveData(sourceEntity, destinationEntity) == 0){ return true; } @@ -96,7 +96,7 @@ class ComponentManager{ * @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){ + protected void moveAllComponentData(Entity sourceEntity, Entity destinationEntity, BitSet sourceRegistrations){ int result = sourceRegistrations.nextSetBit(0); while (result != -1){ Type key = indexComponentType.get(result); @@ -126,7 +126,7 @@ class ComponentManager{ * @param componentType the class type of the component to remove * @param entity the entity to remove the component from */ - public boolean removeComponentFromEntity(Type componentType, int entity){ + public boolean removeComponentFromEntity(Type componentType, Entity entity){ return componentArrays.get(componentType).removeData(entity); } } \ No newline at end of file diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java index 6899ce9..dc2a6a1 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ECS.java @@ -56,9 +56,9 @@ public class ECS { * @return the index of the new entity * @throws IndexOutOfBoundsException */ - Integer createEntity() throws IndexOutOfBoundsException{ - int newEntity = entityManager.addEntity(); - if (newEntity == -1) throw new IndexOutOfBoundsException("Could not create a new entity"); + Entity createEntity() throws IndexOutOfBoundsException{ + Entity newEntity = entityManager.addEntity(); + if (newEntity == null) throw new IndexOutOfBoundsException("Could not create a new entity"); return newEntity; } @@ -75,7 +75,7 @@ public class ECS { * Signals each manager to remove the specified entity * @param entity the entity to destroy */ - void destroyEntity(int entity){ + void destroyEntity(Entity entity){ entityManager.removeEntity(entity); componentManager.entityDestroyed(entity); systemManager.entityDestroyed(entity); @@ -99,7 +99,7 @@ public class ECS { * @param componentName the class name of the component to add * @param component the actual component data */ - boolean addComponent(int entity, Type componentName, Object component){ + boolean addComponent(Entity entity, Type componentName, Object component){ if (entityManager.registerComponent(componentManager.getComponentIndex(componentName), entity)) { systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); @@ -114,7 +114,7 @@ public class ECS { * @param entity the entity to remove the component from * @param componentName the class name of the component */ - boolean removeComponent(int entity, Type componentType){ + boolean removeComponent(Entity entity, Type componentType){ if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity)) { componentManager.removeComponentFromEntity(componentType, entity); @@ -131,7 +131,7 @@ public class ECS { * @param componentType the class type of the component * @return the component data Object associated with the entity */ - Object getComponentData(int entity, Type componentType){ + Object getComponentData(Entity entity, Type componentType){ return componentManager.getComponent(componentType, entity); } diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java b/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java index b419bd4..9ea1803 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java @@ -16,7 +16,7 @@ import java.util.Set; import java.util.HashSet; abstract class ECSSystem{ - Set entities = new HashSet<>(); + Set entities = new HashSet<>(); /** * Implement additional parameterised init() functions as required. diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java b/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java new file mode 100644 index 0000000..3d6da74 --- /dev/null +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java @@ -0,0 +1,38 @@ +package nz.ac.massey.javaecs; +/** + * Entity class. + * Whilst an entity is just an Integer, and using Integer values + * would be more performant (no function call), it is encapsulated to help + * distinguish between entities and Integers. + */ +public class Entity { + private int value; + + public Entity(int value){ + this.value = value; + } + + public int getValue() { + return value; + } + + /** + * Returns the int value wrapped as an entity. + * Used to provide a distinction between creating a new entity, and + * using an int value we assume is a valid entity. + * + * Functionally, this is no different to creating a new entity. + * + * @param value + * @return + */ + public static Entity asEntity(int value){ + return new Entity(value); + } + + @Override + public boolean equals(Object obj) + { + return (this.getValue() == ((Entity)obj).getValue()); + } +} diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java b/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java index fd87fb5..1140d4c 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java @@ -21,7 +21,7 @@ import java.util.ArrayList; * I.e. Controls adding and removing entities, and registration and deregistration of components. */ class EntityManager{ - private Queue unusedEntities; + private Queue unusedEntities; private List entityRegistrations; private int maxSize = 1024; @@ -33,7 +33,7 @@ class EntityManager{ entityRegistrations = new ArrayList<>(); for (int i = 0; i < maxSize; i++) { - unusedEntities.add(i); + unusedEntities.add(new Entity(i)); entityRegistrations.add(null); // Leave bitsets out as if they are null the entity isn't initialised properly } } @@ -48,7 +48,7 @@ class EntityManager{ entityRegistrations = new ArrayList<>(); for (int i = 0; i < maxEntities; i++) { - unusedEntities.add(i); + unusedEntities.add(new Entity(i)); entityRegistrations.add(null); } } @@ -57,13 +57,13 @@ class EntityManager{ * Creates a new entity * @return the index of the new entity, or -1 if there is no more available entities */ - protected Integer addEntity(){ + protected Entity addEntity(){ if (unusedEntities.size() == 0){ ECS.writeErr("No available space to create a new entity"); - return -1; + return null; } - int result = unusedEntities.remove(); - entityRegistrations.set(result, new BitSet()); + Entity result = unusedEntities.remove(); + entityRegistrations.set(result.getValue(), new BitSet()); return result; } @@ -80,12 +80,12 @@ class EntityManager{ * @param entity the entity whose BitSet to retrieve * @return the BitSet of the provided entity */ - protected BitSet getRegistrations(int entity){ + protected BitSet getRegistrations(Entity entity){ try{ - return entityRegistrations.get(entity); + return entityRegistrations.get(entity.getValue()); } catch (IndexOutOfBoundsException e){ - ECS.writeErr("Index out of bounds error getting registrations for " + entity + ";\nThe entity might not exist"); + ECS.writeErr("Index out of bounds error getting registrations for " + entity.getValue() + ";\nThe entity might not exist"); return new BitSet(); // Using a blank BitSet will retain data safety (that is, no data will be modified) } } @@ -96,18 +96,18 @@ class EntityManager{ * @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); + protected boolean registerComponent(int component, Entity entity){ + if (entity.getValue() >= maxSize){ + ECS.writeErr("Attempted to assign a component to non-existent entity: " + entity.getValue()); return false; } - else if (entityRegistrations.get(entity).get(component)) + else if (entityRegistrations.get(entity.getValue()).get(component)) { ECS.writeErr("Entity is already assigned to the component"); return false; } else{ - entityRegistrations.get(entity).set(component); + entityRegistrations.get(entity.getValue()).set(component); return true; } } @@ -118,9 +118,9 @@ class EntityManager{ * Does not handle associated data Use the method in ECS to remove entities cleanly * @param entity the entity to remove */ - protected void removeEntity(int entity){ + protected void removeEntity(Entity entity){ unusedEntities.add(entity); - entityRegistrations.set(entity, null); + entityRegistrations.set(entity.getValue(), null); } /** @@ -128,8 +128,8 @@ class EntityManager{ * @param entity the entity to set * @param registrations the preset registrations */ - protected void setRegistrations(int entity, BitSet registrations){ - entityRegistrations.set(entity, registrations); + protected void setRegistrations(Entity entity, BitSet registrations){ + entityRegistrations.set(entity.getValue(), registrations); } /** @@ -140,9 +140,9 @@ class EntityManager{ * @param entity the entity to remove * @return true if successful */ - protected boolean unregisterComponent(int component, int entity){ + protected boolean unregisterComponent(int component, Entity entity){ try{ - entityRegistrations.get(entity).clear(component); + entityRegistrations.get(entity.getValue()).clear(component); return true; } catch (NullPointerException e){ @@ -175,16 +175,17 @@ class EntityManager{ // Consistency should be maintained. // This is computationally expensive; we must re-order every assigned entity above newSize, if the newSize is smaller if (newSize < maxSize){ - List outOfBounds = new ArrayList<>(); + List outOfBounds = new ArrayList<>(); for (int i = newSize; i < maxSize; i++) { - if (!unusedEntities.remove(i)){ + Entity entityI = Entity.asEntity(i); + if (!unusedEntities.remove(entityI)){ // Could not remove element, as it didn't exist (already assigned). // must find it and reassign it a new in-bounds value - outOfBounds.add(i); + outOfBounds.add(entityI); } } - for (Integer integer : outOfBounds) { - int newPos = addEntity(); + for (Entity integer : outOfBounds) { + Entity newPos = addEntity(); setRegistrations(newPos, getRegistrations(integer)); // Invoke an update in the SystemManager systemManager.entityDestroyed(integer); @@ -200,7 +201,7 @@ class EntityManager{ else{ // Init unassigned values for (int i = maxSize; i < newSize; i++) { - unusedEntities.add(i); + unusedEntities.add(new Entity(i)); entityRegistrations.add(new BitSet()); } } diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java b/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java index ba80e9f..9edd3ce 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java @@ -23,7 +23,7 @@ class SystemManager{ * Removes the entity from each system's tracked entities * @param entity the destroyed entity */ - protected void entityDestroyed(int entity){ + protected void entityDestroyed(Entity entity){ for (Type key : systems.keySet()) { systems.get(key).entities.remove(entity); } @@ -35,7 +35,7 @@ class SystemManager{ * @param entity the entity that was modified * @param entityRegistrations the new registrations of the entity */ - protected void entityRegistrationsChanged(int entity, BitSet entityRegistrations){ + protected void entityRegistrationsChanged(Entity entity, BitSet entityRegistrations){ for (Type key : systems.keySet()) { // Check if the signature is null if (!entityRegistrations.equals(null)){ diff --git a/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java b/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java index c7de0e4..2028840 100644 --- a/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java +++ b/javaecs/src/test/java/nz/ac/massey/javaecs/AppTest.java @@ -33,7 +33,7 @@ class AppTest { class TestSystem extends ECSSystem{ public void init(){} public void update() { - for (Integer entity : entities) { + for (Entity entity : entities) { TestObject entityComponent = (TestObject)gameEngine.getComponentData(entity, TestObject.class); entityComponent.i++; } @@ -44,7 +44,7 @@ class AppTest { */ @Test void testAssignOne() { - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertEquals(1, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); } @@ -55,7 +55,7 @@ class AppTest { @Test void testAssignMax(){ for (int i = 0; i < gameEngine.getMaxEntities(); i++) { - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertEquals(1, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); } @@ -72,7 +72,7 @@ class AppTest { }); } else{ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); } } @@ -83,7 +83,7 @@ class AppTest { */ @Test void testRemoveOne(){ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); gameEngine.destroyEntity(entity); assertNull(((TestObject)gameEngine.getComponentData(entity, TestObject.class))); @@ -94,7 +94,7 @@ class AppTest { */ @Test void testRunSystem(){ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); for (int i = 0; i < 5; i++) { @@ -110,7 +110,7 @@ class AppTest { */ @Test void testResizeMinimal(){ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); gameEngine.resizeMaximum(10); @@ -124,12 +124,12 @@ class AppTest { @Test void testLargeCreateDelete(){ for (int i = 0; i < gameEngine.getMaxEntities(); i++) { - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertEquals(1, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); } for (int i = 256; i < 512; i++) { - gameEngine.destroyEntity(i); + gameEngine.destroyEntity(Entity.asEntity(i)); } } @@ -139,12 +139,12 @@ class AppTest { @Test void testLargeCreateDeleteResize(){ for (int i = 0; i < gameEngine.getMaxEntities(); i++) { - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertEquals(1, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); } for (int i = 256; i < 512; i++) { - gameEngine.destroyEntity(i); + gameEngine.destroyEntity(Entity.asEntity(i)); } gameEngine.resizeMaximum(1024 - 255); @@ -156,12 +156,12 @@ class AppTest { @Test void testLargeCreateDeleteResizeTooSmall(){ for (int i = 0; i < gameEngine.getMaxEntities(); i++) { - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertEquals(1, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); } for (int i = 256; i < 512; i++) { - gameEngine.destroyEntity(i); + gameEngine.destroyEntity(Entity.asEntity(i)); } assertFalse(gameEngine.resizeMaximum(512)); @@ -175,12 +175,12 @@ class AppTest { @Test void testAssignToNonExistentEntity(){ - assertFalse(gameEngine.addComponent(1025, TestObject.class, new TestObject())); + assertFalse(gameEngine.addComponent(Entity.asEntity(1025), TestObject.class, new TestObject())); } @Test void testRemoveComponent(){ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); gameEngine.removeComponent(entity, TestObject.class); assertFalse(gameEngine.entityManager.getRegistrations(entity).get(gameEngine.getComponentIndex(TestObject.class))); @@ -214,12 +214,12 @@ class AppTest { @Test void testRemoveNonExistentComponentData(){ - assertFalse(gameEngine.removeComponent(1, TestObject.class)); + assertFalse(gameEngine.removeComponent(Entity.asEntity(1), TestObject.class)); } @Test void testAssignComponentAlreadyAssigned(){ - int entity = gameEngine.createEntity(); + Entity entity = gameEngine.createEntity(); gameEngine.addComponent(entity, TestObject.class, new TestObject()); assertFalse(gameEngine.addComponent(entity, TestObject.class, new TestObject())); }