반응형
    
    
    
  이전 글에서는 Fractal noise를 이용하여 자연스러운 지형을 만들어 보았습니다
마지막으로 이번 글에서는 만든 지형에 바이옴을 적용하는 법을 알아보도록 하겠습니다
바이옴을 자연스럽게 적용하기 위해서는 Voronoi Noise를 사용하여 생성할 수 있습니다
Voronoi Noise란? : 임의의 여러 점을 선택하고 그 점으로부터 가장 가까운 점을 얼마나 가까운지 표현한 노이즈
이 알고리즘대로 Voronoi Noise를 생성한다면 이런 노이즈가 생성됩니다

우리는 바이옴을 표현하기 위하여 값을 거리가 아닌 그 점으로 잡고 노이즈를 생성할 것입니다
반응형
    
    
    
  
GenerateBiome 함수
    private Biome[,] GenerateBiome(Vector2[] points, Vector2[] biomePoints)
    {
        Biome[,] biomeArr = new Biome[mapSize, mapSize];
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                float minDist = float.MaxValue;
                int idx = -1;
                for (int i = 0; i < pointNum; i++)
                {
                    float dist = Vector2.Distance(points[i], new Vector2(x, y));
                    if (dist < minDist)
                    {
                        minDist = dist;
                        idx = i;
                    }
                }
                biomeArr[x, y] = (Biome)GetMinIdx(points[idx], biomePoints);
            }
        }
        return biomeArr;
    }
GenerateBiome 함수는 앞서 설명한 알고리즘 대로 Noise를 생성 후 가장 가까운 지형 포인트를 찾아 값을 설정합니다
Biome Enum, Start & GenerateRandomPos & GetMinIdx 함수
public enum Biome : int
{
    Plain = 0,
    Snow,
    MAX
}
    private async void Start()
    {
        seed = Random.Range(0, 10000f);
        var noiseArr = await Task.Run(GenerateNoise);
        var randomPoint = GenerateRandomPos(pointNum);
        var biomePoint = GenerateRandomPos((int)Biome.MAX);
        var biomeArr = await Task.Run(() => GenerateBiome(randomPoint, biomePoint));
        SettingTileMap(noiseArr, biomeArr);
    }
    private Vector2[] GenerateRandomPos(int num)
    {
        Vector2[] arr = new Vector2[num];
        for(int i = 0; i < num; i++)
        {
            int x = Random.Range(0, mapSize - 1);
            int y = Random.Range(0, mapSize - 1);
            arr[i] = new Vector2(x, y);
        }
        return arr;
    }
    private int GetMinIdx(Vector2 point, Vector2[] biomeArr)
    {
        int curIdx = 0;
        float min = float.MaxValue;
        for (int i = 0; i < biomeArr.Length; i++)
        {
            float value = Vector2.Distance(point, biomeArr[i]);
            if (min > value)
            {
                min = value;
                curIdx = i;
            }
        }
        return curIdx;
    }
GetMinIdx 함수에서는 가장 가까운 지형 포인트를 찾고
GenerateRandomPos 함수에서는 임의의 점을 선택하고 반환합니다
이제 만들어진 Noise를 활용해 지형을 그리면 이런 모양이 나옵니다

