Intelligence artificielle – jeux vidéo – atteindre la cible

 

Suite de l’article Intelligence artificiellejeux vidéo javascript – obstacle, il faut maintenant atteindre la cible.
Je vous conseille de lire les articles précédents dans l’ordre :
Intelligence artificiellejeux vidéo javascript – crash and turn
Intelligence artificiellejeux vidéo javascript – obstacle

 

Introduction

Dans l’article précédent, notre enemy se déplaçait, évitait les murs mais n’atteignait pas la cible. En bref, il était très con. Le rendre un peu moins bête en lui permettant d’atteindre sa cible qu’est le joueur. Tel est l’objectif de cet article.

 

Résumé

Un petit résumé de ce qui été fait jusqu’à présent.

Des fonctions relatives à l’affichage (pas d’intelligence artificielle dans cette partie) :
– une fonction de création de canvas html5 (createCanvasContext) rattaché à un html element et qui renvoie le contexte 2d associé;
– une fonction pour effacé le canvas html5 entre chaque changement de position (clearCanvas) : on pourrait s’en passer en utilisant 2 canvas html5 superposés mais là n’est pas la question;
– une fonction qui affiche un carré de 10 pixels sur 10 dans un canvas context donné, avec une couleur, et une position données;
– 3 autres fonctions dérivées de la précédente (showWall, showPlayer, showEnemy);
– une fonction pour afficher les murs (displayWall).

Un gros objet enemy a été créé : normal puisque c’est lui qui se déplace.

Cet objet compote 5 propriétés :
– sa position matérialisée par les propriétés x et y;
– sa direction matérialisée par la propriété currentDirection qui ne peut prendre que 4 valeurs (E, W, N, S);
– son pas de déplacement sur les axes x et y par les propriétés xStep et yStep.

Auxquelles sont rajoutées des méthodes relatives aux déplacements en lien avec les fonctions d’affichage :
– une fonction qui spécifie les pas de déplacement sur les axes X et Y à partir de la direction à prendre (setDSPStepFromCurrentDirection);
– une fonction qui change la position en fonction des pas spécifiés précédemment (moveDSPToTarget);
– une fonction qui vérifie s’il y a un mur devant (wallDSPInFrontOf) à partir de la direction (currentDirection).

D’autres fonctions comme :
– celle d’initialisation du labyrinthe (initGameGrid) à partir du tableau walls;
– celle lancée au lancement du jeu (initialisation) et qui lance la boucle synchronisée avec l’affichage;
– celle lancée en boucle synchronisée (main).

La plus importante rattachée à l’objet enemy : searchDirectionToTarget, c’est ici qu’est implémentée l’intelligence artificielle, certes minimaliste, du jeu.

Un dernier petit objet, le cible appelée player : player parce que c’est le personnage que le joueur incarne et qui est poursuivi par l’enemy (l’intelligence artificielle)

 

Atteindre la cible

Tout se passe dans la méthode searchDirectionToTarget et c’est elle qu’il faut modifier pour se rapprocher du joueur et le killer (hé oui, c’est le but).

Il suffit d’ajouter une fonction qui regarde si la cible est en visibilité directe (pas de mur entre les deux). En réalité, ce sont deux fonctions : une pour l’axe des abscisses et la seconde pour l’axe des ordonnées.

Ces fonctions rattachées à l’objet enemy (l’intelligence artificielle) renvoient une structure de données de 2 valeurs :
– la première est une valeur booléenne indicatrice de la visibilité;
– la seconde est la direction (E, W, N ou S) que doit prendre l’enemy pour atteindre sa cible (en l’occurrence le joueur).

Ces fonctions utilisent le tableau walls pour répondre à la question posée.

