Unity - Génération procédurale de terrain

Refactorisation, deuxième partie

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

ThreadedDataRequester
Sélectionnez
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 :

 
Sélectionnez
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.

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

  

Copyright © 2018 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.