I. Introduction▲
Cette série explique comment générer de manière procédurale des continents dans Unity.
Vous pouvez retrouver les autres épisodes de cette série dans le sommaire dédié.
II. Vidéo▲
Unity - Génération procédurale de terrain - Réfactorisation
III. Résumé▲
Cet épisode constitue la seconde partie de la refactorisation du code. Vous pouvez retrouver la première partie ici.
III-A. Isolation des requêtes de génération des données▲
Le fichier MapGenerator.cs contient l'intégralité du mécanisme de requête pour générer les données dans un thread secondaire. Ce code est déplacé dans une nouvelle classe ThreadedDataRequester :
using
System.
Collections;
using
System.
Collections.
Generic;
using
UnityEngine;
using
System;
using
System.
Threading;
public
class
ThreadedDataRequester :
MonoBehaviour {
static
ThreadedDataRequester instance;
Queue<
ThreadInfo>
dataQueue =
new
Queue<
ThreadInfo>(
);
void
Awake
(
) {
instance =
FindObjectOfType<
ThreadedDataRequester>
(
);
}
public
static
void
RequestData
(
Func<
object
>
generateData,
Action<
object
>
callback) {
ThreadStart threadStart =
delegate
{
instance.
DataThread (
generateData,
callback);
};
new
Thread (
threadStart).
Start (
);
}
void
DataThread
(
Func<
object
>
generateData,
Action<
object
>
callback) {
object
data =
generateData (
);
lock
(
dataQueue) {
dataQueue.
Enqueue (
new
ThreadInfo (
callback,
data));
}
}
void
Update
(
) {
if
(
dataQueue.
Count >
0
) {
for
(
int
i =
0
;
i <
dataQueue.
Count;
i++
) {
ThreadInfo threadInfo =
dataQueue.
Dequeue (
);
threadInfo.
callback (
threadInfo.
parameter);
}
}
}
struct
ThreadInfo {
public
readonly
Action<
object
>
callback;
public
readonly
object
parameter;
public
ThreadInfo (
Action<
object
>
callback,
object
parameter)
{
this
.
callback =
callback;
this
.
parameter =
parameter;
}
}
}
Nous profitons de ce travail pour généraliser le code, afin de traiter sans spécificité aussi bien des requêtes du modèle que des requêtes des hauteurs du terrain. D'une part, le code est plus aisément maintenable, mais il permet aussi d'intégrer simplement de nouveaux types de données à générer.
Par ailleurs, la classe adopte le patron de conception Singleton.
Dorénavant, pour interroger des données, il faut utiliser la syntaxe suivante :
ThreadedDataRequested.
Request
((
) =>
HeightMapGenerator.
GenerateHeightMap
(
meshSettings.
numVertsPerLine,
meshSettings.
numVertsPerLine,
heightMapSettings,
sampleCenter),
OnMapDataReceived);
L'appel à la fonction Request() incorpore un lambda pour faire correspondre les signatures de fonctions.
Le constructeur des morceaux de terrain est modifié pour prendre en paramètre un HeightMapSettings et un MeshSettings. Paramètres qui vont être copiés dans des variables membres équivalentes.
III-B. Refactorisation de EndlessTerrain.cs▲
C'est au tour du fichier EndlessTerrain.cs d'être modifié complètement. Notamment, la classe TerrainChunk (avec LODMesh) est extraite du fichier pour intégrer un nouveau fichier TerrainChunk.cs.
La structure LODInfo est sortie de la classe EndlessTerrain.
De plus, l'ajout et le retrait des morceaux de terrain sont effectués à travers le système d'événements.
La classe EndlessTerrain est renommée TerrainGenerator. L'instance de MapGenerator est supprimée. Les variables membres pour les paramètres (MeshSettings, HeightMapSettings et TextureData) sont ajoutées.
Le contenu de la fonction MapGenerator::Start() est déplacé dans la fonction TerrainGenerator::Start().
III-C. Suppression de MapGenerator.cs▲
La classe MapDisplay est renommée MapPreview. Ensuite, la majorité du contenu de MapGenerator est déplacée dans le fichier MapPreview.cs. Suivant le rendu voulu, les composants sont désactivés ou activés en accord avec le choix de l'utilisateur.
La classe MapGeneratorEditor est renommée MapPreviewEditor et prend en compte un MapPreview.
La fonction TextureGenerator::TextureFromHeightMap() ne prend plus les valeurs de la carte des hauteurs, mais l'intégralité de l'instance de HeightMap.
IV. Code source▲
Vous pouvez consulter le code de ce chapitre sur le GitHub de Sebastian Lague.
V. Commenter▲
Vous pouvez commenter et donner vos avis dans la discussion associée sur le forum.