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▲
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.