Share via


Cet article a fait l'objet d'une traduction automatique.

Développement de jeux

Un jeu Web en une heure

Michael Oneppo

Téléchargez l'exemple de Code

Vous n'avez pas besoin un tout nouveau set habileté à développer des jeux. En fait, vos compétences actuelles de développement Web en HTML, JavaScript, CSS et ainsi de suite sont très bien pour un large éventail de jeux. Lorsque vous générez un jeu avec les technologies Web, il s'exécute sur à peu près n'importe quel appareil avec un navigateur.

Pour le prouver, je démontrerai que construire un jeu de zéro en utilisant les technologies Web et des bibliothèques externes que deux, et je vais le faire en moins d'une heure. Je vais couvrir une variété de sujets de développement de jeux, de la base de conception et de mise en page, de contrôles et de sprites, à arti­ficiels intelligence (AI) pour un simple adversaire. Je vais même à développer le jeu pour qu'elle fonctionne sur PC, tablettes et smartphones. Si vous avez une expérience avec la programmation comme un développeur Web ou un autre domaine de développement, mais aucune expérience de l'écriture des jeux, cet article va vous aider à démarrer. Si vous me donnez une heure, je vous promets de vous montrer les ficelles du métier.

Obtenir en cours d'exécution

Je vais faire tout développement dans le Visual Studio, qui permettra une exécution rapide de l'application Web que j'ai apporter des modifications. Assurez-vous d'avoir la dernière version de Visual Studio (télécharger à bit.ly/1xEjEnX) donc vous pouvez suivre le long. J'ai utilisé Visual Studio 2013 Pro, mais mis à jour le code avec la communauté de Visual Studio 2013.

Cette app n'exigera aucun code serveur, donc j'ai commencer par créer un projet vide page Web au Visual Studio. J'utiliserai le modèle vide c# pour un site Web en sélectionnant l'option Visual c# après avoir sélectionné le fichier | Nouveau | ASP.NET Site Web vide.

Le fichier HTML d'index nécessite trois ressources : jQuery, une feuille de style principale et un fichier JavaScript principal. J'ai ajouter un fichier CSS vide au projet appelé style.css et un fichier JavaScript vide appelé ping.js pour éviter les erreurs lors du chargement de la page :

<!DOCTYPE html>
<html>
<head>
  <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
  <script src="ping.js"></script>
  <link rel="stylesheet" href="style.css"></script>
</head>
<body>
</body>
</html>

Conception de base

Le jeu que je construis est une variante de Pong que j'appelle Ping. Ping a essentiellement les mêmes règles que Pong, sauf que chaque joueur attrape la balle quand il s'agit pour eux et peut alors feu le ballon en arrière soit directement, soit à un angle vers le haut ou vers le bas. Il est souvent préférable de dessiner vous voulez que le jeu à regarder avant de vous le construire. Ce jeu, la mise en page globale, je veux voir figure dans Figure 1.

la conception globale du Ping
Figure 1 la conception globale du Ping

Une fois que j'ai développé la mise en page de la conception du jeu, c'est juste une question d'ajouter chaque élément HTML à construire le jeu. Une chose à noter, cependant, c'est je vais regrouper le tableau de bord et les contrôles pour s'assurer qu'ils sont assis. Oui un par un, vous pouvez voir j'ai ajouté des éléments, comme le montre Figure 2.

Figure 2 la présentation initiale de HTML

<div id="arena">
  <div id="score">
    <h1>
      <span id="playerScore">0</span>
      -
      <span id="opponentScore">0</span>
    </h1>
  </div>
  <div id="player"></div>
  <div id="opponent"></div>
  <div id="ball"></div>
  <div id="controls-left">
    <div id="up"></div>
    <div id="down"></div>
  </div>
  <div id="controls-right">
    <div id="left"></div>
    <div id="right"></div>
  </div>
</div>

Jouez avec Style

Si vous deviez charger cette page, vous ne verriez pas n'importe quoi parce qu'il n'y a pas de style appliqué. J'ai déjà mis en place un lien vers un fichier main.css dans mon code HTML, donc je vais placer tous mes CSS dans un nouveau fichier avec ce nom. La première chose que je vais faire, c'est tout placer sur l'écran. Le corps de la page doit reprendre la totalité de l'écran, donc je vais mettre qui en premier :

body {
  margin: 0px;
  height: 100%;
}

