This projects demonstrates the use of an Entity-Component-System for the implementation of an individual-based model (IBM). We use Java and the ECS library [Artemis-odb](https://github.com/junkdog/artemis-odb) to build a simple grassing model.
This projects demonstrates the use of an Entity-Component-System (ECS) for the implementation of an individual-based model (IBM). We use Java and the ECS library [Artemis-odb](https://github.com/junkdog/artemis-odb) to build a simple grassing model.
The model description is structured following the ODD protocol (Grimm et al. 2006, 2010) to demonstrate the good fit of ECS and ODD. The complete model code is presented along with the descriptions. ECS-specific remarks are shown as indented notes:
...
...
@@ -9,7 +9,7 @@ The model description is structured following the ODD protocol (Grimm et al. 200
**Contents**
*[How to use this project?](#how-to-use-this-project)
*[What is an ECS?](#what-is-an-ecs)
*[What is an Entity-Component-System?](#what-is-an-entity-component-system)
*[Model purpose](#model-purpose)
*[Entities, state variables and scales](#entities-state-variables-and-scales)
*[Process overview and scheduling](#process-overview-and-scheduling)
...
...
@@ -25,17 +25,39 @@ The model description is structured following the ODD protocol (Grimm et al. 200
## How to use this project?
See the **[How to](md/howto.md)** for details on the possible ways to use this project, from simply running downloaded binaries to cloning and building on your machine.
The main purpose of this project is for you to read this page.
## What is an ECS?
See the **[How to](md/howto.md)** for details on further usage, from simply running downloaded binaries to cloning and building it on your machine.
## What is an Entity-Component-System?
Entity–Component–System (ECS) is an architectural pattern originating from game design, but also useful for the development of (individual-based) models (IBMs).
Entities of a model are just that in an ECS, generic "somethings", or merely just an ID. Each entity has one or more components, which can contain state variables or simply serve as labels. What an entity is results from the composition of it's components, and this composition can even change at runtime.
Systems are where the logic of the model sits, and represent (in most cases) what is called a submodel in modelling terms. Each system operates on all entities with a certain combination of components. Examples of this kind of logic could be:
* If it has a position and a velocity, it moves (i.e. changes position).
* If it has a velocity and a mass, is falls due to gravity (i.e. changes velocity).
The respective systems do not care what the entity really "is", nor are they interested in any further potential components.
Besides benefits for performance and maintainability, ECS offers great flexibility to create many different kinds of entities. This is mainly an advantage in the field of game design, were ECS was invented. However, in individual-based models, there are rarely more than a few "types" of entities, so how can IBM design benefit from ECS? Here are some thoughts:
* Flexibility: ECS is perfectly suited for iterative development of, or experimentation with a model. Submodels can be easily swapped out. Entities can get new or different state variables easily, if a new or alternative submodel requires it.
* Maintainability: Data (components) and logic (systems) are well separated, and the structure of the code follows well-known principles.
* ...
* With an ECS-thinking about "types" of entities, much more types can arise in the model. E.g. from the view of a birth submodel, a pregnant animal is a different type than a non-pregnant animal (which is not of interest for the system)
...
## Model purpose
The purpose of this model is to demonstrate the use of Entity-Component-Systems for individual-based models (IBMs).
The purpose of this model is to demonstrate the use of Entity-Component-Systems for individual-based models.
## Entities, state variables and scales
In an ECS, all entities are generic, but characterized by the components they possess. Components contain an entity's state variables.
> In an ECS, all entities are generic, but characterized by the components they possess. Components contain an entity's state variables.
**Components**
...
...
@@ -115,7 +137,7 @@ public class IsSearching extends com.artemis.Component {}
> Resources are "things" that exist only once in the model, contrary to entities, and are potentially available for all submodels. Any instance of a Java class can become a resource. However, only one instance of each class is possible.
Grassers live on a two-dimensional landscape. The landscape is represented by a grid of grass the grassers consume.
Grassers live in a two-dimensional world. The world is represented by a grid of grass the grassers can consume.
```java
/// file:src/main/java/grassing/res/Grass.java
...
...
@@ -154,13 +176,13 @@ public class Randomness {
}
```
Spatial and temporal scales of the model are arbitrary. The duration of a model step is by orders of magnitude shorter than the average lifespan of an grasser.
Spatial and temporal scales of the model are arbitrary. The duration of a model step is by orders of magnitude shorter than the average lifespan of a grasser. Grid cells are small enough to be rapidly traversed by a grasser.
## Process overview and scheduling
Processes in the model are grass growth, grasser metabolism, grasser reproduction and the two grasser behaviours grassing and searching. Processes are executed in that order.
> In the ECS, processes (or submodels, or systems) are added during world construction via `with(...)`. Resources are added via `.register(...)`.
> In the ECS, processes (or submodels, or systems) are added during world construction via `with(...)`. Resources are added via `register(...)`.
```java
/// file:src/main/java/grassing/Main.java
...
...
@@ -209,9 +231,9 @@ public class Main {
## Design details
Grassers grass on the grass, and grass regrows. Grassers have a metabolism consuming their energy. When energy drops to zero, a grasser dies. Grassers reproduce stochastically.
Grassers consume the grass and convert it to energy. The grass regrows. Grassers have a metabolism consuming their energy. When energy drops to zero, a grasser dies. Grassers reproduce stochastically.
Grassers have two different behaviours: grassing and searching. Behaviour changes are based on grass availability.
Grassers have two different behaviours: grassing and searching. Behaviour changes are based on grass availability in the current location.
## Initialization
...
...
@@ -328,7 +350,7 @@ When reproducing, energy is split in half between the grasser and it's offspring
> We extend `IteratingSystem` again. This time we wrote `@All({Position.class, Energy.class})`, which means that we are interested only in entities that have all listed components.
>
> This system also shows how to create entities in a running model.
> This system shows how to create entities in a running model.
@@ -374,13 +396,13 @@ public class Reproduction extends IteratingSystem {
### Grassing
The grassing behaviour system works on all entities that have the components `Position`, `Energy` and `IsGrassing`.
The grassing behaviour works on all entities that have the components `Position`, `Energy` and `IsGrassing`. Thus, it ignores grassers that are currently searching.
If the grasser's energy is 1.0 or above, it does nothing. Otherwise, the grasser decides if there is enough grass in it's landscape cell. If this is the case, the grasser consumes grass, otherwise it switches to the searching behaviour.
If the grasser's energy is 1.0 or above, it does nothing. Otherwise, the grasser decides if there is enough grass in it's grid cell. If this is the case, the grasser consumes grass and converts it to energy. Otherwise, the grasser switches to the searching behaviour.
> The behaviour is switched by removing component `IsGrassing` and adding a new component `IsSearching`.
>
> We extend `IteratingSystem` again, and use `@All(...)` as before, but with an additional component. This time, we want only entities that have `IsGrassing`, in addition to `Position` and `Energy`, which is optional according to our model logic. Thus, grassers that are searching instead of grassing will not be processed by this system. In the `else` branch at the end of the code, we remove `IsGrassing` and add `IsSearching` instead, which means that the respective entity will not be processed here in the next step.
> We extend `IteratingSystem` again, and use `@All(...)` as before, but with an additional component. This time, we want only entities that have `IsGrassing` in addition to `Position` and `Energy`, which is optional according to our model logic. Thus, grassers that are searching instead of grassing will not be processed by this system. In the `else` branch at the end of the code, we remove `IsGrassing` and add `IsSearching` instead, which means that the respective entity will not be processed by this system in the next step.
@@ -432,9 +454,9 @@ public class GrassingBehaviour extends IteratingSystem {
### Searching
The grassing behaviour system works on all entities that have the components `Position` and `IsSearching`.
The grassing behaviour system works on all entities that have the components `Position`, `Heading` and `IsSearching`. Thus, it ignores grassers that are currently grassing.
The grasser decides if there is enough grass in it's landscape cell. If this is the case, the grasser switches to the searching behaviour. Otherwise, it continues searching ba a random walk.
The grasser decides if there is enough grass in it's grid cell. If this is the case, the grasser switches to the searching behaviour. Otherwise, it continues searching by a random walk. When reaching a world borders, the grasser is turned by 180°.
> The behaviour is switched by removing component `IsSearching` and adding a new component `IsGrassing`.
@@ -11,7 +11,7 @@ This section describes code that is not vital for understanding the model or the
## Graphics
The graphics system draws the amount of grass per cell by shades of green. Further, it draws grassers as triangles pointing in the direction of their heading. Grassers are coloured according their current behaviour: white for grassing, yellow for searching.
The graphics system draws the amount of grass per cell by shades of green. Further, it draws grassers as triangles pointing in the direction of their heading. Grassers are coloured according their current behaviour: white for grassing, orange for searching.
```java
/// file:src/main/java/grassing/sys/Graphics.java
...
...
@@ -105,7 +105,6 @@ public class Graphics extends BaseSystem {