Cleaned and minor restructure
This commit is contained in:
parent
4f4e1bec7b
commit
529f99abbd
@ -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;
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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,14 +174,28 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified system's signature to the provided signature
|
||||
* @param system the class name of the system to set the signature of
|
||||
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user