Deuxièmement, j'ai besoin d'avoir l'arène à remplir tout l'écran avec l'image de fond de scène (voir Figure 3) appliquée :

#arena {
  background-image: url(arena.png);
  background-size: 100% 100%;
  margin: 0px;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

l'Image de fond de scène
Figure 3 l'Image de fond de scène

Ensuite, je vais placer le tableau de bord. Je veux que cela apparaisse haut et au centre, sur les autres éléments. La position de la commande : absolu me permet de placer où je veux et à gauche : 50 % le place à mi-chemin à travers le haut de la fenêtre, mais le départ à l'extrême gauche de l'élément de tableau de bord. Pour s'assurer qu'il soit parfaitement centré, j'utilise la propriété transform et la propriété z-index assure qu'il est toujours en haut :

#score {
  position: absolute;
  z-index: 1000;
  left: 50%;
  top: 5%;
  transform: translate(-50%, 0%);
}

Je tiens également à la police du texte à être sur le thème rétro. Plupart des navigateurs modernes je voudrais inclure mes propres polices. J'ai trouvé l'approprié Press Start polices 2p de codeman38 (zone38.net). Pour ajouter la police pour le tableau de bord, je dois créer un nouveau type de police :

@font-face {
  font-family: 'PressStart2P';
  src: url('PressStart2P.woff');
}

Maintenant, les scores sont dans une balise h1, je peux définir la police pour toutes les balises h1. Juste au cas où la police est manquante, je vais donner quelques options de sauvegarde :

h1 {
  font-family: 'PressStart2P', 'Georgia', serif;
}

Pour les autres éléments, je vais utiliser une feuille de sprite d'images. Une feuille de sprite contient toutes les images que j'ai besoin pour le jeu dans un seul fichier (voir Figure 4).

la feuille de Sprite pour Ping
Figure 4 la feuille de Sprite pour Ping

Un élément qui a une image sur cette feuille aura une classe sprite assignée. Ensuite, pour chaque élément, je vais utiliser background-position pour définir quelle partie de la feuille de sprite, je veux montrer :

.sprite {
  background-image: url("sprites.png");
  width: 128px;
  height: 128px;
}

Ensuite, je vais ajouter la classe sprite à tous les éléments qui utiliseront la feuille du sprite. Je vais devoir sauter brièvement vers HTML pour ce faire :

<div id="player" class="sprite"></div>
<div id="opponent" class="sprite"></div>
<div id="ball" class="sprite"></div>
<div id="controls-left">
  <div id="up" class="sprite"></div>
  <div id="down" class="sprite"></div>
</div>
<div id="controls-right">
  <div id="left" class="sprite"></div>
  <div id="right" class="sprite"></div>
</div>

Maintenant, j'ai besoin d'indiquer les positions de chaque sprite sur la feuille pour chaque élément. Encore une fois, je vais faire cela à l'aide de background-position, comme le montre Figure 5.

Figure 5 ajout des compensations pour la feuille de Sprite

#player {
  position: absolute;
  background-position: 0px 128px;
}
#opponent {
  position: absolute;
  background-position: 0px 0px;
}
#ball {
  position: absolute;
  background-position: 128px 128px;
}
#right {
  background-position: 64px 192px;
}
#left {
  background-position: 64px 0px;
}
#down {
  background-position: 128px 192px;
}
#up {
  background-position: 128px 0px;
}

La position : propriété absolue sur le joueur, l'adversaire et la boule me permettra de les déplacer de l'utilisation de JavaScript. Si vous regardez la page maintenant, vous verrez les contrôles et la boule ont des pièces inutiles qui leur sont rattachés. C'est parce que les tailles de sprite sont plus petits que les 128 pixels par défaut, donc je vais ajuster à la bonne taille. Il n'y a qu'une seule balle, donc je vais mettre sa taille directement :

#ball {
  position: absolute;
  width: 64px;
  height: 64px;
  background-position: 128px 128px;
}