눈 지형과 평지 지형이 섞여서 잘 생성되는 것을 확인할 수 있습니다
이제 이 코드를 알맞게 수정하여 더 다양한 지형을 생성할 수 있을 것입니다
2D 랜덤 맵 생성을 이것으로 마치도록 하겠습니다
전체코드
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Tilemaps;
public enum Biome : int
{
    Plain = 0,
    Snow,
    MAX
}
public class RandomMapGenerater : MonoBehaviour
{
    [Header("타일맵 관련")]
    [SerializeField] private Tilemap tileMap;
    [Space]
    [Header("평지 지형")]
    [SerializeField] private TileBase water, sand, gress, forest;
    [Header("눈 지형")]
    [SerializeField] private TileBase s_water, s_sand, s_gress, s_forest;
    [Space]
    [Header("값 관련")]
    [SerializeField] private float mapScale = 0.01f;
    [SerializeField] private int mapSize;
    [SerializeField] private int octaves;
    [SerializeField] private int pointNum;
    private float seed;
    private async void Start()
    {
        seed = Random.Range(0, 10000f);
        var noiseArr = await Task.Run(GenerateNoise);
        var randomPoint = GenerateRandomPos(pointNum);
        var biomePoint = GenerateRandomPos((int)Biome.MAX);
        var biomeArr = await Task.Run(() => GenerateBiome(randomPoint, biomePoint));
        SettingTileMap(noiseArr, biomeArr);
    }
    private float[,] GenerateNoise()
    {
        float[,] noiseArr = new float[mapSize, mapSize];
        float min = float.MaxValue, max = float.MinValue;
        for(int x = 0; x < mapSize; x++)
        {
            for(int y = 0; y < mapSize; y++)
            {
                float lacunarity = 2.0f;
                float gain = 0.5f;
                float amplitude = 0.5f;
                float frequency = 1f;
                for (int i = 0; i < octaves; i++)
                {
                    noiseArr[x, y] += amplitude * (Mathf.PerlinNoise(
                        seed + (x * mapScale * frequency),
                        seed + (y * mapScale * frequency)) * 2 - 1);
                    frequency *= lacunarity;
                    amplitude *= gain;
                }
                if (noiseArr[x, y] < min)
                {
                    min = noiseArr[x, y];
                }
                else if (noiseArr[x, y] > max)
                {
                    max = noiseArr[x, y];
                }
            }
        }
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                noiseArr[x, y] = Mathf.InverseLerp(min, max, noiseArr[x, y]);
            }
        }
        return noiseArr;
    }
    private Biome[,] GenerateBiome(Vector2[] points, Vector2[] biomePoints)
    {
        Biome[,] biomeArr = new Biome[mapSize, mapSize];
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                float minDist = float.MaxValue;
                int idx = -1;
                for (int i = 0; i < pointNum; i++)
                {
                    float dist = Vector2.Distance(points[i], new Vector2(x, y));
                    if (dist < minDist)
                    {
                        minDist = dist;
                        idx = i;
                    }
                }
                biomeArr[x, y] = (Biome)GetMinIdx(points[idx], biomePoints);
            }
        }
        return biomeArr;
    }
    private Vector2[] GenerateRandomPos(int num)
    {
        Vector2[] arr = new Vector2[num];
        for(int i = 0; i < num; i++)
        {
            int x = Random.Range(0, mapSize - 1);
            int y = Random.Range(0, mapSize - 1);
            arr[i] = new Vector2(x, y);
        }
        return arr;
    }
    private int GetMinIdx(Vector2 point, Vector2[] biomeArr)
    {
        int curIdx = 0;
        float min = float.MaxValue;
        for (int i = 0; i < biomeArr.Length; i++)
        {
            float value = Vector2.Distance(point, biomeArr[i]);
            if (min > value)
            {
                min = value;
                curIdx = i;
            }
        }
        return curIdx;
    }
    private void SettingTileMap(float[,] noiseArr, Biome[,] biomeArr)
    {
        Vector3Int point = Vector3Int.zero;
        for (int x = 0; x < mapSize; x++)
        {
            for (int y = 0; y < mapSize; y++)
            {
                point.Set(x, y, 0);
                tileMap.SetTile(point, GetTileByHight(noiseArr[x, y], biomeArr[x, y]));
            }
        }
    }
    private TileBase GetTileByHight(float hight, Biome biome)
    {
        if(biome == Biome.Plain)
        {
            switch (hight)
            {
                case <= 0.35f: return water;
                case <= 0.45f: return sand;
                case <= 0.6f: return gress;
                default: return forest;
            }
        }
        else
        {
            switch (hight)
            {
                case <= 0.35f: return s_water;
                case <= 0.45f: return s_sand;
                case <= 0.6f: return s_gress;
                default: return s_forest;
            }
        }
    }
}반응형
    
    
    
  '유니티 > 2D 랜덤 맵' 카테고리의 다른 글
| [Unity] 2D 랜덤 맵 - 2. 더 자연스러운 지형 생성 (0) | 2023.10.27 | 
|---|---|
| [Unity] 2D 랜덤 맵 - 1. 기초 (0) | 2023.10.27 |