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;
}
/**