Il y a quatre éléments de commande (boutons, l'utilisateur peut appuyer sur pour déplacer le joueur), alors il appartient à moi de faire une classe spéciale pour eux. Je vais aussi ajouter une marge afin qu'ils aient un peu d'espace autour d'eux :

.control {
  margin: 16px;
  width: 64px;
  height: 64px;
}

Après l'ajout de cette classe, le jeu a beaucoup mieux à la recherche de contrôles :

<div id="controls-left">
  <div id="up" class="sprite control"></div>
  <div id="down" class="sprite control"></div>
</div>
<div id="controls-right">
  <div id="left" class="sprite control"></div>
  <div id="right" class="sprite control"></div>
</div>

La dernière chose que je dois faire est de positionner les contrôles afin qu'elles soient par pouce de l'utilisateur lorsque la page s'exécute sur un périphérique mobile. Je m'en tiendrai eux aux angles inférieurs :

#controls-left {
  position: absolute;
  left: 0; bottom: 0;
}
#controls-right {
  position: absolute;
  right: 0; bottom: 0;
}

Une bonne chose à propos de cette conception est que tout est réglé avec les positions relatives. Cela signifie que l'écran peut être un certain nombre de différentes tailles, tout en faisant toujours le jeu bien paraître.

Suivez la balle qui rebondit

Maintenant, je vais faire la balle se déplacer. Pour obtenir le code JavaScript, j'ai référencé un fichier appelé ping.js dans HTML, tout comme je l'ai fait avec le CSS. Je vais ajouter ce code dans un nouveau fichier avec ce nom. Je vais faire des objets pour la balle et chacun des joueurs, mais je vais utiliser le modèle de fabrique pour les objets.

Il s'agit d'un concept simple. La fonction de boule crée une nouvelle balle quand vous l'appelez. Il n'y a pas besoin d'utiliser le mot clé new. Ce modèle réduit la confusion autour de cette variable en clarifiant les propriétés de l'objet disponible. Et parce que je n'ai qu'une heure pour faire ce jeu, j'ai besoin de minimiser tous les concepts confus.

La structure de ce modèle, comme je fais la classe balle simple, est illustrée par Figure 6.

Figure 6 la classe balle

