Compare commits
No commits in common. "master" and "release" have entirely different histories.
17
README.md
17
README.md
@ -1,12 +1,10 @@
|
|||||||
# JavaECS
|
# JavaECS
|
||||||
An implementation of an Entity-Component-System written in Java.
|
An implementation of a conceptually simple Entity-Component-System written in Java.
|
||||||
|
|
||||||
## Introduction to ECS
|
## Introduction to ECS
|
||||||
The primary goal of an ECS is to provide fast access to many entities; especially where those entities share many of the same properties. It also solves issues regarding adaptability in an inheritance-based engine.
|
The primary goal of an ECS is to provide fast access to many entities; especially where those entities share many of the same properties. It also solves issues regarding adaptability in an inheritance-based engine.
|
||||||
ECS is more of a conceptual idea rather than an actual engine structure. There exists many variations of the concept, each adding in different features and models as required.
|
ECS is more of a conceptual idea rather than an actual engine structure. There exists many variations of the concept, each adding in different features and models as required.
|
||||||
|
|
||||||
The primary example of an ECS is [EnTT](https://github.com/skypjack/entt), which is used in Mojang's Minecraft.
|
|
||||||
|
|
||||||
There are four key elements to an ECS:
|
There are four key elements to an ECS:
|
||||||
1. The **entity**, which is a simple ID, usually an index.
|
1. The **entity**, which is a simple ID, usually an index.
|
||||||
2. The **component**, which is a struct or class that stores data
|
2. The **component**, which is a struct or class that stores data
|
||||||
@ -16,13 +14,14 @@ There are four key elements to an ECS:
|
|||||||
## About JavaECS
|
## About JavaECS
|
||||||
The focus of JavaECS is more about the structure rather than raw performance. It remains performant, but there may be multiple areas where improvements can be made.
|
The focus of JavaECS is more about the structure rather than raw performance. It remains performant, but there may be multiple areas where improvements can be made.
|
||||||
|
|
||||||
In a quick port of Alex Beimler's [ECS Benchmark](https://github.com/abeimler/ecs_benchmark), JavaECS performs at about the same speed as [ECS](https://github.com/redxdev/ECS) (~90 ms). The results aren't normalised between test environments, so take them with a grain of salt; but it tends to suggest that the project has decent performance.
|
|
||||||
|
|
||||||
This project is inspired by:
|
|
||||||
* [C++ implementation](https://austinmorlan.com/posts/entity_component_system/) by Austin Morlan.
|
|
||||||
* [Nomad Game Engine](https://medium.com/@savas/nomad-game-engine-part-2-ecs-9132829188e5) by Niko Savas
|
|
||||||
* [EntityX](https://github.com/alecthomas/entityx) by Alec Thomas.
|
|
||||||
|
|
||||||
|
This project is inspired by a C++ implementation by [Austin Morlan](https://austinmorlan.com/posts/entity_component_system/).
|
||||||
|
|
||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
See the [documentation](https://git.software.kauripeak.co.nz/BrychanD/JavaECS-Docs/wiki) for implementation details
|
See the [documentation](https://git.software.kauripeak.co.nz/BrychanD/JavaECS-Docs/wiki) for implmentation details
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>nz.ac.massey.javaecs</groupId>
|
<groupId>nz.ac.massey.javaecs</groupId>
|
||||||
<artifactId>javaecs</artifactId>
|
<artifactId>javaecs</artifactId>
|
||||||
<version>1.0.1</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
@ -16,21 +16,22 @@ import java.util.HashMap;
|
|||||||
/**
|
/**
|
||||||
* Manages the addition, sorting and retrieving of components and component data
|
* Manages the addition, sorting and retrieving of components and component data
|
||||||
*/
|
*/
|
||||||
public class ComponentManager{
|
class ComponentManager{
|
||||||
public Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
|
private Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
|
||||||
// Need to be able to map bit indices and component types
|
// Need to be able to map bit indices and component types
|
||||||
public Map<Integer, Type> indexComponentType = new HashMap<>();
|
private Map<Integer, Type> indexComponentType = new HashMap<>();
|
||||||
public Map<Type, Integer> componentTypeIndex = new HashMap<>();
|
private Map<Type, Integer> componentTypeIndex = new HashMap<>();
|
||||||
//
|
//
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the specified component to the provided entity.
|
* Adds the specified component to the provided entity.
|
||||||
* Does not ensure synchronisation with the other managers; ensure this is done!
|
* <p>
|
||||||
|
* Providing a null Object defaults the value to boolean false
|
||||||
* @param componentType the class type of the component to add
|
* @param componentType the class type of the component to add
|
||||||
* @param componentData the component data to associate
|
* @param componentData the component data to associate
|
||||||
* @param entity the entity to associate data to
|
* @param entity the entity to associate data to
|
||||||
*/
|
*/
|
||||||
public boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
|
protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
|
||||||
componentArrays.get(componentType).put(entity, componentData);
|
componentArrays.get(componentType).put(entity, componentData);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ import java.util.HashSet;
|
|||||||
* Additional functions can be implemented as required.
|
* Additional functions can be implemented as required.
|
||||||
*/
|
*/
|
||||||
public abstract class ECSSystem{
|
public abstract class ECSSystem{
|
||||||
public Set<Entity> entities = new HashSet<>();
|
protected Set<Entity> entities = new HashSet<>();
|
||||||
public BitSet registrationSet;
|
protected BitSet registrationSet;
|
||||||
|
|
||||||
public ECSSystem(){
|
public ECSSystem(){
|
||||||
registrationSet = new BitSet();
|
registrationSet = new BitSet();
|
||||||
|
@ -151,6 +151,7 @@ public class Engine {
|
|||||||
int componentIndex = componentManager.getComponentIndex(componentType);
|
int componentIndex = componentManager.getComponentIndex(componentType);
|
||||||
if (entityManager.registerComponent(componentIndex, entity))
|
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;
|
||||||
|
@ -19,17 +19,15 @@ import java.util.ArrayList;
|
|||||||
* Manages data from the perspective of the entity.
|
* Manages data from the perspective of the entity.
|
||||||
* <p>
|
* <p>
|
||||||
* I.e. Controls adding and removing entities, and registration and deregistration of components.
|
* I.e. Controls adding and removing entities, and registration and deregistration of components.
|
||||||
* <p>
|
|
||||||
* Do not update values in this class unless you have reviewed the JavaECS source!
|
|
||||||
*/
|
*/
|
||||||
public class EntityManager{
|
class EntityManager{
|
||||||
// According to https://stackoverflow.com/questions/12524826/why-should-i-use-deque-over-stack
|
// According to https://stackoverflow.com/questions/12524826/why-should-i-use-deque-over-stack
|
||||||
// ArrayDeque is likely faster than a LinkedList, when used in place of 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
|
// 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)
|
// at initialisation time (took 1.4s vs 1.8s for 1M)
|
||||||
public Deque<Entity> unusedEntities;
|
private Deque<Entity> unusedEntities;
|
||||||
public List<BitSet> entityRegistrations;
|
private List<BitSet> entityRegistrations;
|
||||||
public int maxSize = 1024;
|
private int maxSize = 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialise the EntityManager with the default max size of 1024
|
* Initialise the EntityManager with the default max size of 1024
|
||||||
@ -65,7 +63,7 @@ public class EntityManager{
|
|||||||
* @return the index of the new entity
|
* @return the index of the new entity
|
||||||
* @throws NoSuchElementException an exception if there are no more unused entities
|
* @throws NoSuchElementException an exception if there are no more unused entities
|
||||||
*/
|
*/
|
||||||
public Entity addEntity() throws NoSuchElementException{
|
protected Entity addEntity() throws NoSuchElementException{
|
||||||
Entity result = unusedEntities.remove();
|
Entity result = unusedEntities.remove();
|
||||||
entityRegistrations.set(result.getValue(), new BitSet());
|
entityRegistrations.set(result.getValue(), new BitSet());
|
||||||
return result;
|
return result;
|
||||||
@ -107,7 +105,7 @@ public class EntityManager{
|
|||||||
* @param entity the entity to register
|
* @param entity the entity to register
|
||||||
* @return true if the operation was successful
|
* @return true if the operation was successful
|
||||||
*/
|
*/
|
||||||
public boolean registerComponent(int component, Entity entity){
|
protected boolean registerComponent(int component, Entity entity){
|
||||||
if (entity.getValue() >= maxSize){
|
if (entity.getValue() >= maxSize){
|
||||||
Engine.writeErr("Attempted to assign a component to non-existent entity: " + entity.getValue());
|
Engine.writeErr("Attempted to assign a component to non-existent entity: " + entity.getValue());
|
||||||
return false;
|
return false;
|
||||||
@ -126,36 +124,32 @@ public class EntityManager{
|
|||||||
/**
|
/**
|
||||||
* Adds the entity index back into unusedEntities, and sets the registrations to null
|
* Adds the entity index back into unusedEntities, and sets the registrations to null
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Does not handle associated data</b> Use the method in ECS to remove entities cleanly,
|
* <b>Does not handle associated data</b> Use the method in ECS to remove entities cleanly
|
||||||
* or otherwise ensure the component data and systems are updated!
|
|
||||||
* @param entity the entity to remove
|
* @param entity the entity to remove
|
||||||
*/
|
*/
|
||||||
public void removeEntity(Entity entity){
|
protected void removeEntity(Entity entity){
|
||||||
unusedEntities.add(entity);
|
unusedEntities.add(entity);
|
||||||
entityRegistrations.set(entity.getValue(), null);
|
entityRegistrations.set(entity.getValue(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the entity's registrations to the provided BitSet.
|
* Sets the entity's registrations to the provided BitSet
|
||||||
* <p>
|
|
||||||
* Does <b>not</b> ensure the systems registrations are updated.
|
|
||||||
* @param entity the entity to set
|
* @param entity the entity to set
|
||||||
* @param registrations the preset registrations
|
* @param registrations the preset registrations
|
||||||
*/
|
*/
|
||||||
public void setRegistrations(Entity entity, BitSet registrations){
|
protected void setRegistrations(Entity entity, BitSet registrations){
|
||||||
entityRegistrations.set(entity.getValue(), registrations);
|
entityRegistrations.set(entity.getValue(), registrations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters the specified component from the entity
|
* Unregisters the specified component from the entity
|
||||||
* <p>
|
* <p>
|
||||||
* <b>Does not handle component data</b> Use the method in ECS to remove components cleanly,
|
* <b>Does not handle component data</b> Use the method in ECS to remove components cleanly
|
||||||
* or otherwise ensure the component data and systems are updated!
|
|
||||||
* @param component the component index to remove
|
* @param component the component index to remove
|
||||||
* @param entity the entity to remove
|
* @param entity the entity to remove
|
||||||
* @return true if successful
|
* @return true if successful
|
||||||
*/
|
*/
|
||||||
public boolean unregisterComponent(int component, Entity entity){
|
protected boolean unregisterComponent(int component, Entity entity){
|
||||||
try{
|
try{
|
||||||
entityRegistrations.get(entity.getValue()).clear(component);
|
entityRegistrations.get(entity.getValue()).clear(component);
|
||||||
return true;
|
return true;
|
||||||
|
@ -17,18 +17,16 @@ import java.util.HashMap;
|
|||||||
/**
|
/**
|
||||||
* Manages system-focused aspects, such as ensuring a system has the correct list of current entities.
|
* Manages system-focused aspects, such as ensuring a system has the correct list of current entities.
|
||||||
* Manages registration of new systems
|
* Manages registration of new systems
|
||||||
* <p>
|
|
||||||
* Do not update data in this class unless you have reviewed the JavaECS source.
|
|
||||||
*/
|
*/
|
||||||
public class SystemManager{
|
class SystemManager{
|
||||||
public Map<Type, ECSSystem> systems = new HashMap<>();
|
private Map<Type, ECSSystem> systems = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals the SystemManager that an entity was destroyed.
|
* Signals the SystemManager that an entity was destroyed.
|
||||||
* Removes the entity from each system's tracked entities
|
* Removes the entity from each system's tracked entities
|
||||||
* @param entity the destroyed entity
|
* @param entity the destroyed entity
|
||||||
*/
|
*/
|
||||||
public void entityDestroyed(Entity entity){
|
protected void entityDestroyed(Entity entity){
|
||||||
// Unlike components, this isn't simply indexed.
|
// 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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user