Generasi Dunia Prosedural dalam Persatuan

Pembuatan dunia di Unity mengacu pada proses pembuatan atau pembuatan dunia virtual, medan, lanskap, atau lingkungan virtual secara prosedural dalam mesin game Unity. Teknik ini biasa digunakan di berbagai jenis game, seperti game dunia terbuka, RPG, simulasi, dan lainnya, untuk menciptakan dunia game yang luas dan beragam secara dinamis.

Unity menyediakan kerangka kerja yang fleksibel dan beragam alat serta API untuk menerapkan teknik generasi dunia ini. Seseorang dapat menulis skrip khusus menggunakan C# untuk menghasilkan dan memanipulasi dunia game atau memanfaatkan Unity fitur bawaan seperti sistem Terrain, fungsi noise, dan antarmuka skrip untuk mencapai hasil yang diinginkan. Selain itu, ada juga aset pihak ketiga dan plugin yang tersedia di Unity Asset Store yang dapat membantu tugas pembuatan dunia.

Ada beberapa pendekatan untuk pembuatan dunia di Unity, dan pilihannya bergantung pada persyaratan spesifik game. Berikut adalah beberapa metode yang umum digunakan:

  • Pembuatan Medan Prosedural dengan Kebisingan Perlin
  • Automata Seluler
  • Diagram Voronoi
  • Penempatan Objek Prosedural

Pembuatan Medan Prosedural dengan Kebisingan Perlin

Pembuatan medan prosedural di Unity dapat dicapai dengan menggunakan berbagai algoritma dan teknik. Salah satu pendekatan yang populer adalah dengan menggunakan noise Perlin untuk menghasilkan peta ketinggian dan kemudian menerapkan berbagai teknik tekstur dan dedaunan untuk menciptakan medan yang realistis atau bergaya.

Kebisingan Perlin adalah jenis kebisingan gradien yang dikembangkan oleh Ken Perlin. Ini menghasilkan pola nilai yang mulus dan berkesinambungan yang tampak acak namun memiliki struktur yang koheren. Kebisingan Perlin banyak digunakan untuk menciptakan medan, awan, tekstur, dan bentuk organik lainnya yang tampak alami.

Di Unity, seseorang dapat menggunakan fungsi 'Mathf.PerlinNoise()' untuk menghasilkan noise Perlin. Dibutuhkan dua koordinat sebagai masukan dan mengembalikan nilai antara 0 dan 1. Dengan mengambil sampel kebisingan Perlin pada frekuensi dan amplitudo yang berbeda, dimungkinkan untuk membuat tingkat detail dan kompleksitas berbeda dalam konten prosedural.

Berikut ini contoh cara menerapkannya di Unity:

  • Di Editor Unity, buka "GameObject -> 3D Object -> Terrain". Ini akan membuat medan default di tempat kejadian.
  • Buat skrip C# baru bernama "TerrainGenerator" dan lampirkan ke objek medan. Berikut ini contoh skrip yang menghasilkan medan prosedural menggunakan noise Perlin:
using UnityEngine;

public class TerrainGenerator : MonoBehaviour
{
    public int width = 512;       // Width of the terrain
    public int height = 512;      // Height of the terrain
    public float scale = 10f;     // Scale of the terrain
    public float offsetX = 100f;  // X offset for noise
    public float offsetY = 100f;  // Y offset for noise
    public float noiseIntensity = 0.1f; //Intensity of the noise

    private void Start()
    {
        Terrain terrain = GetComponent<Terrain>();

        // Create a new instance of TerrainData
        TerrainData terrainData = new TerrainData();

        // Set the heightmap resolution and size of the TerrainData
        terrainData.heightmapResolution = width;
        terrainData.size = new Vector3(width, 600, height);

        // Generate the terrain heights
        float[,] heights = GenerateHeights();
        terrainData.SetHeights(0, 0, heights);

        // Assign the TerrainData to the Terrain component
        terrain.terrainData = terrainData;
    }

