Generarea de apă interesantă folosind zgomotul perlin (Dezvoltarea jocurilor, Unitate, Zgomot Perlin)

Majs a intrebat.

Eu folosesc Mathf.PerlinNoise pentru a genera iarbă/apă într-un pătrat 2d. Funcționează așa cum este prevăzut, însă nu obțin o apă foarte interesantă și, uneori, poate arăta foarte goală sau nerealistă; iată o mostră de generare proastă:

Din păcate, mi-a luat aproximativ 20 de încercări pentru a găsi una interesantă, ceea ce vreau este ceva de genul acesta:

Sau ceva de genul acesta, dar nu vreau zone mici de apă:

Poți să copiezi/lipesti componentele de mai jos și să încerci singur cu cuburi de 1×1 (dacă nu ai Odin, șterge doar partea respectivă):

using UnityEngine;
using Sirenix.OdinInspector;

public class GenerateMap : MonoBehaviour
{
    [Range(0, 40)]
    public int mapWidth = 20;
    [Range(0, 40)]
    public int mapHeight = 20;
    [Range(0, 20)]
    public float noiseScale;

    [Space]
    public GameObject parent;
    public GameObject grassTile, waterTile;

    [Button("Generate map")]
    public void GenerateMap()
    {
        for (int i = 0; i < parent.transform.childCount; i++)
        {
            Destroy(parent.transform.GetChild(i).gameObject);
        }

        float[,] noiseMap = Noise.GenerateNoiseMap(mapWidth, mapHeight, noiseScale);

        for (int x = 0; x < mapWidth; x++)
        {
            for (int y = 0; y < mapHeight; y++)
            {
                Debug.Log(noiseMap[x, y]);

                if (noiseMap[x, y] >= .3)
                    Instantiate(grassTile, new Vector3(x, 0, y), Quaternion.identity, parent.transform);
                else
                    Instantiate(waterTile, new Vector3(x, 0, y), Quaternion.identity, parent.transform);
            }
        }
    }
}

Zgomot:

using UnityEngine;

public static class Noise
{

    public static float[,] GenerateNoiseMap(int mapWidth, int mapHeight, float scale)
    {
        float[,] noiseMap = new float[mapWidth, mapHeight];

        Vector2 offset = new Vector2(Random.Range(0, 100), Random.Range(0, 100));

        if (scale <= 0)
            scale = .0001f;

        for (int y = 0; y < mapHeight; y++)
        {
            for (int x = 0; x < mapWidth; x++)
            {
                float sampleX = x / scale + offset.x;
                float sampleY = y / scale + offset.y;

                float perlinValue = Mathf.PerlinNoise(sampleX, sampleY);
                noiseMap[x, y] = perlinValue;
            }
        }

        return noiseMap;
    }
}

Comentarii

  • Ne puteți ajuta să înțelegem ce criterii folosiți pentru a evalua cât de „interesant” este un anumit model de apă? Înțelegem că vrei să eviți zonele de apă foarte mici, dar nu este clar ce alte criterii sunt în joc.  > Por DMGregory.
  • @DMGregory Am încercat să postez 2 imagini cu ceea ce am vrut să spun. Dar, în realitate, tot ce vreau să spun este 1-3 formațiuni de apă de dimensiuni medii-mari. uneori conectate, alteori nu. În momentul de față mă trezesc cu apă doar pe margini, sau cu bălți mici. Are sens?  > Por Majs.
  • Ai încercat să reduci dimensiunea texturii perlin noise și să folosești o suprafață mai mare din ea?-  > Por Jay.
  • @Jake Am încercat, dar nu există nicio combinație a acestei soluții actuale care să garanteze tipul de corpuri de apă pe care îl doresc, adică nu bălți la întâmplare, nu doar o linie de-a lungul marginii, etc. –  > Por Majs.
  • @Jake da, m-am gândit și eu la asta, s-ar putea ca în cele din urmă să fac asta,.-.  > Por Majs.
1 răspunsuri
KdotJPG

Încearcă să cobori scara și să adaugi mai multe octave.

De asemenea, nu aș sugera Mathf.Perlin, deoarece generează zgomotul său omonim Old Perlin, care are o aliniere pătrată vizibilă. În schimb, aș importa Unity.Mathematics și aș folosi Unity.Mathematics.noise.snoise(float2) pentru o bună implementare a zgomotului 2D Simplex.

Luați orice tutorial sau proiect, care utilizează zgomotul „Perlin”, cu un grăunte de sare. De cele mai multe ori Perlin este folosit, sunt pentru că o bibliotecă a inclus funcția sau pentru că autorul a auzit primul de numele său iconic-sonorizant. Rareori Perlin este ales pentru că a fost gândit cu atenție pentru a fi cel mai bun algoritm pentru acel scop.

Ați putea face următoarele:

  • Utilizați un număr mai mare pentru scale
  • float2 pos = new float2(sampleX, sampleY);
  • float noiseValue = noise.snoise(pos) + 0.5f*noise.snoise(pos * 2f + new float2(35f, 66f) + 0.25f*noise.snoise(pos * 4f * 2f + new float2(75f, 104f); unde frecvența se dublează, dar amplitudinea se înjumătățește de fiecare dată. Decalajul are rolul de a împiedica suprapunerea acelorași caracteristici la originea zgomotului. De asemenea, puteți experimenta cu valori ale frecvenței și amplitudinii care nu sunt exact dublul / jumătatea celei anterioare.

EDIT: Reluând partea din postarea dvs. care discută despre ceea ce căutați, poate că ceea ce doriți să faceți este să alegeți 1-3 puncte aleatorii din harta dvs. care desemnează corpuri de apă, să alegeți o rază, să calculați distanța euclidiană la pătrat de la fiecare (plus raza) și să adăugați un zgomot simplex pe acesta pentru a-i da niște limite interesante. Asigurați-vă că frecvența este suficient de joasă și amplitudinea suficient de mare pentru a-l abate suficient de mult de la circular. În cazul lacurilor care se suprapun, le puteți face să se unească fără probleme dacă folosiți o formulă de genul float lake1 = (1f - (dx*dx + dy*dy) / (radius + smallPadding)); lake1 *= lake1; unde dx = (input point).x - (lake center).x și la fel pentru dy, apoi făcând float totalLake = lake1 + lake2 + lake3 + snoise și block = totalLake > smallThreshold ? water : grass