Tower Defense Three.JS

Créer un jeu de Tower Defense avec Three.JS – Partie 2 : Chargement dynamique de la carte de jeu

Ce chapitre est la seconde partie du tutoriel de programmation de notre jeu de type Tower Defense avec Three.JS.

Tower Defense Three.JS
Notre jeu de Tower Defense

Ce tutoriel est disponible au format vidéo !

Pour bien commencer, je vous suggère de débuter par la première partie :

Introduction

Dans la partie précédente, nous avons pu créer un univers de base composé des acteurs principaux de Three.JS (camera, scène, éclairage …) et un cube au centre de la scène.

Three.JS Orthographic Cube
Résultat de la partie 1

Dans cette seconde partie de notre projet, nous allons développer les fonctionnalités liées au chargement dynamique de la carte de jeu. Voici un visuel de notre objectif :

Three.JS Tower Defense part 2
Three.JS Tower Defense – Tuto 2

Chargement dynamique de la carte de jeu

Création du module JavaScript map.js

Commençons par créer un module JavaScript dans un nouveau fichier : map.js. Commençons par y inclure Three.JS :

// --- map.js ---
import * as THREE from '../js/build/three.module.js';

Les données de la carte de jeu

Puis, créons un objet JavaScript composé d’un Array à deux dimensions sur l’index data. C’est dans cette variable que sera stocké la composition de notre carte de jeu :

// ATTENTION - For this game, map width and length will be the same !

export var map0_data = {
  "data" : [
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 1, 1, 1, 1, 1, 1, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
  ]
};

Notre double tableau aura ici la même longueur dans les deux dimensions !

Le mot-clé export est utilisé pour exporter notre variable hors du module map.js, dans index.html.

Chaque coordonnée du double Array de notre variable représente un bloc de notre carte de jeu :

  • La valeur 0 représente une case normale.
  • La valeur 1 représente une case du chemin qui sera emprunté par les ennemis.
Notre Carte de Jeu
Notre Carte de Jeu

La fonction loadMap

Toujours dans le module map.js, créons une fonction loadMap. Cette fonction acceptera deux paramètres :

  1. mapdata – Une structure de données de carte ( équivalent a la variable map0_data).
  2. scene – Une scène Three.JS.
export function loadMap(mapdata, scene)
{
    [...]
}

Dans le corps ce cette fonction, créons deux variables :

  • size_Y – La hauteur du double Array dans mapdata.data.
  • size_X – La largeur du double Array dans mapdata.data.

Puis, déclarons deux objets 3D Mesh sans les ajouter à la scène :

var size_Y = mapdata.data.length;
var size_X = mapdata.data[0].length;

const material = new THREE.MeshLambertMaterial({ });
const geometry = new THREE.BoxGeometry( 2, 2, 2 );
var basic_cube = new THREE.Mesh( geometry, material );

const road_material = new THREE.MeshLambertMaterial({ color : 0x2c3e50});
var road_cube = new THREE.Mesh( geometry, road_material );

Le premier cube matérialisera chaque bloc de base ( valeur 0 dans le double Array), et le second représentera chaque bloc du chemin ( valeur 1 dans le double Array).

Puis, créons une imbrication de deux boucles for, pour parcourir chaque coordonnée du double Array mapdata.data.

for(var x = 0 ; x < size_X ; x++)
{
    for(var y = 0 ; y < size_Y ; y++)
    {
        //code
    }
}

Cette imbrication de structures for, va boucler sur chaque coordonnée possible de notre carte de jeu.

Dans l’imbrication de boucles for, créons deux variables, posx et posy. Ces dernières prennent pour valeur la position du bloc de carte à créer en fonction de la coordonnée couramment analysée.

Pour rappel, l’axe Y de Three.JS correspond à la hauteur. C’est pour cela que nous positionnons nos blocs sur les axes X et Z.

Ainsi :

var posx = (x*2) - (size_X/2)*2; // position x
var posy = (y*2) - (size_Y/2)*2; // position y ( ATTENTION, this is the Z axis in three.js universe)

Toujours dans les boucles for, créons une structure switch analysant la valeur de la case aux cordonnée courantes. Deux cas sont possibles :

  • Si la valeur de coordonnée courante est un 0, nous plaçons un bloc basique dans la scène à la position (posx , 0 , posy) XYZ.
  • Si la valeur de coordonnée courante est un 1, nous plaçons un bloc de chemin dans la scène à la position (posx , -0.2 , posy) XYZ.
Notre Carte de Jeu
Notre Carte de Jeu

Notons que le bloc de chemin sera placé légèrement plus bas que les blocs basiques, c’est pour cela que leur position sur l’axe Y est de -0.2.

Pour rappel, l’axe Y de Three.JS correspond à la hauteur. C’est pour cela que nous positionnons nos blocs sur les axes X et Z.

switch(mapdata.data[y][x])
{
    case 0: // If [x/y] value is 0 - We are creating a basic block
      var tmpbloc = basic_cube.clone();
      tmpbloc.position.set(posx, 0, posy);
      scene.add(tmpbloc);
    break;

    case 1: // If [x/y] value is 0 - We are creating a road block
      var tmpbloc = road_cube.clone();
      tmpbloc.scale.y = 0.8;
      tmpbloc.position.set(posx, -0.2, posy);
      scene.add(tmpbloc);
    break;
}

Notre fonction loadMap est désormais terminée !

Utilisation de notre module map.js dans index.html

Chargerons dès à présent ce nouveau module dans index.html. Nous importons la variable map0_data et la fonction loadMap :

import {map0_data , loadMap } from './map.js';

Supprimons le cube de test ajouté à la scène dans le chapitre précédent. Puis, appelons loadMap dans notre fonction d’initialisation init :

function init()
{
    [...]

    // ---------------- CALLING LOADING AND INIT FUNCTIONS ----------------
    loadMap(map0_data, scene);

    // ---------------- STARTING THE GAME MAIN LOOP ----------------
    render();
}

Le premier argument est map0_data, importé de map.js, le second est notre scène Three.JS !

Pour finir, notre caméra est un peu trop proche du centre de la scène, modifions la ligne de code créée dans le chapitre précédent :

camera.position.set(-15, 15, -15);

Résultat final

Téléchargez le code final : Github.

Félicitations, vous êtes arrivés à la fin de cette seconde partie ! Voila l’état de notre projet à l’issue de ce deuxième article :

Three.JS Tower Defense part 2
Three.JS Tower Defense – Partie 2

Dans la prochaine partie, nous étudierons la capture des événements de la souris et tactiles :

(2 commentaires)

Les commentaires sont fermés.