Le code des 2 fonctions :

  // on X
  // return if player is visible
  // and direction to go
  isPlayerVisibleOnX : function() {
    var returnValue = {
	  value : true,
	  direction : undefined
	};
	var startX;
	var endX;
	
	if ( this.x < player.x ) {
	  startX = this.x;
	  endX = player.x;
	  returnValue.direction = "E";
	} else if ( this.x > player.x ) {
	  startX = player.x;
	  endX = this.x;
	  returnValue.direction = "W";
	}
	
	for (var i=startX;i<endX;i++) {
	  if (walls[this.y][i] == "#") {
		  returnValue.value = false;
	  }
	}
    return returnValue;
  },

  // on Y
  // return if player is visible
  // and direction to go
  isPlayerVisibleOnY : function() {
    var returnValue = {
	  value : true,
	  direction : undefined
	};
	var startY;
	var endY;
	
	if ( this.y < player.y ) {
	  startY = this.y;
	  endY = player.y;
	  returnValue.direction = "S";
	} else if ( this.y > player.y ) {
	  startY = player.y;
	  endY = this.y;
	  returnValue.direction = "N";
	}
	
	for (var i=startY;i<endY;i++) {
      if (walls[i][this.x] == "#") {
        returnValue.value = false;
	  }
	}
    return returnValue;
  },

Maintenant, il reste à savoir quand les appeler.

Jusqu’à présent, l’enemy change de direction dès qu’il rencontre un obstacle. En dehors de cela, il va droit devant lui.

  if ( enemy.wallDSPInFrontOf() ) {
   enemy.searchDirectionToTarget(enemy,player);
  }

par

Le fait que l’enemy change de direction dès qu’il rencontre un obstacle ne change pas. Par contre lorsqu’il n’en rencontre pas (il se déplace en ligne droite), il regarde si sa cible est visible. Si c’est le cas, il se dirige vers sa cible dont la direction est donnée par les fonctions de visibilité isPlayerVisibleOnX et isPlayerVisibleOnY.

Le code précédent devient :

  if ( enemy.wallDSPInFrontOf() ) {
   enemy.searchDirectionToTarget(enemy,player);
  } else {
    if ( enemy.isPlayerVisibleOnX().value && enemy.y == player.y ) {
		enemy.currentDirection = enemy.isPlayerVisibleOnX().direction;
	} else if ( enemy.isPlayerVisibleOnY().value && enemy.x == player.x ) {
		enemy.currentDirection = enemy.isPlayerVisibleOnY().direction;
	}
  }

Pour le test en live, cliquez TEST LIVE



 

Parfait ?

Vous voyez qu’il n’y a rien de compliquer. Mais (hé oui, il y a toujours un mais) la solution n’est pas parfaite : changez le labyrinthe en accolant pas exemple un mur horizontal à un mur vertical.

Par exemple, ceci :

var walls = {
  0:  "                    ",
  1:  "              #     ",
  2:  "              #     ",
  3:  "                    ",
  4:  "                    ",
  5:  "                    ",
  6:  "              #     ",
  7:  "         #    #     ",
  8:  "         #    #     ",
  9:  "  E           #  P  ",
  10: "              #     ",
  11: "         #   ##     ",
  12: "         #    #     ",
  13: "         #    #     ",
  14: "                    ",
  15: "                    ",
  16: "                    ",
  17: "              #     ",
  18: "              #     ",
  19: "                    "
 }

Pour le test en live, cliquez TEST LIVE



Aïe, ça fait mal : l’enemy passe à travers les murs. Le code ne fonctionne que sur labyrinthes avec des murs horizontaux et/ou verticaux mais qui ne sont pas accolés.

Comme ceci, par exemple :

 var walls = {
  0:  "                    ",
  1:  "              #     ",
  2:  "              #     ",
  3:  "                    ",
  4:  "                    ",
  5:  "                    ",
  6:  "              #     ",
  7:  "         #    #     ",
  8:  "         #    #     ",
  9:  "  E           #  P  ",
  10: "              #     ",
  11: "         # ## #     ",
  12: "         #    #     ",
  13: "         #    #     ",
  14: "                    ",
  15: "            ####    ",
  16: "                    ",
  17: "              #     ",
  18: "              #     ",
  19: "                    "
 }

Pour le test en live, cliquez TEST LIVE



 

Conclusion

Il était complètement con et en lui donnant la faculté à trouver sa cible, on lui a donné le pouvoir de passer à travers les murs.
Promis, plus de passe muraille au prochain article.

 

Des remarques, des améliorations, des idées, des coquilles : dites le. Faites savoir si cet article vous a été utile par un commentaire ou les réseaux sociaux.

 

Posté dans html5Taggé intelligence artificielle, intelligence artificielle jeu vidéo, jeu vidéo javascript  |  1 commentaire

Une réponse à "Intelligence artificielle – jeux vidéo – atteindre la cible"

Répondre