Compare commits

..

No commits in common. "master" and "release" have entirely different histories.

7 changed files with 34 additions and 41 deletions

View File

@ -1,12 +1,10 @@
# 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
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.
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:
1. The **entity**, which is a simple ID, usually an index.
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
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
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

View File

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>nz.ac.massey.javaecs</groupId>
<artifactId>javaecs</artifactId>
<version>1.0.1</version>
<version>1.0.0</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

View File

@ -16,21 +16,22 @@ import java.util.HashMap;
/**
* Manages the addition, sorting and retrieving of components and component data
*/
public class ComponentManager{
public Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
class ComponentManager{
private Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
// Need to be able to map bit indices and component types
public Map<Integer, Type> indexComponentType = new HashMap<>();
public Map<Type, Integer> componentTypeIndex = new HashMap<>();
private Map<Integer, Type> indexComponentType = new HashMap<>();
private Map<Type, Integer> componentTypeIndex = new HashMap<>();
//
/**
* 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 componentData the component data to associate
* @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);
return true;
}

View File

@ -29,8 +29,8 @@ import java.util.HashSet;
* Additional functions can be implemented as required.
*/
public abstract class ECSSystem{
public Set<Entity> entities = new HashSet<>();
public BitSet registrationSet;
protected Set<Entity> entities = new HashSet<>();
protected BitSet registrationSet;
public ECSSystem(){
registrationSet = new BitSet();

View File

@ -151,6 +151,7 @@ public class Engine {
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;

View File

@ -19,17 +19,15 @@ import java.util.ArrayList;
* Manages data from the perspective of the entity.
* <p>
* 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
// 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)
public Deque<Entity> unusedEntities;
public List<BitSet> entityRegistrations;
public int maxSize = 1024;
private Deque<Entity> unusedEntities;
private List<BitSet> entityRegistrations;
private int maxSize = 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
* @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();
entityRegistrations.set(result.getValue(), new BitSet());
return result;
@ -107,7 +105,7 @@ public class EntityManager{
* @param entity the entity to register
* @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){
Engine.writeErr("Attempted to assign a component to non-existent entity: " + entity.getValue());
return false;
@ -126,36 +124,32 @@ public class EntityManager{
/**
* Adds the entity index back into unusedEntities, and sets the registrations to null
* <p>
* <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!
* <b>Does not handle associated data</b> Use the method in ECS to remove entities cleanly
* @param entity the entity to remove
*/
public void removeEntity(Entity entity){
protected void removeEntity(Entity entity){
unusedEntities.add(entity);
entityRegistrations.set(entity.getValue(), null);
}
/**
* Sets the entity's registrations to the provided BitSet.
* <p>
* Does <b>not</b> ensure the systems registrations are updated.
* Sets the entity's registrations to the provided BitSet
* @param entity the entity to set
* @param registrations the preset registrations
*/
public void setRegistrations(Entity entity, BitSet registrations){
protected void setRegistrations(Entity entity, BitSet registrations){
entityRegistrations.set(entity.getValue(), registrations);
}
/**
* Unregisters the specified component from the entity
* <p>
* <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!
* <b>Does not handle component data</b> Use the method in ECS to remove components cleanly
* @param component the component index to remove
* @param entity the entity to remove
* @return true if successful
*/
public boolean unregisterComponent(int component, Entity entity){
protected boolean unregisterComponent(int component, Entity entity){
try{
entityRegistrations.get(entity.getValue()).clear(component);
return true;

View File

@ -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 registration of new systems
* <p>
* Do not update data in this class unless you have reviewed the JavaECS source.
*/
public class SystemManager{
public Map<Type, ECSSystem> systems = new HashMap<>();
class SystemManager{
private Map<Type, ECSSystem> systems = new HashMap<>();
/**
* Signals the SystemManager that an entity was destroyed.
* Removes the entity from each system's tracked entities
* @param entity the destroyed entity
*/
public void entityDestroyed(Entity entity){
protected void entityDestroyed(Entity entity){
// Unlike components, this isn't simply indexed.
for (Type key : systems.keySet()) {
systems.get(key).entities.remove(entity);