Unity – Roguelike 2D

Génération du niveau

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Le projet présenté dans cette série est tout aussi bien compatible avec Unity 4.6 qu'avec Unity 5.
Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.

II. Vidéo


Unity – Roguelike 2D – Génération du niveau


III. Résumé

Ce chapitre vous guide dans la création du script gérant la création des tuiles de manière procédurale. Grâce à ce script, les niveaux seront différents pour chaque partie.

III-A. Création des éléments

Créez deux scripts :

  • BoardManager.cs 
  • GameManager.cs (sera complété dans le chapitre suivant)

ainsi qu'un GameObject vide que l'on nommera GameManager.

III-B. BoardManager.cs

Le but de ce script est de disposer aléatoirement les tuiles de notre niveau, chaque fois que le joueur démarre un nouveau niveau.

Toutefois, quelques règles sont appliquées afin que le niveau soit toujours correct :

  • des murs sont utilisés sur les bordures du niveau ;
  • les murs intérieurs, la nourriture et les ennemis ne sont disposés qu'à l'intérieur du niveau et une allée est laissée vide, afin de s’assurer que le joueur puisse toujours atteindre la sortie ;
  • la sortie est toujours disposée en haut à droite.

Grâce à la liste gridPositions, qui contient la position de chacune des cases pouvant être utilisées pour y placer un élément, il n'est pas possible de mettre deux éléments différents sur la même case. En effet, une fois qu'une case est utilisée, celle-ci est retirée de la liste et ne peut donc pas être à nouveau choisie pour y placer un autre élément.

III-B-1. Script

 
Sélectionnez
using UnityEngine;
using System;
using System.Collections.Generic;       // Nous permet d'utiliser les listes.
using Random = UnityEngine.Random;      // Indique d'utiliser le générateur de nombres du moteur Unity.

namespace Completed
    
{
    
    public class BoardManager : MonoBehaviour
    {
        // L'utilisation de Serializable nous permet d'intégrer la classe et ses propriétés dans l'inspecteur.
        [Serializable]
        public class Count
        {
            public int minimum;             // Valeur minimale pour notre classe Count.
            public int maximum;             //Valeur maximale pour notre classe Count.
            
            
            // Constructeur.
            public Count (int min, int max)
            {
                minimum = min;
                maximum = max;
            }
        }
        
        
        public int columns = 8;                                         // Nombre de colonnes pour notre niveau.
        public int rows = 8;                                            // Nombre de lignes pour notre niveau.
        public Count wallCount = new Count (5, 9);                      // Limites basse et haute pour le nombre de murs par niveau.
        public Count foodCount = new Count (1, 5);                      // Limites basse et haute pour le nombre de nourritures par niveau. 
        public GameObject exit;                                         // Préfabriqué pour la sortie.
        public GameObject[] floorTiles;                                 // Tableau des préfabriqués de sols.
        public GameObject[] wallTiles;                                  // Tableau des préfabriqués de murs.
        public GameObject[] foodTiles;                                  // Tableau des préfabriqués de nourriture.
        public GameObject[] enemyTiles;                                 // Tableau des préfabriqués d'ennemis.
        public GameObject[] outerWallTiles;                             // Tableau des préfabriqués de murs extérieurs.
        
