From 051836852c1fbd54477a91fd90b99eef8a5196b5 Mon Sep 17 00:00:00 2001 From: Brychan Dempsey Date: Sat, 12 Jun 2021 00:51:35 +1200 Subject: [PATCH] Fixed javadocs, cleaned many lines Prepped for release at v0.9.9 Breaking changes for all implementations --- javaecs/pom.xml | 2 +- .../nz/ac/massey/javaecs/ComponentArray.java | 4 +- .../ac/massey/javaecs/ComponentManager.java | 14 ++-- .../java/nz/ac/massey/javaecs/ECSSystem.java | 12 ++-- .../java/nz/ac/massey/javaecs/Engine.java | 15 ++-- .../java/nz/ac/massey/javaecs/Entity.java | 70 +++++++++++++++++-- .../nz/ac/massey/javaecs/EntityManager.java | 24 ++++--- .../nz/ac/massey/javaecs/SystemManager.java | 13 ++-- .../java/nz/ac/massey/javaecs/AppTest.java | 5 +- 9 files changed, 117 insertions(+), 42 deletions(-) diff --git a/javaecs/pom.xml b/javaecs/pom.xml index 01a9425..8b8f346 100644 --- a/javaecs/pom.xml +++ b/javaecs/pom.xml @@ -2,7 +2,7 @@ 4.0.0 nz.ac.massey.javaecs javaecs - 0.9.2-PRERELEASE + 0.9.9-RELEASE_CANDIDATE 1.8 1.8 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 15f0e5d..710b80c 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentArray.java @@ -19,7 +19,9 @@ import java.util.HashMap; import java.util.List; import java.util.ArrayList; - +/** + * Stores component data in a packed interal list, and provides methods for accessing this data + */ class ComponentArray{ // The object data array private List componentArray; 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 9aa4db4..fb56e88 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ComponentManager.java @@ -13,6 +13,9 @@ import java.lang.reflect.Type; import java.util.BitSet; import java.util.HashMap; +/** + * Manages the addition, sorting and retrieving of components and component data + */ class ComponentManager{ private Map componentArrays = new HashMap<>(); private Map componentPosIndex = new HashMap<>(); @@ -27,9 +30,6 @@ class ComponentManager{ * @param entity the entity to associate data to */ protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){ - /*if (componentData == null){ - return componentArrays.get(componentType).insertData(entity, false); - } */ return componentArrays.get(componentType).insertData(entity, componentData); } @@ -93,7 +93,7 @@ class ComponentManager{ * @param component the component class type to consider * @return true if the component was moved successfully, else false */ - protected boolean moveComponentData(Entity sourceEntity, Entity destinationEntity, Type component){ + public boolean moveComponentData(Entity sourceEntity, Entity destinationEntity, Type component){ if (componentArrays.get(component).moveData(sourceEntity, destinationEntity) == 0){ return true; } @@ -106,7 +106,7 @@ class ComponentManager{ * @param destinationEntity the entity to move data to * @param sourceRegistrations the component registrations of the source entity */ - protected void moveAllComponentData(Entity sourceEntity, Entity destinationEntity, BitSet sourceRegistrations){ + public void moveAllComponentData(Entity sourceEntity, Entity destinationEntity, BitSet sourceRegistrations){ int result = sourceRegistrations.nextSetBit(0); while (result != -1){ Type key = indexComponentType.get(result); @@ -120,7 +120,7 @@ class ComponentManager{ * @param type the class type to register * @return true if the component was registered successfully, else false */ - protected boolean registerComponent(Type type){ + public boolean registerComponent(Type type){ return registerComponent(type, 16); } @@ -130,7 +130,7 @@ class ComponentManager{ * @param arraySize the number of elements to prereserve space for. * @return true if the component was registered successfully, else false */ - protected boolean registerComponent(Type type, int arraySize){ + public boolean registerComponent(Type type, int arraySize){ if (componentArrays.containsKey(type)){ Engine.writeErr("Component " + type.getTypeName() + " is already registered"); return false; 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 47e76ae..773ef00 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/ECSSystem.java @@ -20,17 +20,21 @@ public abstract class ECSSystem{ protected Set entities = new HashSet<>(); protected BitSet registrationSet; - public BitSet getRegistrationSet; + public BitSet getRegistrationSet(){ + return registrationSet; + } /** + * Functionality that should be run only once. * Implement additional parameterised init() functions as required. - * These should run once, when the system is first initialised. */ public abstract void init(); /** + * Functionality that is expected to be called regularly + * Intended as a template only; may be superficially implemented. * Implement additional parameterised update() functions as required. - * These should be run each game loop or otherwise sensible regular interval + * @param dt delta-time; the change in time in milliseconds since this function was last run */ - public abstract void update(); + public abstract void update(double dt); } \ No newline at end of file diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/Engine.java b/javaecs/src/main/java/nz/ac/massey/javaecs/Engine.java index 0a7e5f4..c8e2f5a 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/Engine.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/Engine.java @@ -1,7 +1,8 @@ package nz.ac.massey.javaecs; /** - * ECS manager class. + * ECS engine class. * Call this class and its functions to interact correctly with the ECS system. + * This is the entry-point to the library * * Contributors: * Brychan Dempsey - brychand@hotmail.com @@ -18,7 +19,8 @@ import java.util.BitSet; import java.util.NoSuchElementException; /** - * The ECS manager. + * The ECS management engine. Supplemented by the EntityManager, ComponentManager, and System Manager + * These four classes provide the actual functionality of the ecs *

* See https://git.software.kauripeak.co.nz/BrychanD/JavaECS * for documentation and more information. @@ -39,6 +41,9 @@ public class Engine { entityManager = new EntityManager(); componentManager = new ComponentManager(); systemManager = new SystemManager(); + // In a non-ECS type manner (instead OO-like), provide an additional method to + // add components to an entity: entity.addComponent(). + Entity.engineRef = this; } /** @@ -49,6 +54,7 @@ public class Engine { entityManager = new EntityManager(maxEntities); componentManager = new ComponentManager(); systemManager = new SystemManager(); + Entity.engineRef = this; } /** @@ -156,7 +162,7 @@ public class Engine { if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity)) { componentManager.removeComponentFromEntity(componentType, entity); - systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); // entityRegistrationRemoved(entity, componentManager.getComponentIndex(componentType)); + systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); return true; } else return false; @@ -191,6 +197,7 @@ public class Engine { * Registers the system to the SystemManager * @param systemType the type of the system * @param instance the instance of the system + * @return true if successful */ public boolean registerSystem(Type systemType, ECSSystem instance){ return systemManager.registerSystem(systemType, instance); @@ -202,7 +209,7 @@ public class Engine { * @param signature the new signature data */ public void setSystemSignature(Type system, BitSet signature){ - systemManager.setRegistrationSignature(system, signature); + systemManager.setSystemRegistraions(system, signature); } diff --git a/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java b/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java index 1073a51..d77f48c 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/Entity.java @@ -1,11 +1,17 @@ package nz.ac.massey.javaecs; + +import java.lang.reflect.Type; + /** * 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. + * would be more performant (no function call, primitive arrays & etc.), it is encapsulated to help + * distinguish between entities and Integers. Also, in this form it may be extended to also include things + * like names, grouping, specific ordering etc */ public class Entity { + protected static Engine engineRef; + private int value; public Entity(int value){ @@ -20,11 +26,11 @@ public class Entity { * 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 + * @param value the integer value to read as an Entity + * @return an Entity object representing the ID value */ public static Entity asEntity(int value){ return new Entity(value); @@ -44,6 +50,58 @@ public class Entity { @Override public int hashCode(){ - return value; + return value; // each entity is an id; the value is implicitly unique, so use it as the hash code + } + + /** + * Adds the provided component to this entity. + *

+ * This function calls addComponent() in the engine, so + * that should be used instead. + * @deprecated This function is not ECS-like. + * It is provided as an auxilliary method, that will be more + * intuitive to those familiar with OO design + * @see Engine.addComponent() - which this function makes a call to + * @param componentType the class type of the component to add + * @param componentData the component data + * @return true if successful + */ + @Deprecated + public boolean addComponent(Type componentType, Object componentData){ + return engineRef.addComponent(this, componentType, componentData); + } + + /** + * Removes the provided component from this entity. + *

+ * This function calls removeComponent() in the engine, so + * that should be used instead. + * @deprecated This function is not ECS-like. + * It is provided as an auxilliary method, that will be more + * intuitive to those familiar with OO design + * @see Engine.removeComponent() - which this function makes a call to + * @param componentType the class type of the component to remove + * @return true if successful + */ + @Deprecated + public boolean removeComponent(Type componentType){ + return engineRef.removeComponent(this, componentType); + } + + /** + * Gets the component data associated to this entity. + *

+ * This function calls getComponentData() in the engine, so + * that should be used instead. + * @deprecated This function is not ECS-like. + * It is provided as an auxilliary method, that will be more + * intuitive to those familiar with OO design + * @see Engine.getComponentData() - which this function makes a call to + * @param componentType the class type of the component to fetch data from + * @return the data Object, which requires casting + */ + @Deprecated + public Object getComponent(Type componentType){ + return engineRef.getComponentData(this, componentType); } } 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 2853823..f4040f6 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/EntityManager.java @@ -15,7 +15,6 @@ import java.util.NoSuchElementException; import java.util.ArrayDeque; import java.util.ArrayList; -// Define the manager classes internally - should be moved to seperate source files as appropriate /** * Manages data from the perspective of the entity. *