var Ball = function( {
  // List of variables only the object can see (private variables).
  var velocity = [0,0];
  var position = [0,0];
  var element = $('#ball');
  var paused = false;
  // Method that moves the ball based on its velocity. This method is only used
  // internally and will not be made accessible outside of the object.
  function move(t) {
  }
  // Update the state of the ball, which for now just checks  
  // if the play is paused and moves the ball if it is not.  
  // This function will be provided as a method on the object.
  function update(t) {
    // First the motion of the ball is handled
    if(!paused) {
      move(t);
    }
  }
  // Pause the ball motion.
  function pause() {
    paused = true;
  }
  // Start the ball motion.
  function start() {
    paused = false;
  }
  // Now explicitly set what consumers of the Ball object can use.
  // Right now this will just be the ability to update the state of the ball,
  // and start and stop the motion of the ball.
  return {
    update:       update,
    pause:        pause,
    start:        start
}

Pour créer une nouvelle pelote, j'ai simplement appeler cette fonction, j'ai défini :

var ball = Ball();

Maintenant je veux faire le ballon se déplacent et rebondissent autour de l'écran. Tout d'abord, j'ai besoin d'appeler la fonction update à un intervalle pour créer une animation de la balle. Les navigateurs modernes fournissent une fonction destinée à cet effet, appelé requestAnimationFrame. Cela prend une fonction comme argument et appellera cette fonction passée, il lance son cycle d'animation. Cela permet à la balle se déplacer en douceur étapes lorsque le navigateur est prêt pour une mise à jour. Lorsqu'il appelle la fonction passée, il lui donnera le temps en secondes depuis la page de chargement. Cela est essentiel pour assurer les animations sont cohérentes dans le temps. Dans le jeu, l'utilisation de requestAnimationFrame s'affiche comme suit :

var lastUpdate = 0;
var ball = Ball();
function update(time) {
  var t = time - lastUpdate;
  lastUpdate = time;
  ball.update(t);
  requestAnimationFrame(update);
}
requestAnimationFrame(update);

Notez que requestAnimationFrame est rappelée dans la fonction, que la balle a fini de mettre à jour. Ceci assure l'animation continue.

Bien que ce code fonctionne, il peut y avoir un problème où le script commence à courir avant que la page est complètement chargée. Pour éviter cela, je vais le coup d'envoi le code lorsque la page est chargée, à l'aide de jQuery :

var ball;
var lastUpdate;
$(document).ready(function() {
  lastUpdate = 0;
  ball = Ball();
  requestAnimationFrame(update);
});

Parce que je sais la vitesse de la balle (vitesse) et le temps depuis sa dernière mise à jour, je peux faire quelques physique simple pour déplacer le ballon vers l'avant :

var position = [300, 300];
var velocity = [-1, -1];
var move = function(t) {
  position[0] += velocity[0] * t;
  position[1] += velocity[1] * t;  
  element.css('left', position[0] + 'px');
  element.css('top', position[1] + 'px');
}

Essayez d'exécuter le code et vous verrez la boule se déplacer à un angle et hors de l'écran. C'est amusant pour une seconde, mais une fois que le ballon s'éteint au bord de l'écran, le plaisir s'arrête. Donc la prochaine étape est de faire la balle rebondir sur les bords de l'écran, tel qu'implémenté dans Figure 7. Ajoutez ce code et l'exécution de l'application affichera une balle en mouvement en permanence.

Figure 7 Simple ballon rebondissant physique

var move = function(t) {
  // If the ball hit the top or bottom, reverse the vertical speed.
  if (position[1] <= 0 || position[1] >= innerHeight) {
   velocity[1] = -velocity[1];
  }
  // If the ball hit the left or right sides, reverse the horizontal speed.
  if (position[0] <= 0 || position[0] >= innerWidth) {
   velocity[0] = -velocity[0];
  }
  position[0] += velocity[0] * t;
  position[1] += velocity[1] * t; 
  element.css('left', (position[0] - 32) + 'px');
  element.css('top', (position[1] - 32) + 'px');
}

Un joueur mobile

Il est maintenant temps de rendre les objets de lecteur. La première étape de la refonte de la classe du joueur sera de faire la fonction déplacer modifier la position du joueur. La variable côté indiquera de quel côté de la Cour le joueur doit résider, qui va dicter comment positionner le joueur horizontalement. La valeur de y, passée dans la fonction de déplacement, sera combien vers le haut ou vers le bas le joueur se déplace :

var Player = function (elementName, side) {
  var position = [0,0];
  var element = $('#'+elementName);
  var move = function(y) {
  }
  return {
    move: move,
    getSide:      function()  { return side; },
    getPosition:  function()  { return position; }
  }
}

Figure 8 expose le mouvement du joueur, arrêtant le mouvement si le sprite de joueur atteint le haut ou le bas de la fenêtre.

Je peux maintenant créer deux joueurs et les faire passer à leur côté appropriée de l'écran :

player = Player('player', 'left');
player.move(0);
opponent = Player('opponent', 'right');
opponent.move(0);

Figure 8 contrôle des déplacements pour le Sprite de joueur

var move = function(y) {
  // Adjust the player's position.
  position[1] += y;
  // If the player is off the edge of the screen, move it back.
  if (position[1] <= 0)  {
    position[1] = 0;
  }
  // The height of the player is 128 pixels, so stop it before any
  // part of the player extends off the screen.
  if (position[1] >= innerHeight - 128) {
    position[1] = innerHeight - 128;
  }
  // If the player is meant to stick to the right side, set the player position
  // to the right edge of the screen.
  if (side == 'right') {
    position[0] = innerWidth - 128;
  }
  // Finally, update the player's position on the page.
  element.css('left', position[0] + 'px');
  element.css('top', position[1] + 'px');
}

Entrée au clavier

Donc, en théorie, vous pouvez déplacer le joueur, mais il ne bouge sans instruction préalable. Ajouter des contrôles au joueur sur la gauche. Vous voulez deux manières de contrôler ce joueur : en utilisant le clavier (sur PC) et en tapant les commandes (sur les tablettes et les téléphones).

Pour assurer la cohérence entre les entrées de contact et de souris sur diverses plates-formes, j'utiliserai le grand cadre unificateur Hand.js (handjs.codeplex.com). Tout d'abord, je vais ajouter le script au format HTML dans la section head :

 

<script src="hand.minified-1.3.8.js"></script>

Figure 9 illustre l'utilisation de Hand.js et jQuery pour contrôler le lecteur lorsque vous appuyez sur les touches A et Z, ou quand vous tapez les commandes.

Figure 9 ajout tactile et clavier contrôle

var distance = 24;  // The amount to move the player each step.
$(document).ready(function() {
  lastUpdate = 0;
  player = Player('player', 'left');
  player.move(0);
  opponent = Player('opponent', 'right');
  opponent.move(0);
  ball = Ball();
  // pointerdown is the universal event for all types of pointers -- a finger,
  // a mouse, a stylus and so on.
  $('#up')    .bind("pointerdown", function() {player.move(-distance);});
  $('#down')  .bind("pointerdown", function() {player.move(distance);});
  requestAnimationFrame(update);
});
$(document).keydown(function(event) {
  var event = event || window.event;
  // This code converts the keyCode (a number) from the event to an uppercase
  // letter to make the switch statement easier to read.
  switch(String.fromCharCode(event.keyCode).toUpperCase()) {
    case 'A':
      player.move(-distance);
      break;
    case 'Z':
      player.move(distance);
      break;
  }
  return false;
});

Attraper la balle

La balle rebondit, je tiens à laisser les joueurs à l'attraper. Quand il est pris, le ballon a un propriétaire, et il suit le mouvement de ce propriétaire. Figure 10 ajoute des fonctionnalités à la méthode de déplacement de la balle, permettant à un propriétaire, qui la balle suivra ensuite.

Figure 10 Make le ballon suivre son propriétaire

var move = function(t) {
  // If there is an owner, move the ball to match the owner's position.
  if (owner !== undefined) {
    var ownerPosition = owner.getPosition();
    position[1] = ownerPosition[1] + 64;
    if (owner.getSide() == 'left') {
      position[0] = ownerPosition[0] + 64;
    } else {
      position[0] = ownerPosition[0];
    }
  // Otherwise, move the ball using physics. Note the horizontal bouncing
  // has been removed -- ball should pass by a player if it
  // isn't caught.
  } else {
    // If the ball hits the top or bottom, reverse the vertical speed.
    if (position[1] - 32 <= 0 || position[1] + 32 >= innerHeight) {
      velocity[1] = -velocity[1];
    }
    position[0] += velocity[0] * t;
    position[1] += velocity[1] * t;  
  }
  element.css('left', (position[0] - 32) + 'px');
  element.css('top',  (position[1] - 32) + 'px');
}

Il n'existe actuellement aucun moyen d'obtenir la position d'un objet de joueur, je vais donc ajouter les accesseurs getPosition et getSide à l'objet de joueur :

return {
  move: move,
  getSide:      function()  { return side; },
  getPosition:  function()  { return position; }
}

Maintenant, si le ballon a un propriétaire, il suivra ce propriétaire. Mais comment déterminer le propriétaire ? Quelqu'un doit attraper la balle. Figure 11 montre comment déterminer quand on les sprites de joueur touche le ballon. Quand cela arrive, je vais définir le propriétaire de la balle à ce joueur.

Figure 11 détection de Collision pour le ballon et les joueurs

var update = function(t) {
// First the motion of the ball is handled.
if(!paused) {
    move(t);
}
// The ball is under control of a player, no need to update.
if (owner !== undefined) {
    return;
}
// First, check if the ball is about to be grabbed by the player.
var playerPosition = player.getPosition();
  if (position[0] <= 128 &&
      position[1] >= playerPosition[1] &&
      position[1] <= playerPosition[1] + 128) {
    console.log("Grabbed by player!");
    owner = player;
}
// Then the opponent...
var opponentPosition = opponent.getPosition();
  if (position[0] >= innerWidth - 128 &&
      position[1] >= opponentPosition[1] &&
      position[1] <= opponentPosition[1] + 128) {
    console.log("Grabbed by opponent!");
    owner = opponent;
}

Si vous essayez de jouer le jeu maintenant, vous trouverez la balle rebondit sur le haut de l'écran, et vous pouvez déplacer le joueur de l'attraper. Maintenant, comment vous le jeter ? C'est ce que les contrôles de droite pour — visant le ballon. Figure 12 ajoute une fonction de « feu » pour le joueur, mais aussi une propriété de but.

Figure 12 viser et tirer la balle

var aim = 0;
var fire = function() {
  // Safety check: if the ball doesn't have an owner, don't not mess with it.
  if (ball.getOwner() !== this) {
    return;
  }
  var v = [0,0];
  // Depending on the side the player is on, different directions will be thrown.
  // The ball should move at the same speed, regardless of direction --
  // with some math you can determine that moving .707 pixels on the
  // x and y directions is the same speed as moving one pixel in just one direction.
  if (side == 'left') {
    switch(aim) {
    case -1:
      v = [.707, -.707];
      break;
    case 0:
      v = [1,0];
      break;
    case 1:
      v = [.707, .707];
    }
  } else {
    switch(aim) {
    case -1:
      v = [-.707, -.707];
      break;
    case 0:
      v = [-1,0];
      break;
    case 1:
      v = [-.707, .707];
    }
  }
  ball.setVelocity(v);
  // Release control of the ball.
  ball.setOwner(undefined);
}
// The rest of the Ball definition code goes here...
return {
  move: move,
  fire: fire,
  getSide:      function()  { return side; },
  setAim:       function(a) { aim = a; },
  getPosition:  function()  { return position; },
}

Figure 13 augmente la fonction clavier pour définir l'objectif du joueur et de fonctions de feu. Visant fonctionne légèrement différemment. En relâchant la touche visée, le but sera de retour à simple.

Figure 13 la valeur visant fonction du joueur

$(document).keydown(function(event) {
  var event = event || window.event;
  switch(String.fromCharCode(event.keyCode).toUpperCase()) {
    case 'A':
      player.move(-distance);
      break;
    case 'Z':
      player.move(distance);
      break;
    case 'K':
      player.setAim(-1);
      break;
    case 'M':
      player.setAim(1);
      break;
    case ' ':
      player.fire();
      break;
  }
  return false;
});
$(document).keyup(function(event) {
  var event = event || window.event;
  switch(String.fromCharCode(event.keyCode).toUpperCase()) {
    case 'K':
    case 'M':
      player.setAim(0);
      break;
  }
  return false;
});

L'addition finale sera support tactile sur tous les contrôles. Je vais faire les contrôles sur la modification du droite le but du joueur. Je vais le faire aussi donc toucher n'importe où sur l'écran déclenche le ballon :

$('#left')  .bind("pointerdown", function() {player.setAim(-1);});
$('#right') .bind("pointerdown", function() {player.setAim(1);});
$('#left')  .bind("pointerup",   function() {player.setAim(0);});
$('#right') .bind("pointerup",   function() {player.setAim(0);});
$('body')   .bind("pointerdown", function() {player.fire();});

Compter les points

Lorsque le ballon passe à un joueur, je veux changer le score et donner le ballon à ce joueur. Je vais utiliser des événements personnalisés, donc je peux séparer marquant de n'importe lequel des objets existants. La fonction de mise à jour se fait longue, donc je vais ajouter une nouvelle fonction privée appelée checkScored :

function checkScored() {
  if (position[0] <= 0) {
    pause();
    $(document).trigger('ping:opponentScored');
  }
  if (position[0] >= innerWidth) {
    pause();
    $(document).trigger('ping:playerScored');
  }
}

Figure 14 illustre le code qui réagit aux événements pour mettre à jour la partition et de remettre la balle. Ajoutez ce code au bas du document JavaScript.

Figure 14 mise à jour le tableau de bord

$(document).on('ping:playerScored', function(e) {
  console.log('player scored!');
  score[0]++;
  $('#playerScore').text(score[0]);
  ball.setOwner(opponent);
  ball.start();
});
$(document).on('ping:opponentScored', function(e) {
  console.log('opponent scored!');
  score[1]++;
  $('#opponentScore').text(score[1]);
  ball.setOwner(player);
  ball.start();
});

Quand le ballon rend désormais passé votre adversaire (ce qui n'est pas si difficile, car l'adversaire ne bouge pas) vos points vont augmenter, et la balle sera remise à l'adversaire. Toutefois, l'adversaire sera il suffit de tenir le ballon.

Get Smart

Vous avez presque un jeu. Si seulement vous aviez quelqu'un avec qui jouer. Dans un dernier temps, je vais montrer comment contrôler l'adversaire avec Ia simple. L'adversaire va essayer de rester parallèle avec le ballon comme il se déplace. Si l'adversaire attrape la balle, il se déplacent au hasard et tirer la balle dans une direction aléatoire. Pour rendre l'IA se sentent un peu plus humain, je vais ajouter des retards dans tout ce que fait. Ce n'est pas très intelligent AI, vous l'esprit, mais ce sera quelque chose à jouer le match contre.

Lors de la conception de ce genre de système, il est bon de penser à des États. L'adversaire AI a trois États possibles : suivants, visant/prise de vue et en attente. Je serai l'état entre les actions suivantes pour ajouter un élément plus humain. Commencer par juste que pour l'objet de l'AI :

function AI(playerToControl) {
  var ctl = playerToControl;
  var State = {
    WAITING: 0,
    FOLLOWING: 1,
    AIMING: 2
  }
  var currentState = State.FOLLOWING;
}

Selon l'état de l'IA, je voudrai qu'il fasse une action différente. À l'instar de la balle, je vais faire une fonction de mise à jour que je peux appeler à requestAnimationFrame d'avoir l'AI agir selon son état :

function update() {
  switch (currentState) {
    case State.FOLLOWING:
      // Do something to follow the ball.
      break;
    case State.WAITING:
      // Do something to wait.
      break;
    case State.AIMING:
      // Do something to aim.
      break;
  }
}

L'état suivant est simple. L'adversaire se déplace dans le sens vertical de la balle, et les transitions de l'AI à l'attente d'État pour injecter certains ralenti le temps de réaction. Figure 15 montre ces deux États.

Figure 15 Simple suite AI

function moveTowardsBall() {
  // Move the same distance the player would move, to make it fair.
  if(ball.getPosition()[1] >= ctl.getPosition()[1] + 64) {
    ctl.move(distance);
  } else {
    ctl.move(-distance);
  }
}
function update() {
  switch (currentState) {
    case State.FOLLOWING:
      moveTowardsBall();
      currentState = State.WAITING;
    case State.WAITING:
      setTimeout(function() {
        currentState = State.FOLLOWING;
      }, 400);
      break;
    }
  }
}

Avec le code dans Figure 15, l'AI alterne entre devoir suivre le ballon et attendre une fraction de seconde. Maintenant, ajoutez le code pour la fonction de mise à jour du jeu à l'échelle :

function update(time) {
  var t = time - lastUpdate;
  lastUpdate = time;
  ball.update(t);
  ai.update();
  requestAnimationFrame(update);
}

Lorsque vous lancez le jeu, vous verrez l'adversaire suivre les mouvements de la balle, pas une mauvaise IA en moins de 30 lignes de code. Bien sûr, si l'adversaire attrape la balle, il ne fera rien. Donc, pour le dernier tour de l'heure, il est temps de gérer les actions de l'état de viser. Je veux l'IA à se déplacer au hasard quelques fois et puis lancer la balle dans une direction aléatoire. Figure 16 ajoute une fonction privée qui fait juste cela. L'ajout de la fonction aimAndFire à l'instruction case de viser fait une IA totalement fonctionnelle permettant de jouer.

Figure 16 comment viser et tirer des AI

function repeat(cb, cbFinal, interval, count) {
  var timeout = function() {
    repeat(cb, cbFinal, interval, count-1);
  }
  if (count <= 0) {
    cbFinal();
  } else {
    cb();
    setTimeout(function() {
      repeat(cb, cbFinal, interval, count-1);
    }, interval);
  }
}
function aimAndFire() {
  // Repeat the motion action 5 to 10 times.
  var numRepeats = Math.floor(5 + Math.random() * 5);
  function randomMove() {
    if (Math.random() > .5) {
      ctl.move(-distance);
    } else {
      ctl.move(distance);
    }
  }
  function randomAimAndFire() {
    var d = Math.floor( Math.random() * 3 - 1 );
    opponent.setAim(d);
    opponent.fire();
    // Finally, set the state to FOLLOWING.
    currentState = State.FOLLOWING;
  }
  repeat(randomMove, randomAimAndFire, 250, numRepeats);
}

Récapitulation

Maintenant, vous avez un jeu à part entière de Web qui fonctionne sur PC, les smartphones et les tablettes. Il y a beaucoup d'améliorations possibles à ce jeu. Cela ressemblera un peu maladroite en mode portrait sur un smartphone, par exemple, si vous devez vous assurer que vous tenez le téléphone en paysage pour qu'il fonctionne correctement. C'est juste une petite démonstration des possibilités de développement de jeux pour le Web et au-delà.


Michael Oneppo *est technicien créatif et responsable de l'ancien programme chez Microsoft dans l'équipe de Direct3D. Ses activités récentes incluent le travail comme directeur technique à la technologie sans but lucratif bibliothèque pour tous et explorer une maîtrise à l'Université de New York Interactive Telecommunications Program.*​

Grâce à l'expert technique suivante pour l'examen de cet article : Mohamed Ameen Ibrahim