Chargement...
AUTRES ARTICLES miyajimamizy dynamic poses
Intelligence ArtificielleDécembre 12, 2017

STEERING BEHAVIORS

Il est possible d’imiter le mouvement d’êtres vivants grâce à une série d’algorithmes baptisés les Steering Behaviors. Dans l’optique de maîtriser leur utilisation, j’ai ici simulé le vol en groupe des oiseaux. Ce projet s’intègre dans l’apprentissage du livre “Programming game AI by exemple” de Mat Buckland.

post image

Pour maintenir un contact visuel constant avec les autres membres du groupe, les oiseaux migratoires adoptent la formation en V. Son fonctionnement est le suivant : un leader présent au début dirige la troupe librement, à son arrière deux files d’agents suiveurs sont orientés à sa droite et à gauche. Tous les membres sont à la même distance les uns des autres.

Problème 01: Garder la formation constante

La première étape dans la construction de la formation en V est de faire suivre un agent leader par un agent poursuiveur. Il est possible de prendre pour cible la position du leader et de pousser l’agent à y aller grâce au bon vecteur de force. À chaque pas de jeu, notre agent va donc se diriger vers la nouvelle position du leader. Cela nous donne le code suivant :

Vector2D SteeringBehavior::Seek(Vector2D TargetPos)
{
	Vector2D DesiredVelocity = Vec2DNormalize(TargetPos - m_pVehicle->Pos()) * m_pVehicle->MaxSpeed();

	return (DesiredVelocity - m_pVehicle->Velocity());
}

Ce comportement n’est cependant pas satisfaisant, car la formation n'est pas constante dans le temps, il y a trop de variations entre les distances des deux agents. Cela est causé par plusieurs facteurs que j’ai ici listés :

  1. L’agent n’arrive presque jamais à destination, en effet, il se dirige à la position actuelle de la cible et non pas celle vers laquelle elle tend. L’agent est donc par définition toujours en retard.
  2. Si l’agent arrive à atteindre sa cible, il la dépasse, car sa vitesse est trop grande.
  3. La cible étant la position de l’agent leader, si l’agent remplit les deux critères précédents, il ne fera qu’un avec le leader au lieu de compléter la formation en V.

Il est maintenant temps d'améliorer ce comportement en trouvant des solutions à ces problèmes afin de garder la formation constante, dans l’ordre :

  1. L’agent va maintenant prédire la future position du leader afin de l’atteindre à tous les coups. C’est un mouvement de poursuite.
  2. L’agent va réduire progressivement sa vitesse lorsqu’il arrive à proximité de sa cible, jusqu'à s'arrêter si besoin.
  3. La cible n’est plus le leader, mais une position relative à la sienne (un offset) placée à son arrière.

Ces solutions intégrées au code précédent donnent ceci :

Vector2D SteeringBehavior::OffsetPursuit(const Vehicle* leader, const Vector2D offset)
{
	// Calculate the offset's position in world space
	Vector2D WorldOffsetPos = PointToWorldSpace(offset, leader->Heading(), leader->Side(), leader->Pos());

	Vector2D ToOffset = WorldOffsetPos - m_pVehicle->Pos();

	// The lookahead time is propotional to the distance between the leader
	// and the pursuer; and is inversely proportional to the sum of both
	// agent's velocities
	double LookAheadTime = ToOffset.Length() / (m_pVehicle->MaxSpeed() + leader->Speed());

	// Now Arrive at the predicted future position of the offset
	return Arrive(WorldOffsetPos + leader->Velocity() * LookAheadTime, fast);
}
Problème 02: Attribuer les cibles

Maintenant que notre comportement intègre un agent qui reste en position derrière un leader, il faut maintenant créer la formation en V. Pour commencer on peut faire en sorte que chaque agent prenne pour cible l’agent juste avant lui pour créer une file.

Les agents sont tous stockés dans une liste de pointeurs, il suffit alors de prendre un agent sur deux pour les mettre dans deux nouvelles listes, une pour la file de droite et une pour la file de gauche. Pour des raisons d’optimisation, je n’ai cependant pas créé de liste supplémentaire, chaque agent poursuivant alors celui qui est à deux positions avant lui dans la liste générale de véhicules.

Vector2D offsetLeft = Vector2D(-10, -10);
Vector2D offsetRight = Vector2D(-10, 10);
boolean bEven = true;
					  
m_Vehicles[0]->Steering()->FlockingOff();
m_Vehicles[1]->Steering()->FlockingOff();
					  
// Starts from the second agent to avoid list overflow
for (int i = 2; i < Prm.NumAgents - 1; ++i)
	{
		m_Vehicles[i]->Steering()->FlockingOff();
		m_Vehicles[i]->Steering()->SetSummingMethod(SteeringBehavior::weighted_average);
									
		// One agent on two go to the left queue
		// meaning that the agent before on the queue
		// is 2 position before on the list
		if (bEven)
		{
			m_Vehicles[i]->Steering()->OffsetPursuitOn(m_Vehicles[i - 2], offsetLeft);
			bEven = false;
		}
		else
		{
			m_Vehicles[i]->Steering()->OffsetPursuitOn(m_Vehicles[i - 2], offsetRight);
			bEven = true;
		}
	}