        private Transform boardHolder;                                  // Une variable pour stocker une référence vers la transformation de notre objet niveau.
        private List <Vector3> gridPositions = new List <Vector3> ();   // Une liste des emplacements possibles pour placer les tuiles.
        
        
        // Nettoie notre liste gridPositions et la prépare pour générer un nouveau niveau.
        void InitialiseList ()
        {
            // Nettoie la liste gridPositions.
            gridPositions.Clear ();
            
            // Boucle sur l'axe des X (colonnes).
            for(int x = 1; x < columns-1; x++)
            {
                // Pour chaque colonne, boucle sur l'axe des Y (lignes).
                for(int y = 1; y < rows-1; y++)
                {
                    // À chaque index, ajoute un nouveau Vector3 à notre liste avec les coordonnées X et Y de cette position.
                    gridPositions.Add (new Vector3(x, y, 0f));
                }
            }
        }
        
        
        // Définit les murs extérieurs et le sol (fond) du niveau.
        void BoardSetup ()
        {
            // Instancie Board et définit boardHolder à sa transformation.
            boardHolder = new GameObject ("Board").transform;
            
            // Boucle sur l'axe des X, en commençant par -1 (pour remplir le coin) avec des tuiles de sol ou de mur extérieur.
            for(int x = -1; x < columns + 1; x++)
            {
                // Boucle sur l'axe des Y, en commençant par -1 pour placer les tuiles de sol ou de mur extérieur.
                for(int y = -1; y < rows + 1; y++)
                {
                    // Sélectionne une tuile aléatoire à partir du tableau des préfabriqués de sols et la prépare pour l'instancier.
                    GameObject toInstantiate = floorTiles[Random.Range (0,floorTiles.Length)];
                    
                    // Vérifie si la position actuelle est en bordure, si c'est le cas, choisit aléatoirement un préfabriqué de mur extérieur.
                    if(x == -1 || x == columns || y == -1 || y == rows)
                        toInstantiate = outerWallTiles [Random.Range (0, outerWallTiles.Length)];
                    
                    // Instancie le GameObject en utilisant le préfabriqué choisi pour toInstantiate au Vector3 correspondant à la position de la grille actuelle, puis le converti en GameObject.
                    GameObject instance =
                        Instantiate (toInstantiate, new Vector3 (x, y, 0f), Quaternion.identity) as GameObject;
                    
                    //Définit le parent de notre nouvel objet instancié à boardHolder. Ce n'est qu'un détail organisationnel pour avoir une hiérarchie en désordre.
                    instance.transform.SetParent (boardHolder);
                }
            }
        }
        
        
        // RandomPosition retourne une position aléatoire parmi la liste gridPositions.
        Vector3 RandomPosition ()
        {
            // Déclare un entier randomIndex et lui donne une valeur aléatoire entre 0 et le nombre d'éléments de notre liste gridPositions.
            int randomIndex = Random.Range (0, gridPositions.Count);
            
            // Déclare une variable de type Vector3 appelée randomPosition et lui donne la valeur de l'élément à l'index randomIndex de la liste gridPositions.
            Vector3 randomPosition = gridPositions[randomIndex];
            
            // Enlève l'élément à l'index randomIndex de la liste, afin qu'il ne puisse être réutilisé.
            gridPositions.RemoveAt (randomIndex);
            
            // Retourne la position aléatoirement choisie.
            return randomPosition;
        }
        
        
        //La fonction LayoutObjectAtRandom accepte un tableau de GameObjects dans lequel piocher, ainsi qu'une plage d'éléments à créer.
        void LayoutObjectAtRandom (GameObject[] tileArray, int minimum, int maximum)
        {
            // Choisit un nombre aléatoire d'objets à instancier suivant la plage.
            int objectCount = Random.Range (minimum, maximum+1);
            
            // Instancie des objets tant que la limite objectCount n'est pas atteinte.
            for(int i = 0; i < objectCount; i++)
            {
                // Choisit une position aléatoire randomPosition à partir de notre liste gridPosition.
                Vector3 randomPosition = RandomPosition();
                
                // Choisit une tuile aléatoire à partir de tileArray.
                GameObject tileChoice = tileArray[Random.Range (0, tileArray.Length)];
                
                // Instancie tileChoice à la position retournée par RandomPosition sans changement d'orientation.
                Instantiate(tileChoice, randomPosition, Quaternion.identity);
            }
        }
        
        
        //La fonction SetupScene initialise notre niveau et appelle les fonctions précédentes pour définir les tuiles du niveau.
        public void SetupScene (int level)
        {
            // Crée les murs extérieurs et le sol.
            BoardSetup ();
            
            // Réinitialise la liste gridpositions.
            InitialiseList ();
            
            // Instancie un nombre aléatoire de murs à des positions aléatoires.
            LayoutObjectAtRandom (wallTiles, wallCount.minimum, wallCount.maximum);
            
            // Instancie un nombre aléatoire de nourritures à des positions aléatoires.
            LayoutObjectAtRandom (foodTiles, foodCount.minimum, foodCount.maximum);
            
            // Détermine un nombre d’ennemis selon le numéro du niveau et une progression logarithmique.
            int enemyCount = (int)Mathf.Log(level, 2f);
            
            // Instancie un nombre aléatoire d'ennemis à des positions aléatoires.
            LayoutObjectAtRandom (enemyTiles, enemyCount, enemyCount);
            
            // Instancie la tuile de sortie dans le coin haut droit de notre niveau.
            Instantiate (exit, new Vector3 (columns - 1, rows - 1, 0f), Quaternion.identity);
        }
    }
}

IV. Ressources

Vous pouvez télécharger les ressources pour ce projet sur l'Asset Store de Unity.

V. Commenter

Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2015 Unity Technologies. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.