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

Commit 552d1b82 authored by Martin Lange's avatar Martin Lange

reworked to use component `Heading` to determine behaviour

parent fb03999f
Pipeline #12173 passed with stage
in 34 seconds
......@@ -85,6 +85,8 @@ public class Position extends com.artemis.Component {
Component `Heading` contains the angle the entity is heading towards, in radians.
Entities have components `Heading` only when searching, while there is no defined heading when they are grassing. The presence or absence of the component thus defines an entity's behaviour.
```java
/// file:src/main/java/grassing/comp/Heading.java
package grassing.comp;
......@@ -115,24 +117,6 @@ public class Energy extends com.artemis.Component {
}
```
Component `IsGrassing` labels an entity as currently following the grassing behaviour. It contains no state variables.
```java
/// file:src/main/java/grassing/comp/IsGrassing.java
package grassing.comp;
public class IsGrassing extends com.artemis.Component {}
```
Component `IsSearching` labels an entity as currently following the searching behaviour. It contains no state variables.
```java
/// file:src/main/java/grassing/comp/IsSearching.java
package grassing.comp;
public class IsSearching extends com.artemis.Component {}
```
**Resources**
> Resources are objects that exist only once in the model, contrary to entities, and are potentially available for all submodels. When using the Artemis-odb, any instance of a Java class can become a resource. However, only one instance of each class is possible.
......@@ -235,7 +219,7 @@ Grassers have two different behaviours: grassing and searching. Behaviour change
## Initialization
The model is initialized with `NUM_GRASSERS` grassers. All grassers start with components `Position`, `Heading`, `Energy` and `IsGrassing` (i.e. with the grassing behaviour).
The model is initialized with `NUM_GRASSERS` grassers. All grassers start with components `Position`, `Heading` and `Energy`.
Position and heading are initialized randomly. Energy is initialized with 1.0.
......@@ -247,8 +231,7 @@ private static void createEntities(World world, Random rng) {
world.edit(entity)
.add(new Position(rng.nextFloat() * WORLD_WIDTH, rng.nextFloat() * WORLD_HEIGHT))
.add(new Heading(rng.nextFloat() * 2 * (float) Math.PI))
.add(new Energy(1f))
.add(new IsGrassing());
.add(new Energy(1f));
}
}
```
......@@ -350,7 +333,7 @@ The grasser reproduction works on all entities that have the components `Positio
In each iteration, each grasser reproduces with a fixed probability.
When reproducing, energy is split in half between the grasser and it's offspring. The offspring starts with the grassing behaviour.
When reproducing, energy is split in half between the grasser and it's offspring. The offspring starts with the grassing behaviour (i.e. with a `Heading`).
> 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.
>
......@@ -390,8 +373,7 @@ public class Reproduction extends IteratingSystem {
world.edit(entity)
.add(new Position(pos.x, pos.y))
.add(new Heading(random.rng.nextFloat() * 2 * (float) Math.PI))
.add(new Energy(e.value / 2))
.add(new IsGrassing());
.add(new Energy(e.value / 2));
e.value /= 2;
}
}
......@@ -400,13 +382,13 @@ public class Reproduction extends IteratingSystem {
### Grassing
The grassing behaviour works on all entities that have the components `Position`, `Energy` and `IsGrassing`. Thus, it ignores grassers that are currently searching.
The grassing behaviour works on all entities that have the components `Position`, `Energy` but no `Heading`. Thus, it ignores grassers that are currently searching (and thus have a `Heading`).
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`.
> The behaviour is switched by adding component `Heading`.
>
> 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.
> We extend `IteratingSystem` again, and use `@All(...)` as before, but with an additional `@Exclude(...)`. This time, we want only entities that have no `Heading`, 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 add a component `Heading`, 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
......@@ -414,19 +396,20 @@ package grassing.sys;
import com.artemis.ComponentMapper;
import com.artemis.annotations.All;
import com.artemis.annotations.Exclude;
import com.artemis.annotations.Wire;
import com.artemis.systems.IteratingSystem;
import grassing.comp.*;
import grassing.res.Grass;
import grassing.res.*;
@All({Position.class, Energy.class, IsGrassing.class})
@All({Position.class, Energy.class})
@Exclude(Heading.class)
public class GrassingBehaviour extends IteratingSystem {
ComponentMapper<Position> position;
ComponentMapper<Energy> energy;
ComponentMapper<IsGrassing> grassing;
ComponentMapper<IsSearching> searching;
@Wire Randomness random;
@Wire Grass grass;
private final float minGrass;
......@@ -449,8 +432,8 @@ public class GrassingBehaviour extends IteratingSystem {
e.value += consumption;
grass.grass.set((int) pos.x, (int) pos.y, g - consumption);
} else {
grassing.remove(id);
searching.create(id);
world.edit(id)
.add(new Heading(random.rng.nextFloat() * 2 * (float) Math.PI));
}
}
}
......@@ -458,11 +441,11 @@ public class GrassingBehaviour extends IteratingSystem {
### Searching
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 grassing behaviour system works on all entities that have the components `Position` and `Heading`. Thus, it ignores grassers that are currently grassing (and thus have no `Heading`).
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`.
> The behaviour is switched by removing component `Heading`.
```java
/// file:src/main/java/grassing/sys/SearchBehaviour.java
......@@ -477,13 +460,11 @@ import grassing.res.Grass;
import grassing.res.Randomness;
import grassing.util.MathUtil;
@All({Position.class, Heading.class, IsSearching.class})
@All({Position.class, Heading.class})
public class SearchBehaviour extends IteratingSystem {
ComponentMapper<Position> position;
ComponentMapper<Heading> heading;
ComponentMapper<IsGrassing> grassing;
ComponentMapper<IsSearching> searching;
@Wire Grass grass;
@Wire Randomness random;
......@@ -503,8 +484,7 @@ public class SearchBehaviour extends IteratingSystem {
Position pos = position.get(id);
float g = grass.grass.get((int) pos.x, (int) pos.y);
if( g >= minGrass ) {
searching.remove(id);
grassing.create(id);
heading.remove(id);
} else {
randomWalk(id);
}
......
......@@ -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, orange for searching.
The graphics system draws the amount of grass per cell by shades of green. Further, it draws searching grassers as orange triangles pointing in the direction of their heading, and grassing grassers as white rectangles.
```java
/// file:src/main/java/grassing/sys/Graphics.java
......@@ -32,13 +32,10 @@ import javax.swing.*;
import java.awt.*;
@All(Position.class)
@One({IsGrassing.class, IsSearching.class})
public class Graphics extends BaseEntitySystem {
protected ComponentMapper<Position> position;
protected ComponentMapper<Heading> heading;
protected ComponentMapper<IsGrassing> grassing;
protected ComponentMapper<IsSearching> searching;
@Wire Grass grass;
......@@ -96,22 +93,21 @@ public class Graphics extends BaseEntitySystem {
int entity = grassers.get(i);
var pos = position.get(entity);
var head = heading.get(entity);
if(grassing.has(entity)) {
if(head == null) {
g2d.setPaint(Color.WHITE);
} else if(searching.has(entity)) {
g2d.setPaint(Color.ORANGE);
g2d.fillRect((int) ((pos.x - 0.3f) * s), (int) ((pos.y - 0.3f) * s), (int) (0.6f * s), (int) (0.6f * s));
} else {
g2d.setPaint(Color.MAGENTA);
g2d.setPaint(Color.ORANGE);
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);
y[0] = (int) ((pos.y + f.y) * s);
x[1] = (int) ((pos.x + 0.4f * r.x) * s);
y[1] = (int) ((pos.y + 0.4f * r.y) * s);
x[2] = (int) ((pos.x - 0.4f * r.x) * s);
y[2] = (int) ((pos.y - 0.4f * r.y) * s);
g2d.fillPolygon(x, y, 3);
}
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);
y[0] = (int) ((pos.y + f.y) * s);
x[1] = (int) ((pos.x + 0.4f * r.x) * s);
y[1] = (int) ((pos.y + 0.4f * r.y) * s);
x[2] = (int) ((pos.x - 0.4f * r.x) * s);
y[2] = (int) ((pos.y - 0.4f * r.y) * s);
g2d.fillPolygon(x, y, 3);
}
}
}
......
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