Compare commits

...

5 Commits

Author SHA1 Message Date
244bb38e43 Fixed a typo 2022-01-10 13:39:43 +13:00
4731981977 Update 'README.md' 2021-06-15 18:28:27 +12:00
fc480cfe2e Update 'README.md' 2021-06-15 18:26:43 +12:00
2c96e92a6a Changed access modifiers to public
Changed access so data may be manually changed, if needed
2021-06-15 17:59:03 +12:00
ccabef5226 Made Manager classes public 2021-06-14 18:20:29 +12:00
7 changed files with 41 additions and 34 deletions

View File

@ -1,10 +1,12 @@
# JavaECS
An implementation of a conceptually simple Entity-Component-System written in Java.
An implementation of an 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
@ -14,14 +16,13 @@ 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 a C++ implementation by [Austin Morlan](https://austinmorlan.com/posts/entity_component_system/).
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.
## Implementation
See the [documentation](https://git.software.kauripeak.co.nz/BrychanD/JavaECS-Docs/wiki) for implmentation details
See the [documentation](https://git.software.kauripeak.co.nz/BrychanD/JavaECS-Docs/wiki) for implementation details

View File

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

View File

@ -16,22 +16,21 @@ import java.util.HashMap;
/**
* Manages the addition, sorting and retrieving of components and component data
*/
class ComponentManager{
private Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
public class ComponentManager{
public Map<Type, Map<Entity, Object>> componentArrays = new HashMap<>();
// Need to be able to map bit indices and component types
private Map<Integer, Type> indexComponentType = new HashMap<>();
private Map<Type, Integer> componentTypeIndex = new HashMap<>();
public Map<Integer, Type> indexComponentType = new HashMap<>();
public Map<Type, Integer> componentTypeIndex = new HashMap<>();
//
/**
* Adds the specified component to the provided entity.
* <p>
* Providing a null Object defaults the value to boolean false
* Does not ensure synchronisation with the other managers; ensure this is done!
* @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
*/
protected boolean addComponentToEntity(Type componentType, Object componentData, Entity entity){
public 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{
protected Set<Entity> entities = new HashSet<>();
protected BitSet registrationSet;
public Set<Entity> entities = new HashSet<>();
public BitSet registrationSet;
public ECSSystem(){
registrationSet = new BitSet();

View File

@ -151,7 +151,6 @@ 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,15 +19,17 @@ 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!
*/
class EntityManager{
public 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)
private Deque<Entity> unusedEntities;
private List<BitSet> entityRegistrations;
private int maxSize = 1024;
public Deque<Entity> unusedEntities;
public List<BitSet> entityRegistrations;
public int maxSize = 1024;
/**
* Initialise the EntityManager with the default max size of 1024
@ -63,7 +65,7 @@ class EntityManager{
* @return the index of the new entity
* @throws NoSuchElementException an exception if there are no more unused entities
*/
protected Entity addEntity() throws NoSuchElementException{
public Entity addEntity() throws NoSuchElementException{
Entity result = unusedEntities.remove();
entityRegistrations.set(result.getValue(), new BitSet());
return result;
@ -105,7 +107,7 @@ class EntityManager{
* @param entity the entity to register
* @return true if the operation was successful
*/
protected boolean registerComponent(int component, Entity entity){
public 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;
@ -124,32 +126,36 @@ 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
* <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
*/
protected void removeEntity(Entity entity){
public void removeEntity(Entity entity){
unusedEntities.add(entity);
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 registrations the preset registrations
*/
protected void setRegistrations(Entity entity, BitSet registrations){
public 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
* <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 entity the entity to remove
* @return true if successful
*/
protected boolean unregisterComponent(int component, Entity entity){
public boolean unregisterComponent(int component, Entity entity){
try{
entityRegistrations.get(entity.getValue()).clear(component);
return true;

View File

@ -17,16 +17,18 @@ 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.
*/
class SystemManager{
private Map<Type, ECSSystem> systems = new HashMap<>();
public class SystemManager{
public 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
*/
protected void entityDestroyed(Entity entity){
public void entityDestroyed(Entity entity){
// Unlike components, this isn't simply indexed.
for (Type key : systems.keySet()) {
systems.get(key).entities.remove(entity);