Feel free to join the next Helmholtz Hacky Hour #26 on Wednesday, April 21, 2021 from 2PM to 3PM!

Commit 3045afde authored by Martin Lange's avatar Martin Lange

wrote what ECS is, text tweaks.

parent 8e4646a4
Pipeline #12116 passed with stage
in 12 seconds
# ECS Tutorial with Java and Artemis-odb
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鈥揅omponent鈥揝ystem (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.
```java
/// file:src/main/java/grassing/sys/Reproduction.java
......@@ -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.
```java
/// file:src/main/java/grassing/sys/GrassingBehaviour.java
......@@ -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`.
......@@ -451,7 +473,7 @@ import grassing.res.Grass;
import grassing.res.Randomness;
import grassing.util.MathUtil;
@All({Position.class, IsSearching.class})
@All({Position.class, Heading.class, IsSearching.class})
public class SearchBehaviour extends IteratingSystem {
ComponentMapper<Position> mPosition;
......@@ -504,21 +526,50 @@ public class SearchBehaviour extends IteratingSystem {
## Parameters
The world has a size of 100 x 100 cells and starts populated with 1000 grassers.
```java
/// Parameters
final static int WORLD_WIDTH = 100;
final static int WORLD_HEIGHT = 100;
final static int NUM_GRASSERS = 1000;
```
Grass growth rate is 0.01.
```java
/// Parameters
final static float GRASS_GROWTH_RATE = 0.01f;
```
Grassers start searching when grass goes below 0.1 units, and resume grassing when finding grass of at least 0.2 units.
```java
/// Parameters
final static float GRASS_START_SEARCHING = 0.1f;
final static float GRASS_START_GRASSING = 0.2f;
final static float GRASS_CONSUMPTION = 0.05f;
```
Grassers can consume 0.05 units of grass per step, and need 0.02 units per step for their metabolism.
```java
/// Parameters
final static float GRASS_CONSUMPTION = 0.05f;
final static float ENERGY_CONSUMPTION = 0.02f;
```
Grassers have a reproduction probability of 5% per step.
```java
/// Parameters
final static float REPRODUCTION_PROB = 0.05f;
```
Grassers walk with 0.25 units per step, and turn with values from a normal distribution with a standard deviation of 30掳.
```java
/// Parameters
final static float RANDOM_WALK_SPEED = 0.25f;
final static float RANDOM_WALK_ANGLE = MathUtil.deg2rad(30);
```
......
......@@ -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 {
} else {
g2d.setPaint(Color.MAGENTA);
}
//g2d.fillOval((int) (pos.x*s)-1, (int) (pos.y*s)-1, 2, 2);
var f = MathUtil.heading(head.angle);
var r = MathUtil.heading(head.angle + 0.5f * (float) Math.PI);
x[0] = (int) ((pos.x + f.x) * s);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment