Unity C#で手続き的に生成された世界地図、パート1

画像



この一連の記事では、UnityとC#を使用して手続き的に生成された世界地図を作成する方法を学習します。 サイクルは4つの記事で構成されます。



内容



パート1(この記事):



はじめに

ノイズ発生

はじめに

DEM生成



パート2



1つの軸でカードを最小化する

両軸でカードを最小化する

隣接する要素を検索する

ビットマスク

注ぐ



パート3



ヒートマップの生成

湿度マップの生成

川の世代



パート4



バイオーム生成

球面地図の生成



はじめに



これらのチュートリアル記事では、次のような手順で生成されたマップを作成します。



画像



次のマップがここに表示されます。



このシリーズの次の記事では、これらのマップのデータを管理する方法を学習します。 また、地図を球面に投影する方法も検討します。



ノイズ発生



インターネットにはさまざまなノイズジェネレータがありますが、それらのほとんどはオープンソースであるため、車輪を再発明する必要はありません。 偶発的なノイズライブラリの移植版を借りました。



C#への移植はNikolaj Mariagerによって行われました。



Unityで正常に動作するように、移植されたバージョンに小さな変更が加えられました。



任意のノイズジェネレーターを使用できます。 この記事に記載されているすべての手法は、他のノイズ源にも適用できます。



はじめに



まず、生成するデータを保存するためのコンテナを作成する必要があります。



MapDataクラスを作成することから始めましょう。 変数MinとMaxは、生成された値の下限と上限を追跡するために必要です。



public class MapData { public float[,] Data; public float Min { get; set; } public float Max { get; set; } public MapData(int width, int height) { Data = new float[width, height]; Min = float.MaxValue; Max = float.MinValue; } }
      
      





また、生成されたデータからUnityゲームオブジェクトを作成するために後で使用されるTileクラスも作成します。



 public class Tile { public float HeightValue { get; set; } public int X, Y; public Tile() { } }
      
      





何が起こるかを見るには、データのグラフィカルな表現が必要です。 これを行うには、新しいTextureGeneratorクラスを作成します。



今のところ、このクラスはデータの白黒表示を作成します。



 using UnityEngine; public static class TextureGenerator { public static Texture2D GetTexture(int width, int height, Tile[,] tiles) { var texture = new Texture2D(width, height); var pixels = new Color[width * height]; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { float value = tiles[x, y].HeightValue; //Set color range, 0 = black, 1 = white pixels[x + y * width] = Color.Lerp (Color.black, Color.white, value); } } texture.SetPixels(pixels); texture.wrapMode = TextureWrapMode.Clamp; texture.Apply(); return texture; } }
      
      





すぐにこのクラスを拡張します。



DEM生成



カードのサイズは固定であると決めたので、カードの幅と高さを指定する必要があります。 また、ノイズジェネレーターのカスタマイズ可能なパラメーターも必要になります。



マップのカスタマイズがはるかに簡単になるように、Unity Inspectorでこのデータを表示します。



Generatorクラスは、Noiseモジュールを初期化し、DEMデータを生成し、タイルの配列を作成してから、このデータのテクスチャ表現を生成します。



コメント付きのコードは次のとおりです。



 using UnityEngine; using AccidentalNoise; public class Generator : MonoBehaviour { //    Unity Inspector [SerializeField] int Width = 256; [SerializeField] int Height = 256; [SerializeField] int TerrainOctaves = 6; [SerializeField] double TerrainFrequency = 1.25; //    ImplicitFractal HeightMap; //    MapData HeightData; //   Tile[,] Tiles; //    ( unity) MeshRenderer HeightMapRenderer; void Start() { //  ,       HeightMapRenderer = transform.Find ("HeightTexture").GetComponent (); //   Initialize (); //    GetData (HeightMap, ref HeightData); //        LoadTiles(); //      HeightMapRenderer.materials[0].mainTexture = TextureGenerator.GetTexture (Width, Height, Tiles); } private void Initialize() { //     HeightMap = new ImplicitFractal (FractalType.MULTI, BasisType.SIMPLEX, InterpolationType.QUINTIC, TerrainOctaves, TerrainFrequency, UnityEngine.Random.Range (0, int.MaxValue)); } //      private void GetData(ImplicitModuleBase module, ref MapData mapData) { mapData = new MapData (Width, Height); //      x,y -    for (var x = 0; x < Width; x++) { for (var y = 0; y < Height; y++) { //     float x1 = x / (float)Width; float y1 = y / (float)Height; float value = (float)HeightMap.Get (x1, y1); //      if (value > mapData.Max) mapData.Max = value; if (value < mapData.Min) mapData.Min = value; mapData.Data[x,y] = value; } } } //       private void LoadTiles() { Tiles = new Tile[Width, Height]; for (var x = 0; x < Width; x++) { for (var y = 0; y < Height; y++) { Tile t = new Tile(); tX = x; tY = y; float value = HeightData.Data[x, y]; //    0  1 value = (value - HeightData.Min) / (HeightData.Max - HeightData.Min); t.HeightValue = value; Tiles[x,y] = t; } } } }
      
      





コードを実行すると、次のテクスチャが取得されます。



画像



まだあまり面白くありませんが、始まりました。 0〜1の値を含むデータ配列があり、非常に興味深いパターンがあります。



次に、データに重要性を与える必要があります。 たとえば、0.4未満のものを水と見なします。 TextureGeneratorで以下を変更するには、0.4未満のすべての値を青に、上に白を設定します。



 if (value < 0.4f) pixels[x + y * width] = Color.blue; else pixels[x + y * width] = Color.white;
      
      





その後、次の最終画像が得られました。



画像



すでに何かをしています。 この単純なルールに一致する形状が表示されます。 次のステップに進みましょう。



Generatorクラスに他のカスタム変数を追加します。 これらは、高さが関連付けられているパラメーターを示します。



 float DeepWater = 0.2f; float ShallowWater = 0.4f; float Sand = 0.5f; float Grass = 0.7f; float Forest = 0.8f; float Rock = 0.9f; float Snow = 1;
      
      





また、テクスチャジェネレータに新しい色を追加します。



 private static Color DeepColor = new Color(0, 0, 0.5f, 1); private static Color ShallowColor = new Color(25/255f, 25/255f, 150/255f, 1); private static Color SandColor = new Color(240 / 255f, 240 / 255f, 64 / 255f, 1); private static Color GrassColor = new Color(50 / 255f, 220 / 255f, 20 / 255f, 1); private static Color ForestColor = new Color(16 / 255f, 160 / 255f, 0, 1); private static Color RockColor = new Color(0.5f, 0.5f, 0.5f, 1); private static Color SnowColor = new Color(1, 1, 1, 1);
      
      





この方法で新しいルールを追加すると、次の結果が得られます。



画像



テクスチャを表すピークの興味深いマップが得られました。



最初の部分のソースコードは、ここからダウンロードできます: World Generator Part1



第二部



All Articles