Projet : étape 3.3
Fin de vie

Buts: Faire perdre l'immortalité aux entités qui peuplent le monde
(si vous éprouvez un sentiment de puissance à l'énoncé de cet objectif, il est peut-être temps d'adopter un vrai lézard et de vous y attacher).

Pour avoir un modèle prédateur-proie plus réaliste et éviter une surpopulation incontrôlée lorsque nos animaux auront le moyen de se reproduire, il s'agit maintenant de les rendre mortels.

Plus largement, toute entité vivante du monde peut passer de vie à trépas pour les raison suivantes:

C'est à ces trois facettes que nous allons nous intéresser.

Il s'agit concrètement de mettre en oeuvre le fait :

Mort de vieillesse

Pour mettre en oeuvre le premier point, une idée simple consiste à :

  1. doter les entités organiques d'une «horloge» associée à l'âge (de type sf::Time). L'initialisation à zéro de cette horloge à la naissance de l'entité se fait au moyen de la valeur sf::Time::Zero;
  2. faire en sorte qu'à chaque pas de simulation cette horloge augmente de dt;
  3. et doter les entités organiques d'une méthode permettant de tester si elles sont mortes ou non : niveau d'énergie inférieur à un certain seuil (paramétrable via getAppConfig().animal_min_energy (mais pour simplifier, identique pour toutes les entités) ou âge dépassant la longévité possible.
La longévité maximale possible doit être paramétrable et pourra être spécifique à chaque entité. Des paramètres sont déjà prévus tels que ceux accessibles via getAppConfig().lizard_longevity ou getAppConfig().scorpion_longevity). Par défaut, la longévité sera cependant «infinie» (sf::seconds(1E9)). Les cactus pourront avoir cette longévité par défaut (mais vous pouvez en décider autrement si vous le souhaitez).

[Question Q3.7] Comment proposez-vous de procéder pour faire en sorte que les sous-classes d'entités organiques existantes vieillissent et puissent avoir soit la longévité par défaut soit une longévité spécifique? Répondez à cette question dans votre fichier REPONSES.

[Question Q3.8] Comment proposez-vous de procéder pour qu'une entité morte disparaisse de l'environnement ? Quelle précaution doit alors être prise pour une gestion correcte de la mémoire  ? Répondez à ces questions dans votre fichier REPONSES.

Test 17 : mort de vieillesse

Pour tester vos nouveaux développements vous utiliserez à nouveau la cible ppsTest.

Reprenez pour cela votre fichier .json et dotez les scorpions et les lézards d'une longévité réduite, par exemple 3 (pour ne pas faire durer trop longtemps le supplice).

