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.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@ -27,11 +27,9 @@ class ComponentManager{
|
|||||||
* @param entity the entity to associate data to
|
* @param entity the entity to associate data to
|
||||||
*/
|
*/
|
||||||
protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
|
protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
|
||||||
if (componentData == null){
|
/*if (componentData == null){
|
||||||
// In the case of null component data, insert the boolean false
|
|
||||||
// (preserves structure by associating data with a null component)
|
|
||||||
return componentArrays.get(componentType).insertData(entity, false);
|
return componentArrays.get(componentType).insertData(entity, false);
|
||||||
}
|
} */
|
||||||
return componentArrays.get(componentType).insertData(entity, componentData);
|
return componentArrays.get(componentType).insertData(entity, componentData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +38,7 @@ class ComponentManager{
|
|||||||
* @param entity the entity that was destroyed.
|
* @param entity the entity that was destroyed.
|
||||||
*/
|
*/
|
||||||
public void entityDestroyed(Entity entity, BitSet entityRegistrations){
|
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);
|
int index = entityRegistrations.nextSetBit(0);
|
||||||
while(index != -1){
|
while(index != -1){
|
||||||
if (!componentArrays.get(indexComponentType.get(index)).removeData(entity)){
|
if (!componentArrays.get(indexComponentType.get(index)).removeData(entity)){
|
||||||
@ -48,11 +46,6 @@ class ComponentManager{
|
|||||||
}
|
}
|
||||||
index = entityRegistrations.nextSetBit(index+1);
|
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
|
* @param type the class type of the component
|
||||||
* @return the index of the component type, or -1 if it isn't found
|
* @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{
|
try{
|
||||||
return componentPosIndex.get(type);
|
return componentPosIndex.get(type);
|
||||||
}
|
}
|
||||||
@ -84,7 +77,7 @@ class ComponentManager{
|
|||||||
* @param index the index of the component
|
* @param index the index of the component
|
||||||
* @return the class type of the index. `null` if not found
|
* @return the class type of the index. `null` if not found
|
||||||
*/
|
*/
|
||||||
protected Type getComponentType(Integer index){
|
public Type getComponentType(Integer index){
|
||||||
try{
|
try{
|
||||||
return indexComponentType.get(index);
|
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 componentType the class type of the component to remove
|
||||||
* @param entity the entity to remove the component from
|
* @param entity the entity to remove the component from
|
||||||
*/
|
*/
|
||||||
|
@ -8,13 +8,15 @@ package nz.ac.massey.javaecs;
|
|||||||
*
|
*
|
||||||
* References:
|
* References:
|
||||||
* Based on the implementation by Austin Morlan:
|
* 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.io.PrintStream;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ECS manager.
|
* The ECS manager.
|
||||||
* <p>
|
* <p>
|
||||||
@ -29,11 +31,10 @@ public class Engine {
|
|||||||
protected EntityManager entityManager;
|
protected EntityManager entityManager;
|
||||||
protected ComponentManager componentManager;
|
protected ComponentManager componentManager;
|
||||||
protected SystemManager systemManager;
|
protected SystemManager systemManager;
|
||||||
/**
|
|
||||||
* Initialises the ECS with default values
|
/***************************************
|
||||||
* <p>
|
** Engine Constructors **
|
||||||
* Maximum 1024 enitites default
|
***************************************/
|
||||||
*/
|
|
||||||
public Engine(){
|
public Engine(){
|
||||||
entityManager = new EntityManager();
|
entityManager = new EntityManager();
|
||||||
componentManager = new ComponentManager();
|
componentManager = new ComponentManager();
|
||||||
@ -50,18 +51,6 @@ public class Engine {
|
|||||||
systemManager = new SystemManager();
|
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
|
* Attempts to resize the maximum number of entities
|
||||||
* @param newSize the new 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);
|
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
|
* Signals each manager to remove the specified entity
|
||||||
* @param entity the entity to destroy
|
* @param entity the entity to destroy
|
||||||
*/
|
*/
|
||||||
public void destroyEntity(Entity entity){
|
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));
|
componentManager.entityDestroyed(entity, entityManager.getRegistrations(entity));
|
||||||
entityManager.removeEntity(entity);
|
entityManager.removeEntity(entity);
|
||||||
systemManager.entityDestroyed(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
|
* 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
|
* @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);
|
return componentManager.registerComponent(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getComponentIndex(Type type){
|
|
||||||
return componentManager.getComponentIndex(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an exisiting component to an exisiting entity
|
* Adds an exisiting component to an exisiting entity
|
||||||
* @param entity the entity to add the component to
|
* @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
|
* @return true if the compnent was added to the entity
|
||||||
*/
|
*/
|
||||||
public boolean addComponent(Entity entity, Type componentType, Object component){
|
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));
|
systemManager.entityRegistrationsChanged(entity, entityManager.getRegistrations(entity));
|
||||||
componentManager.addComponentToEntity(componentType, component, entity);
|
componentManager.addComponentToEntity(componentType, component, entity);
|
||||||
return true;
|
return true;
|
||||||
@ -124,7 +156,7 @@ public class Engine {
|
|||||||
if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity))
|
if (entityManager.unregisterComponent(componentManager.getComponentIndex(componentType), entity))
|
||||||
{
|
{
|
||||||
componentManager.removeComponentFromEntity(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;
|
return true;
|
||||||
}
|
}
|
||||||
else return false;
|
else return false;
|
||||||
@ -132,7 +164,7 @@ public class Engine {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the actual data of the component associated to the entity.
|
* 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 entity the entity to retrieve the data for
|
||||||
* @param componentType the class type of the component
|
* @param componentType the class type of the component
|
||||||
* @return the component data Object associated with the entity
|
* @return the component data Object associated with the entity
|
||||||
@ -142,12 +174,26 @@ public class Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the system to
|
* Gets the component index of the provided type
|
||||||
* @param systemType
|
* @param type the type to get the index of
|
||||||
* @param action
|
* @return the index of the component
|
||||||
*/
|
*/
|
||||||
public boolean registerSystem(Type systemType, ECSSystem action){
|
public Integer getComponentIndex(Type type){
|
||||||
return systemManager.registerSystem(systemType, action);
|
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){
|
public void setSystemSignature(Type system, BitSet signature){
|
||||||
systemManager.setRegistrationSignature(system, 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
|
* Writes an error message to the set PrintStream
|
||||||
* <p>
|
* <p>
|
||||||
* <i>Default System.err
|
* <i>Default is System.err
|
||||||
* @param message the message to write
|
* @param message the message to write
|
||||||
*/
|
*/
|
||||||
static void writeErr(String message){
|
static void writeErr(String message){
|
||||||
@ -200,19 +246,27 @@ public class Engine {
|
|||||||
return errorStream;
|
return errorStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a the current component manager
|
||||||
|
* @return the current active component manager
|
||||||
|
*/
|
||||||
public ComponentManager getComponentManager() {
|
public ComponentManager getComponentManager() {
|
||||||
return componentManager;
|
return componentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a the current enitity manager
|
||||||
|
* @return the current active enitity manager
|
||||||
|
*/
|
||||||
public EntityManager getEntityManager() {
|
public EntityManager getEntityManager() {
|
||||||
return entityManager;
|
return entityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a the current systems manager
|
||||||
|
* @return the current active systems manager
|
||||||
|
*/
|
||||||
public SystemManager getSystemManager() {
|
public SystemManager getSystemManager() {
|
||||||
return systemManager;
|
return systemManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumEntities(){
|
|
||||||
return entityManager.getNumEntities();
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ package nz.ac.massey.javaecs;
|
|||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@ -55,13 +56,10 @@ class EntityManager{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new entity
|
* 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(){
|
protected Entity addEntity() throws NoSuchElementException{
|
||||||
if (unusedEntities.size() == 0){
|
|
||||||
Engine.writeErr("No available space to create a new entity");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Entity result = unusedEntities.remove();
|
Entity result = unusedEntities.remove();
|
||||||
entityRegistrations.set(result.getValue(), new BitSet());
|
entityRegistrations.set(result.getValue(), new BitSet());
|
||||||
return result;
|
return result;
|
||||||
|
@ -24,7 +24,7 @@ class SystemManager{
|
|||||||
* @param entity the destroyed entity
|
* @param entity the destroyed entity
|
||||||
*/
|
*/
|
||||||
protected void entityDestroyed(Entity 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()) {
|
for (Type key : systems.keySet()) {
|
||||||
systems.get(key).entities.remove(entity);
|
systems.get(key).entities.remove(entity);
|
||||||
}
|
}
|
||||||
@ -32,21 +32,24 @@ class SystemManager{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals the SystemManager that an entity had its registrations changed, so
|
* 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 entity the entity that was modified
|
||||||
* @param entityRegistrations the new registrations of the entity
|
* @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()) {
|
for (Type key : systems.keySet()) {
|
||||||
// Check if the signature is null
|
// Check if the signature is null
|
||||||
if (!entityRegistrations.equals(null)){
|
if (!entityRegistrations.equals(null)){
|
||||||
BitSet srcCpy = (BitSet)entityRegistrations.clone();
|
BitSet srcCpy = (BitSet)entityRegistrations.clone();
|
||||||
srcCpy.and(systems.get(key).registrationSet);
|
ECSSystem sys = systems.get(key);
|
||||||
if (srcCpy.equals(systems.get(key).registrationSet)){ // Bitwise check if the entity is subscribed to this system
|
srcCpy.and(sys.registrationSet);
|
||||||
systems.get(key).entities.add(entity);
|
if (srcCpy.equals(sys.registrationSet)){ // Bitwise check if the entity is subscribed to this system
|
||||||
|
sys.entities.add(entity);
|
||||||
}
|
}
|
||||||
else{
|
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.ByteArrayOutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ class AppTest {
|
|||||||
void testAssignMoreThanMax(){
|
void testAssignMoreThanMax(){
|
||||||
for (int i = 0; i < gameEngine.getMaxEntities() + 5; i++) {
|
for (int i = 0; i < gameEngine.getMaxEntities() + 5; i++) {
|
||||||
if (i >= gameEngine.getMaxEntities()){
|
if (i >= gameEngine.getMaxEntities()){
|
||||||
assertThrows(IndexOutOfBoundsException.class, () -> {
|
assertThrows(NoSuchElementException.class, () -> {
|
||||||
gameEngine.createEntity();
|
gameEngine.createEntity();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user