    private float[,] GenerateHeights()
    {
        float[,] heights = new float[width, height];

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                // Generate Perlin noise value for current position
                float xCoord = (float)x / width * scale + offsetX;
                float yCoord = (float)y / height * scale + offsetY;
                float noiseValue = Mathf.PerlinNoise(xCoord, yCoord);

                // Set terrain height based on noise value
                heights[x, y] = noiseValue * noiseIntensity;
            }
        }

        return heights;
    }
}
  • Lampirkan skrip "TerrainGenerator" ke objek Terrain di Editor Unity.
  • Di jendela Inspector untuk objek medan, sesuaikan lebar, tinggi, skala, offset, dan intensitas kebisingan untuk mengubah tampilan medan yang dihasilkan.
  • Tekan tombol Play di Editor Unity, dan medan prosedural kemudian akan dihasilkan berdasarkan algoritma noise Perlin.

Generasi Unity Terrain dengan noise Perlin.

Catatan: Skrip ini menghasilkan peta ketinggian medan dasar menggunakan noise Perlin. Untuk membuat medan yang lebih kompleks, modifikasi skrip untuk memasukkan algoritme kebisingan tambahan, terapkan teknik erosi atau penghalusan, tambahkan tekstur, atau tempatkan dedaunan dan objek berdasarkan fitur medan.

Automata Seluler

Automata seluler adalah model komputasi yang terdiri dari kisi-kisi sel, di mana setiap sel berevolusi berdasarkan seperangkat aturan yang telah ditentukan sebelumnya dan status sel tetangganya. Ini adalah konsep kuat yang digunakan dalam berbagai bidang, termasuk ilmu komputer, matematika, dan fisika. Automata seluler dapat menunjukkan pola perilaku kompleks yang muncul dari aturan sederhana, menjadikannya berguna untuk mensimulasikan fenomena alam dan menghasilkan konten prosedural.

Teori dasar di balik automata seluler melibatkan unsur-unsur berikut:

  1. Grid: Grid adalah kumpulan sel yang disusun dalam pola teratur, seperti kisi persegi atau heksagonal. Setiap sel dapat memiliki jumlah negara yang terbatas.
  2. Tetangga: Setiap sel memiliki sel tetangga, yang biasanya merupakan sel yang berdekatan. Lingkungan dapat didefinisikan berdasarkan pola konektivitas yang berbeda, seperti lingkungan von Neumann (atas, bawah, kiri, kanan) atau lingkungan Moore (termasuk diagonal).
  3. Aturan: Perilaku setiap sel ditentukan oleh seperangkat aturan yang menentukan bagaimana sel berkembang berdasarkan keadaan saat ini dan keadaan sel tetangganya. Aturan-aturan ini biasanya didefinisikan menggunakan pernyataan kondisional atau tabel pencarian.
  4. Pembaruan: Robot seluler berevolusi dengan memperbarui status setiap sel secara bersamaan sesuai aturan. Proses ini diulangi secara berulang-ulang, sehingga menghasilkan serangkaian generasi.

Automata seluler memiliki berbagai aplikasi di dunia nyata, termasuk:

  1. Simulasi Fenomena Alam: Automata seluler dapat mensimulasikan perilaku sistem fisik, seperti dinamika fluida, kebakaran hutan, arus lalu lintas, dan dinamika populasi. Dengan mendefinisikan aturan yang tepat, automata seluler dapat menangkap pola dan dinamika yang muncul dalam sistem dunia nyata.
  2. Pembuatan Konten Prosedural: Automata seluler dapat digunakan untuk menghasilkan konten prosedural dalam game dan simulasi. Misalnya, mereka dapat digunakan untuk membuat medan, sistem gua, sebaran vegetasi, dan struktur organik lainnya. Lingkungan yang kompleks dan realistis dapat dihasilkan dengan menetapkan aturan yang mengatur pertumbuhan dan interaksi sel.

Berikut ini contoh sederhana penerapan otomat seluler dasar di Unity untuk menyimulasikan permainan kehidupan:

using UnityEngine;