Ensuite:

  1. lancez la cible ppsTest et mettez la simulation en pause (barre d'espace).
  2. créez un scorpion avec la touche 'S'
  3. relancez la simulation (barre espace);
au bout d'un moment, le scorpion devrait s'éteindre de vieillesse et disparaître (un peu abruptement ... il faut le dire) de l'environnement:

[Video : disparition des animaux trop âgés (ici le mode «debugging» est désactivé)]

Refaites le même test avec un lézard (vous pouvez "nettoyer" l'environnement avec la touche 'R').

Mort d'inanition

La méthode update est codée pour le moment selon l'algorithme suivant:

(il n'est pas précisé ici où sont codées ces différentes facettes du traitement, à vous de voir ... ).

Il convient maintenant de la compléter de sorte à ce qu'au terme de ces traitements, l'animal perde de l'énergie (il perd de l'énergie à chaque pas de temps de la simulation).

La perte d'énergie est fonction du pas de temps écoulé (en secondes) et de la norme de la vitesse (plus l'animal va vite et plus il se fatigue) selon la formule suivante:

  perte_energie = depense_energetique_de_base + norme_vitesse * facteur_perte_energie() * dt

depense_energetique_de_base est paramétrable (getAppConfig().animal_base_energy_consumption). Le facteur de perte d'énergie se calcule de façon différente pour chaque type d'animaux (getAppConfig().scorpion_energy_loss_factor pour les scorpions par exemple).

[Question Q3.9] Comment proposez-vous de modifier la méthode getMaxSpeed de sorte à ce qu'en dessous d'un seuil critique d'énergie l'animal se déplace plus lentement ? répondez à cette question dans votre fichier REPONSES et mettez en oeuvre cette fonctionnalité conformément à votre réponse (vous pouvez pour simplifier considérer que ce seuil est le même pour toute entité vivante).

Test 18 : mort d'inanition

On utilisera le même fichier de test que précédemment.

Commencez par configurer votre simulation comme suit:

Ensuite:

  1. lancez la cible ppsTest et mettez la simulation en pause (barre d'espace).
  2. créez un scorpion avec la touche 'S'
  3. relancez la simulation (barre espace);
Vous devriez voir l'énergie du scorpion baisser au fil du temps (mettez éventuellement en pause de temps en temps pour bien observer le phénomène). Au bout d'un moment, il devrait se déplacer plus lentement (énergie en dessous du seuil critique) et l'issue fatale devrait être la même que précédemment :

← le scorpion perd de l'énergie au cours du temps et se déplace plus lentement s'il est trop faible
[Video : un animal s'affaiblit au cours du temps]

Consommation de nourriture/prédation

Vos animaux perdent de l'énergie en se déplaçant, il faut qu'ils puissent en recouvrer lorsqu'ils consomment de la nourriture. Sans cela, ils seraient amenés à mourir beaucoup trop vite.

Il devient nécessaire pour cela de s'intéresser aux «rencontres» qu'un animal peut faire en cours de déplacement (un lézard croise t-elle de la nourriture ? un scorpion croise t-il un lézard ? etc.)

La classe Collider a été introduite lors de la première étape du projet afin de permettre de tester la collision entre deux corps circulaires. Il est temps de l'utiliser.

Modifiez votre méthode updateState de sorte à ce que :

  1. l'animal se mette à se nourrir (passe en mode FEEDING) s'il est collision avec la source de nourriture la plus proche. Dans ce cas son niveau d'énergie doit augmenter de getAppConfig().animal_meal_retention;
  2. la nourriture avec laquelle il entre en collision perde de l'énergie selon des modalités qui sont spécifiques à son type: les lézards meurent s'il se font manger (énergie réduite à zéro) alors que les cactus perdent getAppConfig().animal_meal_retention de leur énergie;
  3. et que si l'animal est déjà en mode FEEDING il observe un temps de pause (donné par getAppConfig().animal_eating_pause_time) avant de repasser à l'état WANDERING

[Question Q3.10] Comment proposez-vous de mettre en oeuvre le fait que la prédation n'a pas le même effet selon le type de proie, sans faire de tests de type ? répondez à cette question dans votre fichier REPONSES.

Perfectionnisme (bonus)

Temps de pause durant la marche aléatoire

Pour que les animaux ne soient pas perpetuellement en déplacement lorsqu'il sont dans l'état WANDERING, vous pouvez faire en sorte que l'animal observe aussi des temps de pauses aléatoires lorsqu'il se déplace sans but. Vous disposez de deux paramètres configurables si vous souhaitez mettre en oeuvre ce bonus (getAppConfig().animal_idle_probability et getAppConfig().animal_walking_idle_time).

Ralentissement aux abords d'une cible

Lorsque l'animal est en mode FOOD_IN_SIGHT sa cible est la position de la source de nourriture et la force régissant son déplacement est calculée en conséquence.

Il peut cependant y avoir une distance relativement importante entre le bord de la source de nourriture et son centre. Une fois que l'animal a touché le bord (collision détectée), il serait préférable qu'il ralentisse (au lieu de continuer à se précipiter vers le centre de la source).

La méthode update peut donc calculer différemment la force régissant le déplacement, lorsque l'animal est passé en mode FEEDING.

Indications

Pour mettre en oeuvre le ralentissement, on peut calculer la force régissant le déplacement comme une force d'attraction exercée par une cible négative dans le repère local de l'animal, par exemple {-1,0}. Faites ralentir l'animal jusqu'à ce que sa vitesse devienne proche de zéro (test avec isEqual !). La force devient nulle à partir de ce moment. Il peut être judicieux de faire une méthode de ce calcul de force particulier.

Test 19 : consommation de nourriture

Comme pour les tests de l'étape précédente créez des situation où vous pouvez observer la consommation de nourriture.

L'exécution devrait ressembler à celle montrée par ces petites vidéo :


le scorpion dévore le lézard
(redémarrez la vidéo au besoin)

le lézard mange le cactus par petits bouts
(redémarrez la vidéo au besoin)
[Video : prédation (scorpion mangeant un lézard)]
[Video : prédation (lézard mangeant du cactus)]
Notez qu'il est possible que le lézard s'éloigne du cactus avant de le consommer entièrement (tout dépend de où le cactus est placé et donc de si le cactus entre ou non dans le champs de vision du lézard).

Vous disposez dès maintenant d'un outil suffisamment complet pour lancer divers simulations: créez au moyen des touches G et S des populations de lézards et de scorpions et vous devriez pouvoir observer les mécanismes de consommation de nourriture et de prédation : les animaux devraient, en principe, accélérer lorsque de la nourriture est en vue et la nourriture devrait disparaitre après avoir été consommée. Pour lancer des tests à plus large échelle, vous pouvez utiliser le fichier de configuration appPPS.json.

[Question Q3.11] : Pour réaliser les tests de collision, notre conception «voit» les entités organiques qui peuplent le monde simulé comme étant aussi des Collider. Quelle autre conception est-il possible de mettre en place pour réaliser ces traitements   ? Quel avantage/inconvénient y voyez-vous  ? Répondez à ces questions dans votre fichier REPONSES.


Retour à l'énoncé du projet (partie 3) Module suivant (partie 3.4)