Cleaned and minor restructure

This commit is contained in:
Brychan Dempsey 2021-06-11 14:41:22 +12:00
parent 4f4e1bec7b
commit 529f99abbd
6 changed files with 118 additions and 66 deletions

View File

@ -16,6 +16,7 @@ package nz.ac.massey.javaecs;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;

View File

@ -27,11 +27,9 @@ class ComponentManager{
* @param entity the entity to associate data to
*/
protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
if (componentData == null){
// In the case of null component data, insert the boolean false
// (preserves structure by associating data with a null component)
/*if (componentData == null){
return componentArrays.get(componentType).insertData(entity, false);
}
} */
return componentArrays.get(componentType).insertData(entity, componentData);
}
@ -40,7 +38,7 @@ class ComponentManager{
* @param entity the entity that was destroyed.
*/
public void entityDestroyed(Entity entity, BitSet entityRegistrations){
// HashMap lookups take time, use the known bitstates
// HashMap lookups take time, use the known bitstates to avoid
int index = entityRegistrations.nextSetBit(0);
while(index != -1){
if (!componentArrays.get(indexComponentType.get(index)).removeData(entity)){
@ -48,11 +46,6 @@ class ComponentManager{
}
index = entityRegistrations.nextSetBit(index+1);
}
/*for (Type key : componentArrays.keySet()) {
if (!componentArrays.get(key).removeData(entity)){
Engine.writeErr(key.getTypeName());
}
}*/
}
/**
@ -70,7 +63,7 @@ class ComponentManager{
* @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){
public Integer getComponentIndex(Type type){
try{
return componentPosIndex.get(type);
}
@ -84,7 +77,7 @@ class ComponentManager{
* @param index the index of the component
* @return the class type of the index. `null` if not found
*/
protected Type getComponentType(Integer index){
public Type getComponentType(Integer index){
try{
return indexComponentType.get(index);
}
@ -139,7 +132,9 @@ class ComponentManager{
}
/**
* Removes the specified component from the entity
* Removes the specified component from the entity.
* <p>
* Be sure to call SystemManager.entityRegistrationsChanged after calling this function
* @param componentType the class type of the component to remove
* @param entity the entity to remove the component from
*/

View File

@ -8,13 +8,15 @@ package nz.ac.massey.javaecs;
*
* References:
* Based on the implementation by Austin Morlan:
* https://code.austinmorlan.com/austin/ecs - 'A simple C++ Entity Component System' - released under MIT licence
* https://austinmorlan.com/posts/entity_component_system/ - 'A simple C++ Entity Component System'
*
*/
import java.io.PrintStream;
import java.lang.reflect.Type;
import java.util.BitSet;
import java.util.NoSuchElementException;
/**
* The ECS manager.
* <p>
@ -29,11 +31,10 @@ public class Engine {
protected EntityManager entityManager;
protected ComponentManager componentManager;
protected SystemManager systemManager;
/**
* Initialises the ECS with default values
* <p>
* Maximum 1024 enitites default
*/
/***************************************
** Engine Constructors **
***************************************/
public Engine(){
entityManager = new EntityManager();
componentManager = new ComponentManager();
@ -50,18 +51,6 @@ public class Engine {
systemManager = new SystemManager();
}
/**
* /**
* Creates a new entity
* @return the index of the new entity
* @throws IndexOutOfBoundsException if there are no more entities available
*/
public Entity createEntity() throws IndexOutOfBoundsException{
Entity newEntity = entityManager.addEntity();
if (newEntity == null) throw new IndexOutOfBoundsException("Could not create a new entity");
return newEntity;
}
/**
* Attempts to resize the maximum number of entities
* @param newSize the new maximum number of entities
@ -71,16 +60,60 @@ public class Engine {
return entityManager.resize(newSize, systemManager, componentManager);
}
/***************************************
** Manage Entities **
***************************************/
/**
* Create a new Entity (dequeues the first element from unusedEntities)
* @return the next unused Entity, now set to used
* @throws NoSuchElementException if there are no more entities available
*/
public Entity createEntity() throws NoSuchElementException{
try{
Entity newEntity = entityManager.addEntity();
return newEntity;
}
catch (NoSuchElementException e){
// Catch the exception to log the error message
writeErr("No more available entities");
// Throw again to pass up to the original calling function
throw e;
}
}
/**
* Signals each manager to remove the specified entity
* @param entity the entity to destroy
*/
public void destroyEntity(Entity entity){
// Pass the registration list to the componentManager first, to avoid having to iterate through each component type
componentManager.entityDestroyed(entity, entityManager.getRegistrations(entity));
entityManager.removeEntity(entity);
systemManager.entityDestroyed(entity);
}
/**
* Gets the set maximum number of entities
* @return an integer of the maximum number of entities
*/
public Integer getMaxEntities(){
return entityManager.getMaxSize();
}
/**
* Gets the current number of entities
* @return the number of currently active entities
*/
public int getNumEntities(){
return entityManager.getNumEntities();
}
/***************************************
** Manage Components **
***************************************/
/**
* Registers the specified name in the component manager
* @param type the name to register. Should be the component class name or a suitable name for primitive types
@ -90,10 +123,6 @@ public class Engine {
return componentManager.registerComponent(type);
}
public Integer getComponentIndex(Type type){
return componentManager.getComponentIndex(type);
}
/**
* Adds an exisiting component to an exisiting entity
* @param entity the entity to add the component to
@ -102,8 +131,11 @@ public class Engine {
* @return true if the compnent was added to the entity
*/
public boolean addComponent(Entity entity, Type componentType, Object component){
if (entityManager.registerComponent(componentManager.getComponentIndex(componentType), entity))
// Get the old registrations of the entity so that we can update only the relevant systems
int componentIndex = componentManager.getComponentIndex(componentType);
if (entityManager.registerComponent(componentIndex, entity))
{
//systemManager.entityRegistrationAdded(entity, componentIndex);
systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity));
componentManager.addComponentToEntity(componentType, component, entity);
return true;
@ -124,7 +156,7 @@ public class Engine {
if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity))
{
componentManager.removeComponentFromEntity(componentType, entity);
systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity));
systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity)); // entityRegistrationRemoved(entity, componentManager.getComponentIndex(componentType));
return true;
}
else return false;
@ -132,7 +164,7 @@ public class Engine {
/**
* Gets the actual data of the component associated to the entity.
* May require casting from Object to the known data type
* Requires casting from Object to the known data type
* @param entity the entity to retrieve the data for
* @param componentType the class type of the component
* @return the component data Object associated with the entity
@ -142,12 +174,26 @@ public class Engine {
}
/**
* Registers the system to
* @param systemType
* @param action
* Gets the component index of the provided type
* @param type the type to get the index of
* @return the index of the component
*/
public boolean registerSystem(Type systemType, ECSSystem action){
return systemManager.registerSystem(systemType, action);
public Integer getComponentIndex(Type type){
return componentManager.getComponentIndex(type);
}
/****************************************
** Manage Systems **
****************************************/
/**
* Registers the system to the SystemManager
* @param systemType the type of the system
* @param instance the instance of the system
*/
public boolean registerSystem(Type systemType, ECSSystem instance){
return systemManager.registerSystem(systemType, instance);
}
/**
@ -158,16 +204,16 @@ public class Engine {
public void setSystemSignature(Type system, BitSet signature){
systemManager.setRegistrationSignature(system, signature);
}
public Integer getMaxEntities(){
return entityManager.getMaxSize();
}
// Encapsulate syserr writes so they may be redirected out of the lib
/****************************************
** Helper Functions & Accessors **
****************************************/
/**
* Writes an error message to the set PrintStream
* <p>
* <i>Default System.err
* <i>Default is System.err
* @param message the message to write
*/
static void writeErr(String message){
@ -200,19 +246,27 @@ public class Engine {
return errorStream;
}
/**
* Gets a the current component manager
* @return the current active component manager
*/
public ComponentManager getComponentManager() {
return componentManager;
}
/**
* Gets a the current enitity manager
* @return the current active enitity manager
*/
public EntityManager getEntityManager() {
return entityManager;
}
/**
* Gets a the current systems manager
* @return the current active systems manager
*/
public SystemManager getSystemManager() {
return systemManager;
}
public int getNumEntities(){
return entityManager.getNumEntities();
}
}

View File

@ -11,6 +11,7 @@ package nz.ac.massey.javaecs;
import java.util.BitSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.ArrayList;
@ -55,13 +56,10 @@ class EntityManager{
/**
* Creates a new entity
* @return the index of the new entity, or -1 if there is no more available entities
* @return the index of the new entity
* @throws NoSuchElementException an exception if there are no more unused entities
*/
protected Entity addEntity(){
if (unusedEntities.size() == 0){
Engine.writeErr("No available space to create a new entity");
return null;
}
protected Entity addEntity() throws NoSuchElementException{
Entity result = unusedEntities.remove();
entityRegistrations.set(result.getValue(), new BitSet());
return result;

View File

@ -24,7 +24,7 @@ class SystemManager{
* @param entity the destroyed entity
*/
protected void entityDestroyed(Entity entity){
// Unlike components, this isn't simply indexed; makes more sense just to search the systems
// Unlike components, this isn't simply indexed.
for (Type key : systems.keySet()) {
systems.get(key).entities.remove(entity);
}
@ -32,21 +32,24 @@ class SystemManager{
/**
* Signals the SystemManager that an entity had its registrations changed, so
* evaluate if the entity is still relevant to each system
* evaluate if the entity is still relevant to each system. This is not lightweight,
* use entityRegistrationAdded and entityRegistrationRemoved if adding/removing a
* single component
* @param entity the entity that was modified
* @param entityRegistrations the new registrations of the entity
*/
protected void entityRegistrationsChanged(Entity entity, BitSet entityRegistrations){
public void entityRegistrationsChanged(Entity entity, BitSet entityRegistrations){
for (Type key : systems.keySet()) {
// Check if the signature is null
if (!entityRegistrations.equals(null)){
BitSet srcCpy = (BitSet)entityRegistrations.clone();
srcCpy.and(systems.get(key).registrationSet);
if (srcCpy.equals(systems.get(key).registrationSet)){ // Bitwise check if the entity is subscribed to this system
systems.get(key).entities.add(entity);
ECSSystem sys = systems.get(key);
srcCpy.and(sys.registrationSet);
if (srcCpy.equals(sys.registrationSet)){ // Bitwise check if the entity is subscribed to this system
sys.entities.add(entity);
}
else{
systems.get(key).entities.remove(entity);
sys.entities.remove(entity);
}
}
}

View File

@ -11,6 +11,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.BitSet;
import java.util.NoSuchElementException;
import org.junit.jupiter.api.BeforeEach;
@ -67,7 +68,7 @@ class AppTest {
void testAssignMoreThanMax(){
for (int i = 0; i < gameEngine.getMaxEntities() + 5; i++) {
if (i >= gameEngine.getMaxEntities()){
assertThrows(IndexOutOfBoundsException.class, () -> {
assertThrows(NoSuchElementException.class, () -> {
gameEngine.createEntity();
});
}