public class CellularAutomaton : MonoBehaviour
{
    public int width = 50;
    public int height = 50;
    public float cellSize = 1f;
    public float updateInterval = 0.1f;
    public Renderer cellPrefab;

    private bool[,] grid;
    private Renderer[,] cells;
    private float timer = 0f;
    private bool[,] newGrid;

    private void Start()
    {
        InitializeGrid();
        CreateCells();
    }

    private void Update()
    {
        timer += Time.deltaTime;

        if (timer >= updateInterval)
        {
            UpdateGrid();
            UpdateCells();
            timer = 0f;
        }
    }

    private void InitializeGrid()
    {
        grid = new bool[width, height];
        newGrid = new bool[width, height];

        // Initialize the grid randomly
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                grid[x, y] = Random.value < 0.5f;
            }
        }
    }

    private void CreateCells()
    {
        cells = new Renderer[width, height];

        // Create a GameObject for each cell in the grid
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                Vector3 position = new Vector3(x * cellSize, 0f, y * cellSize);
                Renderer cell = Instantiate(cellPrefab, position, Quaternion.identity);
                cell.material.color = Color.white;
                cells[x, y] = cell;
            }
        }
    }

    private void UpdateGrid()
    {
        // Apply the rules to update the grid
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                int aliveNeighbors = CountAliveNeighbors(x, y);

                if (grid[x, y])
                {
                    // Cell is alive
                    if (aliveNeighbors < 2 || aliveNeighbors > 3)
                        newGrid[x, y] = false; // Die due to underpopulation or overpopulation
                    else
                        newGrid[x, y] = true; // Survive
                }
                else
                {
                    // Cell is dead
                    if (aliveNeighbors == 3)
                        newGrid[x, y] = true; // Revive due to reproduction
                    else
                        newGrid[x, y] = false; // Remain dead
                }
            }
        }

        grid = newGrid;
    }

    private void UpdateCells()
    {
        // Update the visual representation of cells based on the grid
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                Renderer renderer = cells[x, y];
                renderer.sharedMaterial.color = grid[x, y] ? Color.black : Color.white;
            }
        }
    }

    private int CountAliveNeighbors(int x, int y)
    {
        int count = 0;

        for (int i = -1; i <= 1; i++)
        {
            for (int j = -1; j <= 1; j++)
            {
                if (i == 0 && j == 0)
                    continue;

                int neighborX = x + i;
                int neighborY = y + j;

                if (neighborX >= 0 && neighborX < width && neighborY >= 0 && neighborY < height)
                {
                    if (grid[neighborX, neighborY])
                        count++;
                }
            }
        }

        return count;
    }
}
  • Lampirkan skrip "CellularAutomaton" ke GameObject dalam adegan Unity dan tetapkan prefab sel ke bidang 'cellPrefab' di inspektur.

Otomat seluler di Unity.

Dalam contoh ini, kisi-kisi sel diwakili oleh array boolean, dengan 'true' menunjukkan sel hidup dan 'false' mewakili sel mati. Aturan permainan kehidupan diterapkan untuk memperbarui grid, dan representasi visual sel juga diperbarui. Metode 'CreateCells()' membuat GameObject untuk setiap sel, dan metode 'UpdateCells()' memperbarui warna setiap GameObject berdasarkan status grid.

Catatan: Ini hanyalah contoh dasar, dan ada banyak variasi dan perluasan pada automata seluler yang dapat dieksplorasi. Aturan, perilaku sel, dan konfigurasi grid dapat dimodifikasi untuk membuat simulasi berbeda dan menghasilkan berbagai pola dan perilaku.

Diagram Voronoi

Diagram Voronoi, juga dikenal sebagai tesselasi Voronoi atau partisi Voronoi, adalah struktur geometris yang membagi ruang menjadi beberapa wilayah berdasarkan kedekatannya dengan sekumpulan titik yang disebut benih atau situs. Setiap wilayah dalam diagram Voronoi terdiri dari semua titik dalam ruang yang lebih dekat ke benih tertentu dibandingkan benih lainnya.

