Опубликовано в Gamedev
avatar
20 секунд читать

Процедурно генерируемые карты мира на Unity ёпт

По классике половина кода не моя. Да, вопросы?

Постараюсь объяснить как делать карту:

Карта высот
Карта высот
***

Шум

В интернете есть множество разных шумов, велосипед нам не нужен. Берем Accidental Noise. Надеюсь имеется минимальное знание C#.
C# синтаксиса тут нема. Довольствуемся просто текстом.

Для начала нам нужен контейнер где будут данные которые сгенерируются.
Создаем класс 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;
    }
}

Еще нам нужен класс для хранения объектов:

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

Создаем класс который нарисует ЧБ текстуру шума: 

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;
    }
}

Делаем карту высот

 Класс Generator инициализирует модуль Noise, генерирует данные карты высот, создает массив тайлов, а затем генерирует текстуру этих данных.

Желательно что бы Width и Height были кратные двум.

using UnityEngine;
using AccidentalNoise;
 
public class Generator : MonoBehaviour {
    [SerializeField]
    int Width = 256;
    [SerializeField]
    int Height = 256;
    [SerializeField]
    int TerrainOctaves = 6;
    [SerializeField]
    double TerrainFrequency = 1.25;
 
    ImplicitFractal HeightMap;
     
    MapData HeightData;
 
    Tile[,] Tiles;
     
    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);
 
        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();
                t.X = x;
                t.Y = y;
                 
                float value = HeightData.Data[x, y];
                 
                value = (value - HeightData.Min) / (HeightData.Max - HeightData.Min);
                 
                t.HeightValue = value;
 
                Tiles[x,y] = t;
            }
        }
    }
 
}

У нас получится довольно тусклое ЧБ текстура. Поэтому пихаем такую маленькую проверку: 

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

и у нас получается вот это: 

Ну и если вы не глупые, можно сделать переменные высот и их цвета:

//Высоты
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);

В конце у нас получится типо этого:

Если искать, можно найти все.

5 Комментарии

avatar
Драко🐼
3 месяца назад

Как все таки удобно читать код в отдельно созданных полях

avatar
Adobe Автор
3 месяца назад

А если бы был шарп, было бы лучше

avatar
Драко🐼
3 месяца назад

добавим

avatar
Нюхаю котов ☕
3 месяца назад

Смотрел в сторону шума Перлина?

avatar
Adobe Автор
3 месяца назад

"Accidental Noise Library is a library for generating Perlin noise and other forms of noise in a modular fashion."
Eсли перейти по ссылке Accidental Noise