Project: Step 3.3
End of Life
Disclaimer: the automatically generated English translation is provided only for convenience and it may contain wording flaws. The original French document must be taken as reference!
Goals:
Make the entities that populate the world lose their immortality.
(If you feel a sense of power when reading this objective, it might be time to adopt a real lizard and grow attached to it).
To have a more realistic predator-prey model and avoid uncontrolled overpopulation when our animals gain the ability to reproduce, we now need to make them mortal.
More broadly, any living entity in the world can pass away for the following reasons:
- it is too old;
- it has too low an energy level (which happens if it does not eat enough, for example);
- it is devoured by another entity.
Concretely, we need to implement the fact that:
- each type of organic entity has a maximum lifespan that cannot be exceeded;
- an animal loses energy when moving, and if it moves too many times without finding food, it dies of starvation (:-/);
- an animal feeds (which may mean the end of another!).
Death from Old Age
To implement the first point, a simple idea consists of:
- Giving organic entities a "clock" associated with age (of type sf::Time). The clock is initialized to zero at birth using the value sf::Time::Zero.
- Ensuring that at each simulation step, this clock increases by dt.
- Providing organic entities with a method to check if they are dead: either because their energy level is below a certain threshold (configurable via getAppConfig().animal_min_energy, but for simplicity, identical for all entities) or because their age exceeds the possible lifespan.
[Question Q3.7] How do you propose ensuring that existing subclasses of organic entities age and can either have the default lifespan or a specific lifespan? Answer this question in your REPONSES file.
[Question Q3.8] How do you propose ensuring that a dead entity disappears from the environment? What precautions should be taken for proper memory management? Answer these questions in your REPONSES file.
Test 17: Death from Old Age
To test your new developments, you will once again use the ppsTest target.
For this, take your .json file and assign scorpions and lizards a reduced lifespan, for example, 3 (to avoid prolonging the ordeal too much).
Then:
- Launch the ppsTest target and pause the simulation (space bar).
- Create a scorpion using the 'S' key.
- Resume the simulation (space bar).
After a while, the scorpion should die of old age and disappear (somewhat abruptly, it must be said) from the environment:
| [Video: Disappearance of overly aged animals (debugging mode is disabled here)] |
Repeat the same test with a lizard (you can "clean up" the environment using the 'R' key).
Death from Starvation
The update method is currently implemented following this algorithm:
- Updating the state.
- Calculating the force f governing movement, based on the state.
- Updating movement, considering f.
- "Aging" the animal.
(It is not specified here where these different aspects of processing are coded—you will have to figure that out.)
Now, we need to modify it so that at the end of these processes, the animal loses energy (since it loses energy with every time step of the simulation).
Energy loss depends on the elapsed time step (in seconds) and the speed norm (the faster the animal moves, the more it tires) according to the following formula:
energy_loss = base_energy_expenditure + speed_norm * energy_loss_factor() * dt
base_energy_expenditure is configurable (getAppConfig().animal_base_energy_consumption). The energy loss factor is calculated differently for each type of animal (getAppConfig().scorpion_energy_loss_factor for scorpions, for example).
[Question Q3.9] How do you propose modifying the getMaxSpeed method so that below a critical energy threshold, the animal moves more slowly? Answer this question in your REPONSES file and implement this functionality according to your response (for simplicity, you may assume this threshold is the same for all living entities).
Test 18: Death from Starvation
We will use the same test file as before.
Start by configuring your simulation as follows:
- Assign scorpions and lizards a very long lifespan (1E9, for example, to clearly differentiate between the two causes of mortality).
- In the .json file, increase the energy loss factor for animals (for example, ["scorpion"]["energy"]["loss factor"] set to 0.2).
Then:
- Launch the ppsTest target and pause the simulation (space bar).
- Create a scorpion using the 'S' key.
- Resume the simulation (space bar).
| ← The scorpion loses energy over time and moves more slowly when too weak. | |
| [Video: An animal weakens over time] |
Food Consumption/Predation
Your animals lose energy while moving, so they need to regain it by consuming food. Without this, they would die too quickly.
It is therefore necessary to consider the "encounters" an animal may have while moving (Does a lizard come across food? Does a scorpion encounter a lizard? etc.).
The Collider class was introduced in the first stage of the project to allow testing for collisions between two circular bodies. It is now time to use it.
Modify your updateState method so that:
- The animal begins feeding (switches to FEEDING mode) if it collides with the nearest food source. In this case, its energy level should increase by getAppConfig().animal_meal_retention.
- The food it collides with loses energy according to rules specific to its type: lizards die if they are eaten (energy reduced to zero), whereas cacti lose getAppConfig().animal_meal_retention of their energy.
- If the animal is already in FEEDING mode, it observes a pause time (given by getAppConfig().animal_eating_pause_time) before switching back to the WANDERING state.
[Question Q3.10] How do you propose implementing the fact that predation has different effects depending on the type of prey, without using type checks? Answer this question in your REPONSES file.
Perfectionism (Bonus)
Pause time during random walk
So that the animals are not constantly moving when they are in the WANDERING state, you can make them observe random pauses in this state as well. You have at your disposal two configurable parameters if you want to implement this bonus (getAppConfig().animal_idle_probability and getAppConfig().animal_walking_idle_time).Slowdown when approaching a target
When an animal is in FOOD_IN_SIGHT mode, its target is the position of the food source, and the force governing its movement is calculated accordingly.
However, there may be a significant distance between the edge of the food source and its center. Once the animal touches the edge (collision detected), it would be preferable for it to slow down (instead of continuing to rush toward the center of the source).
The update method can therefore calculate the movement force differently once the animal enters FEEDING mode.
To implement the slowdown, you can calculate the movement force as an attraction force exerted by a negative target in the animal’s local frame of reference, for example, {-1,0}. Make the animal slow down until its speed is close to zero (test with isEqual!), at which point the force becomes zero. It may be useful to create a dedicated method for this specific force calculation.
Test 19: Food Consumption
As in the previous stage, create situations where you can observe food consumption.
The execution should resemble what is shown in these short videos:
|
|
||
| (Restart the video if needed) |
(Restart the video if needed) |
|
| |
|
Note that it is possible for the lizard to move away from the cactus before consuming it completely (this depends on where the cactus is placed and whether it enters the lizard’s field of vision).
To conduct large-scale tests, you can use the configuration file appPPS.json.
[Question Q3.11]: To perform collision tests, our design "sees" the organic entities populating the simulated world as also being Collider objects. What alternative design could be used for these processes? What advantages/disadvantages do you see in it?
Answer these questions in your REPONSES file.
Back to the project statement (Part 3) Next module (Part 3.4)