Teori dasar di balik diagram Voronoi melibatkan unsur-unsur berikut:

  1. Benih/Situs: Benih atau situs adalah sekumpulan titik dalam ruang. Poin-poin ini dapat dihasilkan secara acak atau ditempatkan secara manual. Setiap benih mewakili titik pusat wilayah Voronoi.
  2. Sel/Wilayah Voronoi: Setiap sel atau wilayah Voronoi berhubungan dengan area ruang yang lebih dekat ke benih tertentu dibandingkan benih lainnya. Batas-batas wilayah dibentuk oleh garis-bagi tegak lurus dari ruas-ruas garis yang menghubungkan benih-benih yang berdekatan.
  3. Triangulasi Delaunay: Diagram Voronoi berkaitan erat dengan triangulasi Delaunay. Triangulasi Delaunay adalah triangulasi titik-titik benih sedemikian rupa sehingga tidak ada benih yang berada di dalam lingkaran luar segitiga mana pun. Triangulasi Delaunay dapat digunakan untuk membuat diagram Voronoi, dan sebaliknya.

Diagram Voronoi memiliki berbagai penerapan di dunia nyata, antara lain:

  1. Pembuatan Konten Prosedural: Diagram Voronoi dapat digunakan untuk menghasilkan medan prosedural, lanskap alam, dan bentuk organik. Dengan menggunakan benih sebagai titik kontrol dan menetapkan atribut (seperti ketinggian atau tipe bioma) ke sel Voronoi, lingkungan yang realistis dan bervariasi dapat diciptakan.
  2. Desain Game: Diagram Voronoi dapat digunakan dalam desain game untuk mempartisi ruang untuk tujuan gameplay. Misalnya, dalam permainan strategi, diagram Voronoi dapat digunakan untuk membagi peta permainan menjadi wilayah atau zona yang dikendalikan oleh faksi berbeda.
  3. Pencarian jalur dan AI: Diagram Voronoi dapat membantu pencarian jalur dan navigasi AI dengan memberikan representasi ruang yang memungkinkan penghitungan benih atau wilayah terdekat secara efisien. Mereka dapat digunakan untuk menentukan jaringan navigasi atau memengaruhi peta untuk agen AI.

Di Unity, ada beberapa cara untuk membuat dan memanfaatkan diagram Voronoi:

  1. Pembuatan Prosedural: Pengembang dapat mengimplementasikan algoritme untuk menghasilkan diagram Voronoi dari sekumpulan titik awal di Unity. Berbagai algoritma, seperti algoritma Fortune atau algoritma relaksasi Lloyd, dapat digunakan untuk membuat diagram Voronoi.
  2. Pembuatan Medan: Diagram Voronoi dapat digunakan dalam pembuatan medan untuk menciptakan lanskap yang beragam dan realistis. Setiap sel Voronoi dapat mewakili fitur medan yang berbeda, seperti pegunungan, lembah, atau dataran. Atribut seperti ketinggian, kelembapan, atau vegetasi dapat ditetapkan ke setiap sel, sehingga menghasilkan medan yang bervariasi dan menarik secara visual.
  3. Partisi Peta: Diagram Voronoi dapat digunakan untuk membagi peta game menjadi beberapa wilayah untuk tujuan gameplay. Dimungkinkan untuk menetapkan atribut atau properti berbeda ke setiap wilayah untuk membuat zona gameplay berbeda. Ini bisa berguna untuk permainan strategi, mekanisme kontrol teritorial, atau desain level.

Tersedia paket dan aset Unity yang menyediakan fungsionalitas diagram Voronoi, membuatnya lebih mudah untuk menggabungkan fitur berbasis Voronoi ke dalam proyek Unity. Paket ini sering kali menyertakan algoritme pembuatan diagram Voronoi, alat visualisasi, dan integrasi dengan sistem rendering Unity.

Berikut contoh pembuatan diagram Voronoi 2D di Unity menggunakan algoritma Fortune:

