유니티/2D 랜덤 맵

[Unity] 2D 랜덤 맵 - 2. 더 자연스러운 지형 생성

이름?없음 2023. 10. 27. 23:51
반응형

이전 글에서 Perline noise만 이용하여 맵을 생성했을 때 지형이 자연스럽지 않은 현상이 있었습니다


지형을 더 자연스럽게 생성하기 위해서는 Perline noise가 아닌 Fractal noise를 사용하여야 합니다

Fractal noise란? : 저주파 Perline noise와 고주파 Perline noise를 중첩하여 얻어낸 디테일한 노이즈

프랙탈 노이즈(출처 : Alex Ayars) https://www.flickr.com/photos/aayars/4072756284

반응형



이제 이 Fractal Noise를 이용하여 지형을 생성하여 봅시다

 

GenerateNoise 함수

    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); // -1 ~ 1

                    frequency *= lacunarity;
                    amplitude *= gain;

                }

                if (noiseArr[x, y] < min)
                {

                    min = noiseArr[x, y];

                }
                else if (noiseArr[x, y] > max)
                {

                    max = noiseArr[x, y];

                }


            }

        }


        //중첩된 노이즈값을 0~1로 변환
        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;

    }


InverseLerp 함수는 a, b 사이의 value가 있는 위치를 반환합니다
참고 : https://docs.unity3d.com/ScriptReference/Mathf.InverseLerp.html


실행해 보면 이전보다 더 자연스러운 지형이 생성된 것을 볼 수 있습니다

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Tilemaps;

public class RandomMapGenerater : MonoBehaviour
{

    [Header("타일맵 관련")]
    [SerializeField] private Tilemap tileMap;
    [SerializeField] private TileBase water, sand, gress, forest;
    [Space]
    [Header("값 관련")]
    [SerializeField] private float mapScale = 0.01f;
    [SerializeField] private int mapSize;
    [SerializeField] private int octaves; //노이즈 중첩 횟수

    private float seed;

    private async void Start()
    {

        seed = Random.Range(0, 10000f);

        var noiseArr = await Task.Run(GenerateNoise);

        SettingTileMap(noiseArr);

    }

    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); // -1 ~ 1

                    frequency *= lacunarity;
                    amplitude *= gain;

                }

                if (noiseArr[x, y] < min)
                {

                    min = noiseArr[x, y];

                }
                else if (noiseArr[x, y] > max)
                {

                    max = noiseArr[x, y];

                }


            }

        }


        //중첩된 노이즈값을 0~1로 변환
        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 void SettingTileMap(float[,] noiseArr)
    {

        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]));

            }

        }

    }

    private TileBase GetTileByHight(float hight)
    {

        switch (hight)
        {

            case <= 0.35f: return water;
            case <= 0.45f: return sand;
            case <= 0.6f: return gress;
            default: return forest;

        }

    }

}


이 다음 글에서는 바이옴을 표현하여 보겠습니다

반응형

'유니티 > 2D 랜덤 맵' 카테고리의 다른 글

[Unity] 2D 랜덤 맵 - 3. 바이옴  (0) 2023.10.29
[Unity] 2D 랜덤 맵 - 1. 기초  (0) 2023.10.27