@@ -23,7 +22,7 @@ import java.util.ArrayList; */ class EntityManager{ // According to https://stackoverflow.com/questions/12524826/why-should-i-use-deque-over-stack - // ArrayDeque is likely faster than a LinkedList, when used as one. + // ArrayDeque is likely faster than a LinkedList, when used in place of one. // We can also supply a size to the constructor of ArrayDeque, which avoids resizing the collection // at initialisation time (took 1.4s vs 1.8s for 1M) private Deque unusedEntities; @@ -39,7 +38,8 @@ class EntityManager{ for (int i = 0; i < maxSize; i++) { unusedEntities.add(new Entity(i)); - entityRegistrations.add(null); // Leave bitsets out as if they are null the entity isn't initialised properly + entityRegistrations.add(null); // Annul the entries, to set list size, but keep the unused + // entities in an invalid state } } @@ -73,7 +73,7 @@ class EntityManager{ * Gets the current maximum size * @return the value of currentSize */ - protected Integer getMaxSize(){ + public Integer getMaxSize(){ return maxSize; } @@ -82,7 +82,7 @@ class EntityManager{ * @param entity the entity whose BitSet to retrieve * @return the BitSet of the provided entity, or a new, empty BitSet if the result was null or out of bounds */ - protected BitSet getRegistrations(Entity entity){ + public BitSet getRegistrations(Entity entity){ try{ BitSet registrations = entityRegistrations.get(entity.getValue()); if (registrations != null){ @@ -171,7 +171,7 @@ class EntityManager{ * @param componentManager reference to the insanced ComponentManager * @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()){ Engine.writeErr("Attempted to resize the maximum entity count to a number smaller than the current assigned entity count."); return false; @@ -181,9 +181,12 @@ class EntityManager{ return true; } else{ - // Consistency should be maintained. - // This is computationally expensive; we must re-order every assigned entity above newSize, if the newSize is smaller + // Every element above the index must be reordered if (newSize < maxSize){ + // Loop through every entity value above our desired index + // and attempt to remove it from the unused entity queue + // If that fails, the entity is in use, and must have its + // data copied to a lower index Deque outOfBounds = new ArrayDeque<>(maxSize - newSize); for (int i = newSize; i < maxSize; i++) { Entity entityI = Entity.asEntity(i); @@ -193,6 +196,7 @@ class EntityManager{ outOfBounds.add(entityI); } } + // Process all out-of-bounds entities, and destroy them as we go while(outOfBounds.size() > 0){ Entity old = outOfBounds.remove(); Entity newPos = addEntity(); @@ -204,7 +208,7 @@ class EntityManager{ componentManager.moveAllComponentData(old, newPos, getRegistrations(old)); } for (int i = newSize; i < maxSize; i++) { - // Remove out-of-bounds data + // Remove out-of-bounds registration data entityRegistrations.remove(newSize); } } @@ -221,7 +225,7 @@ class EntityManager{ } } - protected int getNumEntities(){ + public int getNumEntities(){ return maxSize - unusedEntities.size(); } } \ No newline at end of file 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 9b794e4..5f7b897 100644 --- a/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java +++ b/javaecs/src/main/java/nz/ac/massey/javaecs/SystemManager.java @@ -14,8 +14,11 @@ import java.util.BitSet; import java.util.Map; import java.util.HashMap; +/** + * Manages system-focused aspects, such as ensuring a system has the correct list of current entities. + * Manages registration of new systems + */ class SystemManager{ - //private Map registrationSignatures = new HashMap<>(); private Map systems = new HashMap<>(); /** @@ -32,9 +35,7 @@ class SystemManager{ /** * Signals the SystemManager that an entity had its registrations changed, so - * evaluate if the entity is still relevant to each system. This is not lightweight, - * use entityRegistrationAdded and entityRegistrationRemoved if adding/removing a - * single component + * 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 */ @@ -61,7 +62,7 @@ class SystemManager{ * @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){ + public boolean registerSystem(Type systemType, ECSSystem system){ if (systems.containsKey(systemType)){ Engine.writeErr("System \'" + systemType.getTypeName() + "\' already registered"); return false; @@ -75,7 +76,7 @@ class SystemManager{ * @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){ + public void setSystemRegistraions(Type systemType, BitSet registrations){ systems.get(systemType).registrationSet = registrations; } 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 2df4f15..95bbffd 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() { + public void update(double dt) { for (Entity entity : entities) { TestObject entityComponent = (TestObject)gameEngine.getComponentData(entity, TestObject.class); entityComponent.i++; @@ -99,7 +99,7 @@ class AppTest { gameEngine.addComponent(entity, TestObject.class, new TestObject()); for (int i = 0; i < 5; i++) { - system.update(); + system.update(0.1); } assertEquals(6, ((TestObject)gameEngine.getComponentData(entity, TestObject.class)).i); @@ -132,7 +132,6 @@ class AppTest { for (int i = 256; i < 512; i++) { gameEngine.destroyEntity(Entity.asEntity(i)); } - int k = 0; } /**