using UnityEngine;
using System.Collections.Generic;

public class VoronoiDiagram : MonoBehaviour
{
    public int numSeeds = 50;
    public int diagramSize = 50;
    public GameObject seedPrefab;

    private List<Vector2> seeds = new List<Vector2>();
    private List<List<Vector2>> voronoiCells = new List<List<Vector2>>();

    private void Start()
    {
        GenerateSeeds();
        GenerateVoronoiDiagram();
        VisualizeVoronoiDiagram();
    }

    private void GenerateSeeds()
    {
        // Generate random seeds within the diagram size
        for (int i = 0; i < numSeeds; i++)
        {
            float x = Random.Range(0, diagramSize);
            float y = Random.Range(0, diagramSize);
            seeds.Add(new Vector2(x, y));
        }
    }

    private void GenerateVoronoiDiagram()
    {
        // Compute the Voronoi cells based on the seeds
        for (int i = 0; i < seeds.Count; i++)
        {
            List<Vector2> cell = new List<Vector2>();
            voronoiCells.Add(cell);
        }

        for (int x = 0; x < diagramSize; x++)
        {
            for (int y = 0; y < diagramSize; y++)
            {
                Vector2 point = new Vector2(x, y);
                int closestSeedIndex = FindClosestSeedIndex(point);
                voronoiCells[closestSeedIndex].Add(point);
            }
        }
    }

    private int FindClosestSeedIndex(Vector2 point)
    {
        int closestIndex = 0;
        float closestDistance = Vector2.Distance(point, seeds[0]);

        for (int i = 1; i < seeds.Count; i++)
        {
            float distance = Vector2.Distance(point, seeds[i]);
            if (distance < closestDistance)
            {
                closestDistance = distance;
                closestIndex = i;
            }
        }

        return closestIndex;
    }

    private void VisualizeVoronoiDiagram()
    {
        // Visualize the Voronoi cells by instantiating a sphere for each cell point
        for (int i = 0; i < voronoiCells.Count; i++)
        {
            List<Vector2> cell = voronoiCells[i];
            Color color = Random.ColorHSV();

            foreach (Vector2 point in cell)
            {
                Vector3 position = new Vector3(point.x, 0, point.y);
                GameObject sphere = Instantiate(seedPrefab, position, Quaternion.identity);
                sphere.GetComponent<Renderer>().material.color = color;
            }
        }
    }
}
  • Untuk menggunakan kode ini, buatlah cetakan bola dan tetapkan ke bidang seedPrefab di inspektur Unity. Sesuaikan variabel numSeeds dan diagramSize untuk mengontrol jumlah seed dan ukuran diagram.

Diagram Voronoi di Unity.

Dalam contoh ini, skrip VoronoiDiagram menghasilkan diagram Voronoi dengan menempatkan titik benih secara acak dalam ukuran diagram yang ditentukan. Metode 'GenerateVoronoiDiagram()' menghitung sel Voronoi berdasarkan titik benih, dan metode 'VisualizeVoronoiDiagram()' membuat instance GameObject bola di setiap titik sel Voronoi, memvisualisasikan diagram.

Catatan: Contoh ini memberikan visualisasi dasar diagram Voronoi, namun dimungkinkan untuk memperluasnya lebih jauh dengan menambahkan fitur tambahan, seperti menghubungkan titik sel dengan garis atau menetapkan atribut berbeda ke setiap sel untuk pembuatan medan atau tujuan gameplay.

Secara keseluruhan, diagram Voronoi menawarkan alat serbaguna dan kuat untuk menghasilkan konten prosedural, mempartisi ruang, dan menciptakan lingkungan yang menarik dan bervariasi di Unity.

Penempatan Objek Prosedural

Penempatan objek prosedural di Unity melibatkan pembuatan dan penempatan objek dalam adegan secara algoritmik, bukan memposisikannya secara manual. Ini adalah teknik ampuh yang digunakan untuk berbagai tujuan, seperti mengisi lingkungan dengan pepohonan, batu, bangunan, atau objek lain dengan cara yang alami dan dinamis.

