Ecrire un code idiomatique (avec Javascript) - École de jeux vidéo en ligne Gamecodeur

Hello everybody,

ces derniers temps, je suis amené à regarder de plus en plus de code Javascript lié au jeu vidéo et il y a quelque chose qui me frappe. La plupart des GameDev Javascript que je croise sont d’anciens développeurs C/C++/C# et essayent à tout pris de garder leurs habitudes de programmation. Je trouve cela dérangeant, car Javascript possède tout un tas de concept intéressant, mais trop peu exploités par les développeurs de jeux. Je vais donc essayer de vous montrer comment écrire un code idiomatique en Javascript.

Alors, déjà, que veut dire idiomatique ? On va qualifier un code d’idiomatique lorsqu’il s’efforce de respecter la philosophie (la manière de faire) du langage de programmation dans lequel il est écrit. Autrement dit, il est possible d’écrire le même programme quasiment de la même façon en C#, Python et Javascript, mais chacun de ces langages possèdent ses spécificités qu’il s’agisse de structure de données particulières ou de différences dans la bibliothèque standard. Il est intéressant de s’approcher le plus possible de l’idiome du langage utilisé, cela permet en général d’avoir un code plus propre, plus clair (donc plus simple à maintenir) et parfois plus performant. Je vous propose une petite démonstration avec la réécriture d’un bout de programme Javascript.

Il y a peu de temps, j’ai rencontré le bout de code suivant (que j’ai adapté pour ce contexte) :

var players = [
  { x: 20, y: 20, life: 100 },
  { x: 60, y: 40, life: 100 }
];

var players_alive = [];

for (var i = 0; i < players.length; i++) {
  players[i].x -= 10;
  players[i].y -= 10;
  players[i].life -= 50;

  if (players[i].life > 0) {
    players_alive.push(players[i]);
  }
}

Ce code est volontairement simplifié, normalement il y a beaucoup plus de données et d'opérations. ;)

Pour expliquer vite fait la logique, on a un ensemble de joueurs et une explosion se produit, les joueurs sont projetés en arrières et perdent de la vie (selon leur position dans le vrai code, simplement la moitié ici).

Ce code, bien que parfaitement valide, a plusieurs problèmes, attaquons nous au premier : var.

var était autrefois la construction par défaut pour créer une variable et les développeurs C# ont tendances à le confondre avec le var de C#. Maintenant en JS, on essaye de jamais l’utiliser, car on a deux meilleures constructions : let et const. La première va nous permettre de créer une variable local qui sera mieux scopés dans son block de déclaration (il y avait des soucis avec var à ce niveau-là), la deuxième va nous permettre de créer une constante (une variable que l’on ne pourra pas réassigner). Dans un monde parfait, tout est const, cela évite les mauvaises surprises au fur et à mesure du programme et seule les données qui doivent absolument changer vont passer en let.

Une fois cela modifiée, il nous reste le plus gros problème: for. Je vais être honnête avec vous, une boucle for je trouve cela extrêmement chiant à lire. On est obligé de lire l’intégralité de la boucle pour espérer comprendre la volonté du développeur, c’est super relou. On va donc se tourner vers la programmation fonctionnelle pour essayer d’encapsuler les actions dans des fonctions. Pour l’instant, nous avons le code suivant :

const players = [
  { x: 20, y: 20, life: 100 },
  { x: 60, y: 40, life: 100 }
];

const players_alive = [];

function move_player(player, distance) {
  player.x -= distance;
  player.y -= distance;
  return player;
}

function damage_player(player, damage) {
  player.life -= damage;
  return player;
}

function is_alive(player) {
  return player.life > 0;
}

Okay, nos actions sont encapsulées et sont réutilisables. Maintenant, pour pouvoir itérer on va utiliser les second order functions. Ce sont, globalement, des fonctions qui vont nous permettent de boucler sur une structure de données afin d’effectuer un type d’action. Pour le déplacement et les dégâts, je veux juste changer les valeurs de chaque player, je vais donc mapper une nouvelle valeur (va la fonction map) .

const players = [
{ x: 20, y: 20, life: 100 },
{ x: 60, y: 40, life: 100 }
];

const players_alive = [];

// On garde en tête les fonctions présentés plus haut.

const updated_players = players.map(player => damage_player(move_player(player)));

Héhé, j’aimerais voir votre tête en ce moment. 🙂

Pour info, `x =&gt; y` c'est juste un moyen plus court pour écrire une fonction.

Cela peut sembler déroutant au début, mais en réalité ça se lit très facilement :

“updated_players correspond aux valeurs mapper de players où damage_player et move_player sont appelés sur chaque player”.

C’est aussi simple que ça. On peut appliquer une logique similaire pour garder les joueurs encore en vies, on voudra donc appliquer un filtre grâce à la fonction `filter`.

// On garde en tête les fonctions présentés plus haut.

const players_alive = updated_players.filter((player) => { is_alive(player) });

Là c’est sûrement la plus claire de toutes :

“players_alive correspond aux updated_players en vies”.

Toutes ces modifications nous donnent le code final suivant :

const players = [
  { x: 20, y: 20, life: 100 },
  { x: 60, y: 40, life: 100 }
];

function move_player(player, distance) {
  player.x -= distance;
  player.y -= distance;
  return player;
}

function damage_player(player, damage) {
  player.life -= damage;
  return player;
}

function is_alive(player) {
  return player.life > 0;
}

const updated_players = players.map(player => damage_player(move_player(player, 10), 50));
const players_alive = updated_players.filter(player => is_alive(player));

 

C’est quand même classe non ? En plus, toutes les actions sont réutilisables, on s’oriente donc vers un code plus modulaire et ça, c’est bien.

Ceci conclus cet article, n’hésitez pas à le relire et surtout à tester par vous-même, car c’est déroutant la première fois. Au besoin, je suis souvent sur le Discord GameCodeur.

Tchouss !!

Comments (2)

🔴 David Gamecodeur

“code idiomatique” …. Tu m’as appris quelque chose…
Ce que tu mentionnes ici me fait penser à tous ces codeurs c++ que j’ai croisé qui voulaient toujours mettre des points virgules à la fin des lignes de code quand ils étaient obligés de coder en autre chose, et que le revendiquaient… 🙂

Laisser un commentaire