Berikut contoh penempatan objek prosedural di Unity:

using UnityEngine;

public class ObjectPlacement : MonoBehaviour
{
    public GameObject objectPrefab;
    public int numObjects = 50;
    public Vector3 spawnArea = new Vector3(10f, 0f, 10f);

    private void Start()
    {
        PlaceObjects();
    }

    private void PlaceObjects()
    {
        for (int i = 0; i < numObjects; i++)
        {
            Vector3 spawnPosition = GetRandomSpawnPosition();
            Quaternion spawnRotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
            Instantiate(objectPrefab, spawnPosition, spawnRotation);
        }
    }

    private Vector3 GetRandomSpawnPosition()
    {
        Vector3 center = transform.position;
        Vector3 randomPoint = center + new Vector3(
            Random.Range(-spawnArea.x / 2, spawnArea.x / 2),
            0f,
            Random.Range(-spawnArea.z / 2, spawnArea.z / 2)
        );
        return randomPoint;
    }
}
  • Untuk menggunakan skrip ini, buat GameObject kosong di adegan Unity dan lampirkan skrip "ObjectPlacement" ke dalamnya. Tetapkan prefab objek dan sesuaikan parameter 'numObjects' dan 'spawnArea' di inspektur agar sesuai dengan persyaratan. Saat menjalankan adegan, objek akan ditempatkan secara prosedural dalam area spawn yang ditentukan.

Penempatan objek prosedural di Unity.

Dalam contoh ini, skrip 'ObjectPlacement' bertanggung jawab untuk menempatkan objek secara prosedural dalam adegan. Bidang 'objectPrefab' harus ditetapkan dengan cetakan objek yang akan ditempatkan. Variabel 'numObjects' menentukan jumlah objek yang akan ditempatkan, dan variabel 'spawnArea' menentukan area di mana objek akan ditempatkan secara acak.

Metode 'PlaceObjects()' mengulang jumlah objek yang diinginkan dan menghasilkan posisi pemijahan acak dalam area pemijahan yang ditentukan. Ini kemudian membuat instance cetakan objek di setiap posisi acak dengan rotasi acak.

Catatan: Kode ini dapat ditingkatkan lebih lanjut dengan menggabungkan berbagai algoritme penempatan, seperti penempatan berbasis grid, penempatan berbasis kepadatan, atau penempatan berbasis aturan, bergantung pada kebutuhan spesifik proyek.

Kesimpulan

Teknik pembuatan prosedural di Unity menyediakan alat canggih untuk menciptakan pengalaman yang dinamis dan mendalam. Baik itu menghasilkan medan menggunakan kebisingan Perlin atau algoritma fraktal, menciptakan lingkungan yang beragam dengan diagram Voronoi, mensimulasikan perilaku kompleks dengan automata seluler, atau mengisi adegan dengan objek yang ditempatkan secara prosedural, teknik ini menawarkan fleksibilitas, efisiensi, dan kemungkinan tak terbatas untuk pembuatan konten. Dengan memanfaatkan algoritme ini dan mengintegrasikannya ke dalam proyek Unity, pengembang dapat mencapai pembuatan medan yang realistis, simulasi yang nyata, lingkungan yang menarik secara visual, dan mekanisme permainan yang menarik. Pembuatan prosedural tidak hanya menghemat waktu dan tenaga tetapi juga memungkinkan terciptanya pengalaman unik dan selalu berubah yang memikat pemain dan menghidupkan dunia virtual.

Artikel yang Disarankan
Pentingnya Bercerita dalam Pengembangan Game Unity
Aset Tujuan Umum yang Harus Dimiliki untuk Persatuan
API Skrip Persatuan dan Unity Pro
Cara Membuat Game Terinspirasi FNAF di Unity
Membandingkan Lingkungan Pengembangan 2D dan 3D di Unity
Tutorial Gamepad untuk Persatuan
Menerapkan Konsep Pemrograman Berorientasi Objek (OOP) dalam Unity