Dari penerjemah: artikel ini adalah yang pertama dari serangkaian tutorial terperinci (27 bagian) tentang cara membuat peta dari segi enam. Inilah yang harus terjadi di akhir tutorial.Bagian 1-3: jala, warna, dan tinggi selBagian 4-7: gundukan, sungai, dan jalanBagian 8-11: air, bentang alam, dan bentengBagian 12-15: menyimpan dan memuat, tekstur, jarakBagian 16-19: menemukan jalan, regu pemain, animasiBagian 20-23: Kabut Perang, Penelitian Peta, Generasi ProseduralBagian 24-27: siklus air, erosi, bioma, peta silindrisBagian 1: menyambung dari segi enam
Daftar isi
- Ubah kuadrat menjadi segi enam.
- Triangulasi kisi-kisi segi enam.
- Kami bekerja dengan koordinat kubik.
- Kami berinteraksi dengan sel kisi.
- Buat editor dalam game.
Tutorial ini adalah awal dari seri tentang kartu segi enam. Jaring segi enam digunakan dalam banyak permainan, terutama dalam strategi, termasuk Age of Wonders 3, Civilization 5, dan Endless Legend. Kami akan mulai dengan dasar-dasarnya, kami akan secara bertahap menambahkan fitur baru dan sebagai hasilnya kami akan membuat relief yang kompleks berdasarkan segi enam.
Tutorial ini mengasumsikan bahwa Anda telah mempelajari seri
Mesh Basics , yang dimulai dengan
Grid Prosedur . Itu dibuat di Unity 5.3.1. Seri ini menggunakan beberapa versi Unity. Bagian terakhir dibuat pada Unity 2017.3.0p3.
Peta sederhana segi enam.Tentang segi enam
Mengapa hexagon dibutuhkan? Jika kami membutuhkan kotak, maka logis untuk menggunakan kotak. Kotak sangat mudah untuk menggambar dan memposisikan, tetapi mereka juga memiliki kelemahan. Lihatlah satu bujur sangkar dari kisi-kisi, lalu tetangga-tetangganya.
Alun-alun dan tetangganya.Secara total, alun-alun ini memiliki delapan tetangga. Empat di antaranya bisa dicapai dengan melintasi tepi alun-alun. Ini adalah tetangga horisontal dan vertikal. Empat lainnya dapat dicapai dengan melintasi sudut alun-alun. Ini adalah tetangga diagonal.
Berapa jarak antara pusat sel kotak persegi yang berdekatan? Jika panjang tepi adalah 1, maka untuk tetangga horizontal dan vertikal jawabannya adalah 1. Tetapi untuk tetangga diagonal jawabannya adalah โ2.
Perbedaan antara kedua jenis tetangga menyebabkan kesulitan. Jika kita menggunakan gerakan diskrit, lalu bagaimana memahami gerakan diagonal? Haruskah saya mengizinkannya sama sekali? Bagaimana membuat penampilan lebih organik? Game yang berbeda menggunakan pendekatan yang berbeda dengan kelebihan dan kekurangannya. Salah satu pendekatan adalah tidak menggunakan kotak sama sekali, tetapi menggunakan hexagon sebagai gantinya.
Hexagon dan tetangganya.Tidak seperti persegi, segi enam tidak memiliki delapan, tetapi enam tetangga. Semua tetangga berdekatan dengan tepi, tidak ada tetangga sudut. Artinya, hanya ada satu jenis tetangga, yang banyak disederhanakan. Tentu saja, kisi segi enam lebih sulit dibangun daripada persegi, tetapi kita bisa mengatasinya.
Sebelum kita mulai, kita perlu menentukan ukuran segi enam. Biarkan panjang tepi sama dengan 10 unit. Karena segi enam terdiri dari lingkaran enam segitiga sama sisi, jarak dari pusat ke sudut juga 10. Nilai ini menentukan jari-jari luar sel heksagonal.
Jari-jari luar dan dalam hexagon.Ada juga jari-jari dalam, yang merupakan jarak dari pusat ke masing-masing tepi. Parameter ini penting karena jarak antara pusat-pusat tetangga sama dengan nilai ini dua kali. Jari-jari bagian dalam adalah
f r a c s q r t 3 2 dari radius luar, yaitu, dalam kasus kami
5 s q r t 3 . Mari kita letakkan parameter ini di kelas statis untuk kenyamanan.
using UnityEngine; public static class HexMetrics { public const float outerRadius = 10f; public const float innerRadius = outerRadius * 0.866025404f; }
Bagaimana cara mendapatkan nilai jari-jari internal?Ambil satu dari enam segitiga segi enam. Jari-jari bagian dalam sama dengan ketinggian segitiga ini. Ketinggian ini dapat diperoleh dengan membagi segitiga menjadi dua segitiga reguler, dan kemudian menggunakan teorema Pythagoras.
Karena itu, untuk panjang iga e radius dalam adalah s q r t e 2 - ( e / 2 ) 2 = s q r t 3 e 2 / 4 = e s q r t 3 / 2 s e k i t a r 0886 e .
Jika kita sudah melakukan ini, maka mari kita tentukan posisi enam sudut relatif terhadap pusat sel. Perlu dicatat bahwa ada dua cara untuk mengarahkan segi enam: ke atas dengan sisi yang tajam atau rata. Kami akan memasang sudut. Mari kita mulai dari sudut ini dan tambahkan sisanya searah jarum jam. Tempatkan mereka di bidang XZ sehingga segi enam berada di tanah.
Orientasi yang mungkin. public static Vector3[] corners = { new Vector3(0f, 0f, outerRadius), new Vector3(innerRadius, 0f, 0.5f * outerRadius), new Vector3(innerRadius, 0f, -0.5f * outerRadius), new Vector3(0f, 0f, -outerRadius), new Vector3(-innerRadius, 0f, -0.5f * outerRadius), new Vector3(-innerRadius, 0f, 0.5f * outerRadius) };
paket unityMeshing
Untuk membangun kisi heksagon, kita membutuhkan sel kisi. Untuk tujuan ini, buat komponen
HexCell
. Untuk saat ini, biarkan kosong karena kami belum menggunakan sel apa pun.
using UnityEngine; public class HexCell : MonoBehaviour { }
Untuk memulai dengan yang paling sederhana, buat objek bidang default, tambahkan komponen sel ke dalamnya, dan ubah semuanya menjadi cetakan.
Menggunakan bidang sebagai cetakan sel heksagonal.Sekarang mari masuk ke internet. Mari kita membuat komponen sederhana dengan variabel umum lebar sel, tinggi dan cetakan. Kemudian tambahkan objek game dengan komponen ini ke TKP.
using UnityEngine; public class HexGrid : MonoBehaviour { public int width = 6; public int height = 6; public HexCell cellPrefab; }
Objek mesh segi enam.Mari kita mulai dengan membuat kotak kotak biasa, karena kita sudah tahu bagaimana melakukan ini. Mari kita simpan sel dalam array untuk dapat mengaksesnya.
Karena pesawat secara default memiliki ukuran 10 kali 10 unit, kami akan menggeser setiap sel dengan jumlah ini.
HexCell[] cells; void Awake () { cells = new HexCell[height * width]; for (int z = 0, i = 0; z < height; z++) { for (int x = 0; x < width; x++) { CreateCell(x, z, i++); } } } void CreateCell (int x, int z, int i) { Vector3 position; position.x = x * 10f; position.y = 0f; position.z = z * 10f; HexCell cell = cells[i] = Instantiate<HexCell>(cellPrefab); cell.transform.SetParent(transform, false); cell.transform.localPosition = position; }
Kotak persegi pesawat.Jadi kami punya kotak solid yang indah dari sel-sel persegi. Tapi sel mana? Tentu saja, ini mudah diverifikasi, tetapi ada kesulitan dengan segi enam. Akan lebih mudah jika kita bisa secara bersamaan melihat koordinat semua sel.
Tampilan koordinat
Tambahkan kanvas ke adegan dengan memilih
GameObject / UI / Canvas dan menjadikannya anak dari objek mesh kami. Karena kanvas ini hanya untuk informasi, kami akan menghapus komponen raycaster-nya. Anda juga dapat menghapus objek sistem acara, yang secara otomatis ditambahkan ke tempat kejadian, karena untuk saat ini kami tidak memerlukannya.
Atur
Render Mode ke
World Space dan putar 90 derajat di sepanjang sumbu X sehingga kanvas menutupi grid. Atur pivot dan posisi ke nol. Berikan sedikit offset vertikal sehingga isinya berada di atas. Lebar dan tinggi tidak penting bagi kami, karena kami mengatur isinya sendiri. Kita dapat mengatur nilai ke 0 untuk menyingkirkan persegi panjang besar di jendela adegan.
Sebagai sentuhan akhir, tambah
Dynamic Pixels Per Unit menjadi 10. Jadi kami menjamin bahwa objek teks akan menggunakan resolusi tekstur yang memadai.
Kanvas untuk koordinat kotak segi enam.Untuk menampilkan koordinat, buat objek Teks (
GameObject / UI / Teks ) dan ubah menjadi prefab. Pusatkan jangkar dan pivotnya, atur ukurannya menjadi 5 kali 15. Teks juga harus sejajar secara horizontal dan vertikal di tengah. Setel ukuran font menjadi 4. Akhirnya, kami tidak ingin menggunakan teks default dan kami tidak akan menggunakan
Teks Kaya . Juga, tidak masalah bagi kita apakah
Target Raycast dihidupkan, karena untuk kanvas kita itu masih tidak diperlukan.
Label sel cetakan.Sekarang kita perlu memberi tahu grid tentang kanvas dan cetakan. Tambahkan ke awal skripnya
using UnityEngine.UI;
untuk dengan mudah mengakses jenis
UnityEngine.UI.Text
. Label cetakan membutuhkan variabel yang dibagi, dan kanvas dapat ditemukan dengan memanggil
GetComponentInChildren
.
public Text cellLabelPrefab; Canvas gridCanvas; void Awake () { gridCanvas = GetComponentInChildren<Canvas>(); โฆ }
Tag prefab koneksi.Setelah menghubungkan prefab label, kita dapat membuat instance-nya dan menampilkan koordinat sel. Antara X dan Z, masukkan karakter baris baru sehingga muncul pada baris terpisah.
void CreateCell (int x, int z, int i) { โฆ Text label = Instantiate<Text>(cellLabelPrefab); label.rectTransform.SetParent(gridCanvas.transform, false); label.rectTransform.anchoredPosition = new Vector2(position.x, position.z); label.text = x.ToString() + "\n" + z.ToString(); }
Tampilan koordinat.Posisi Segi Enam
Sekarang kita dapat secara visual mengenali setiap sel, mari kita mulai memindahkannya. Kita tahu bahwa jarak antara sel-sel heksagonal yang berdekatan dalam arah X sama dengan dua kali jari-jari dalam. Kami akan menggunakannya. Selain itu, jarak ke baris sel berikutnya harus 1,5 kali lebih besar dari jari-jari luar.
Geometri heksagon tetangga. position.x = x * (HexMetrics.innerRadius * 2f); position.y = 0f; position.z = z * (HexMetrics.outerRadius * 1.5f);
Kami menerapkan jarak antara segi enam tanpa offset.Tentu saja, baris ordinal heksagon tidak terletak tepat satu di atas yang lain. Setiap baris diimbangi sepanjang sumbu X dengan nilai jari-jari dalam. Nilai ini dapat diperoleh dengan menambahkan setengah Z ke X, dan kemudian kalikan dengan dua kali jari-jari dalam.
position.x = (x + z * 0.5f) * (HexMetrics.innerRadius * 2f);
Penempatan hexagon yang tepat menciptakan kisi berbentuk berlian.Meskipun ini adalah bagaimana kami menempatkan sel-sel di posisi yang benar dari segi enam, kotak kami sekarang mengisi belah ketupat daripada persegi panjang. Kami jauh lebih nyaman bekerja dengan kisi-kisi persegi panjang, jadi mari kita sel kembali beroperasi. Ini dapat dilakukan dengan memindahkan kembali bagian offset. Di setiap baris kedua, semua sel harus digeser mundur satu langkah tambahan. Untuk melakukan ini, kita perlu mengurangi hasil pembagian integer Z dengan 2 sebelum mengalikan.
position.x = (x + z * 0.5f - z / 2) * (HexMetrics.innerRadius * 2f);
Lokasi segi enam di area persegi panjang.paket unityHexagon Rendering
Setelah menempatkan sel dengan benar, kita dapat melanjutkan untuk menampilkan segi enam yang sebenarnya. Pertama kita perlu menyingkirkan pesawat, jadi kami akan menghapus semua komponen kecuali
HexCell
dari
HexCell
sel.
Tidak ada lagi pesawat.Seperti dalam tutorial
Dasar-Dasar Mesh , kami menggunakan satu jala untuk merender seluruh jala. Namun, kali ini kami tidak akan menetapkan jumlah simpul dan segitiga yang diperlukan. Sebaliknya, kami akan menggunakan daftar.
Buat komponen
HexMesh
baru yang menangani mesh kami. Ini akan membutuhkan filter mesh dan renderer, ia memiliki mesh dan daftar untuk simpul dan segitiga.
using UnityEngine; using System.Collections.Generic; [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class HexMesh : MonoBehaviour { Mesh hexMesh; List<Vector3> vertices; List<int> triangles; void Awake () { GetComponent<MeshFilter>().mesh = hexMesh = new Mesh(); hexMesh.name = "Hex Mesh"; vertices = new List<Vector3>(); triangles = new List<int>(); } }
Buat objek anak baru untuk jala ini dengan komponen ini. Dia akan secara otomatis menerima penyaji mesh, tetapi tidak ada bahan yang akan diberikan kepadanya. Karena itu, tambahkan materi default ke dalamnya.
Objek hex mesh.HexGrid
sekarang
HexGrid
dapat mengambil mesh segi enam dengan cara yang sama seperti yang ia temukan kanvas.
HexMesh hexMesh; void Awake () { gridCanvas = GetComponentInChildren<Canvas>(); hexMesh = GetComponentInChildren<HexMesh>(); โฆ }
Setelah mesh bangun, ia harus memerintahkan mesh untuk melakukan pelacakan sel-selnya. Kita harus yakin bahwa ini akan terjadi setelah komponen Sedarlah dari mesh heksa. Karena
Start
dipanggil nanti, masukkan kode yang sesuai di sana.
void Start () { hexMesh.Triangulate(cells); }
Metode
HexMesh.Triangulate
ini dapat dipanggil kapan saja, bahkan jika sel sudah triangulasi sebelumnya. Karena itu, kita harus mulai dengan membersihkan data lama. Saat perulangan melalui semua sel, kami melakukan triangulasi secara individual. Setelah menyelesaikan operasi ini, kami menetapkan simpul dan segitiga yang dihasilkan ke mesh, dan menyelesaikannya dengan menghitung ulang normal mesh.
public void Triangulate (HexCell[] cells) { hexMesh.Clear(); vertices.Clear(); triangles.Clear(); for (int i = 0; i < cells.Length; i++) { Triangulate(cells[i]); } hexMesh.vertices = vertices.ToArray(); hexMesh.triangles = triangles.ToArray(); hexMesh.RecalculateNormals(); } void Triangulate (HexCell cell) { }
Karena segi enam terdiri dari segitiga, mari kita buat metode yang nyaman untuk menambahkan segitiga berdasarkan posisi tiga simpul. Itu hanya akan menambahkan simpul dalam urutan. Dia juga menambahkan indeks dari simpul-simpul ini untuk membentuk segitiga. Indeks verteks pertama sama dengan panjang daftar simpul sebelum menambahkan simpul baru ke dalamnya. Jangan lupa tentang ini saat menambahkan simpul.
void AddTriangle (Vector3 v1, Vector3 v2, Vector3 v3) { int vertexIndex = vertices.Count; vertices.Add(v1); vertices.Add(v2); vertices.Add(v3); triangles.Add(vertexIndex); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 2); }
Sekarang kita bisa melakukan triangulasi sel kita. Mari kita mulai dengan segitiga pertama. Puncak pertamanya adalah di tengah segi enam. Dua simpul lainnya adalah sudut pertama dan kedua relatif terhadap pusat.
void Triangulate (HexCell cell) { Vector3 center = cell.transform.localPosition; AddTriangle( center, center + HexMetrics.corners[0], center + HexMetrics.corners[1] ); }
Segitiga pertama dari setiap sel.Ini berhasil, jadi mari kita memutar semua enam segitiga.
Vector3 center = cell.transform.localPosition; for (int i = 0; i < 6; i++) { AddTriangle( center, center + HexMetrics.corners[i], center + HexMetrics.corners[i + 1] ); }
Bisakah puncak dibagi?Ya kamu bisa. Bahkan, kita dapat melakukan lebih baik dan menggunakan hanya empat bukannya enam segitiga untuk rendering. Tetapi meninggalkan ini, kami akan menyederhanakan pekerjaan kami, dan itu akan benar, karena dalam tutorial berikut semuanya menjadi lebih rumit. Mengoptimalkan simpul dan segitiga pada tahap ini akan menghambat kita di masa depan.
Sayangnya, proses ini akan menghasilkan
IndexOutOfRangeException
. Ini karena segitiga terakhir sedang mencoba untuk mendapatkan sudut ketujuh, yang tidak ada. Tentu saja, ia harus kembali dan menggunakan sebagai simpul terakhir dari sudut pertama. Atau kita bisa menduplikasi sudut pertama di
HexMetrics.corners
agar tidak melampaui batas.
public static Vector3[] corners = { new Vector3(0f, 0f, outerRadius), new Vector3(innerRadius, 0f, 0.5f * outerRadius), new Vector3(innerRadius, 0f, -0.5f * outerRadius), new Vector3(0f, 0f, -outerRadius), new Vector3(-innerRadius, 0f, -0.5f * outerRadius), new Vector3(-innerRadius, 0f, 0.5f * outerRadius), new Vector3(0f, 0f, outerRadius) };
Segi enam sepenuhnya.paket unityKoordinat heksagonal
Mari kita lihat kembali koordinat sel, sekarang dalam konteks kisi segi enam. Koordinat Z terlihat baik-baik saja, dan koordinat X zigzag. Ini adalah efek samping dari garis offset untuk menutupi area persegi panjang.
Koordinat offset dengan garis nol yang disorot.Saat bekerja dengan segi enam, koordinat offset tersebut tidak mudah ditangani. Mari kita tambahkan struct
HexCoordinates
, yang dapat digunakan untuk mengkonversi ke sistem koordinat lain. Mari kita buat serializable sehingga Unity dapat menyimpannya dan itu akan mengalami kompilasi ulang dalam mode Play. Kami juga membuat koordinat ini tidak dapat diubah menggunakan properti hanya baca publik.
using UnityEngine; [System.Serializable] public struct HexCoordinates { public int X { get; private set; } public int Z { get; private set; } public HexCoordinates (int x, int z) { X = x; Z = z; } }
Tambahkan metode statis untuk membuat satu set koordinat dari koordinat offset biasa. Untuk saat ini, kami hanya akan menyalin koordinat ini.
public static HexCoordinates FromOffsetCoordinates (int x, int z) { return new HexCoordinates(x, z); } }
Kami juga menambahkan metode konversi string yang nyaman. Metode
ToString
secara default mengembalikan nama tipe struct, yang tidak terlalu berguna bagi kami. Kami mendefinisikan ulang sehingga mengembalikan koordinat pada satu baris. Kami juga akan menambahkan metode untuk menampilkan koordinat pada baris yang terpisah, karena kami sudah menggunakan skema seperti itu.
public override string ToString () { return "(" + X.ToString() + ", " + Z.ToString() + ")"; } public string ToStringOnSeparateLines () { return X.ToString() + "\n" + Z.ToString(); }
Sekarang kita dapat melewatkan banyak koordinat ke komponen
HexCell
kita.
public class HexCell : MonoBehaviour { public HexCoordinates coordinates; }
Ubah
HexGrid.CreateCell
sehingga dapat menggunakan koordinat baru.
HexCell cell = cells[i] = Instantiate<HexCell>(cellPrefab); cell.transform.SetParent(transform, false); cell.transform.localPosition = position; cell.coordinates = HexCoordinates.FromOffsetCoordinates(x, z); Text label = Instantiate<Text>(cellLabelPrefab); label.rectTransform.SetParent(gridCanvas.transform, false); label.rectTransform.anchoredPosition = new Vector2(position.x, position.z); label.text = cell.coordinates.ToStringOnSeparateLines();
Sekarang mari kita ulang koordinat X ini sehingga mereka sejajar sepanjang sumbu lurus. Ini dapat dilakukan dengan membatalkan pergeseran horizontal. Hasil yang dihasilkan biasanya disebut koordinat aksial.
public static HexCoordinates FromOffsetCoordinates (int x, int z) { return new HexCoordinates(x - z / 2, z); }
Koordinat aksial.Sistem koordinat dua dimensi ini memungkinkan kita untuk menggambarkan pergerakan perpindahan secara berurutan dalam empat arah. Namun, dua arah yang tersisa masih membutuhkan perhatian khusus. Ini membuat kita sadar bahwa ada dimensi ketiga. Dan faktanya, jika kita membalik dimensi X secara horizontal, kita akan mendapatkan dimensi Y yang hilang.
Pengukuran Y muncul.Karena pengukuran X dan Y ini merupakan salinan cermin satu sama lain, penambahan koordinatnya selalu memberikan hasil yang sama jika Z tetap konstan. Bahkan, jika Anda menambahkan ketiga koordinat, maka kami akan selalu mendapatkan nol. Jika Anda menambah satu koordinat, maka Anda harus mengurangi yang lainnya. Dan faktanya, ini memberi kita enam kemungkinan arah pergerakan. Koordinat seperti itu biasanya disebut kubik, karena mereka tiga dimensi, dan topologi menyerupai kubus.
Karena jumlah semua koordinat adalah nol, kita selalu dapat memperoleh koordinat dari dua koordinat lainnya. Karena kita sudah menyimpan koordinat X dan Z, kita tidak perlu menyimpan koordinat Y.
Kami dapat menambahkan properti yang mengevaluasinya jika perlu dan menggunakannya dalam metode string.
public int Y { get { return -X - Z; } } public override string ToString () { return "(" + X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ")"; } public string ToStringOnSeparateLines () { return X.ToString() + "\n" + Y.ToString() + "\n" + Z.ToString(); }
Koordinat kubik.Koordinator Inspektur
Dalam mode Putar, pilih salah satu sel kisi. Ternyata inspektur tidak menampilkan koordinatnya, hanya label awalan
HexCell.coordinates
yang
HexCell.coordinates
.
Inspektur tidak menampilkan koordinat.Meskipun tidak ada masalah besar dengan ini, akan bagus untuk menampilkan koordinat. Unity tidak menunjukkan koordinat karena mereka tidak ditandai sebagai bidang serial. Untuk menampilkannya, Anda harus secara eksplisit menentukan bidang serializable untuk X dan Z.
[SerializeField] private int x, z; public int X { get { return x; } } public int Z { get { return z; } } public HexCoordinates (int x, int z) { this.x = x; this.z = z; }
Koordinat X dan Z sekarang ditampilkan, tetapi dapat diubah. Kami tidak memerlukan ini, karena koordinat harus diperbaiki. Itu juga tidak terlalu baik bahwa mereka ditampilkan di bawah satu sama lain.
Kita bisa berbuat lebih baik: mendefinisikan laci properti kita sendiri untuk tipe
HexCoordinates
. Buat skrip
HexCoordinatesDrawer
dan rekatkan ke folder
Editor , karena skrip ini hanya untuk editor.
Kelas harus memperluas
UnityEditor.PropertyDrawer
dan membutuhkan atribut
UnityEditor.CustomPropertyDrawer
untuk mengaitkannya dengan tipe yang sesuai.
using UnityEngine; using UnityEditor; [CustomPropertyDrawer(typeof(HexCoordinates))] public class HexCoordinatesDrawer : PropertyDrawer { }
Laci properti menampilkan konten mereka menggunakan metode
OnGUI
. Metode ini memungkinkan untuk menggambar data properti serial dan label bidang tempat mereka berada di dalam persegi panjang layar.
public override void OnGUI ( Rect position, SerializedProperty property, GUIContent label ) { }
Kami mengekstrak nilai x dan z dari properti, dan kemudian menggunakannya untuk membuat set koordinat baru. Kemudian gambarkan label GUI di posisi yang dipilih menggunakan metode
HexCoordinates.ToString
kami.
public override void OnGUI ( Rect position, SerializedProperty property, GUIContent label ) { HexCoordinates coordinates = new HexCoordinates( property.FindPropertyRelative("x").intValue, property.FindPropertyRelative("z").intValue ); GUI.Label(position, coordinates.ToString()); }
Koordinat tanpa label awalan.Ini akan menampilkan koordinat, tetapi sekarang kami tidak memiliki nama bidang. Nama-nama ini biasanya diberikan dengan menggunakan metode
EditorGUI.PrefixLabel
. Sebagai bonus, itu mengembalikan persegi panjang selaras yang cocok dengan ruang di sebelah kanan label ini.
position = EditorGUI.PrefixLabel(position, label); GUI.Label(position, coordinates.ToString());
Berkoordinasi dengan label.paket unitySentuh sel
Kisi segi enam tidak terlalu menarik jika kita tidak dapat berinteraksi dengannya. Interaksi paling sederhana adalah menyentuh sel, jadi mari kita tambahkan dukungan untuknya. Untuk saat ini, kami cukup menempelkan kode ini langsung ke
HexGrid
. Ketika mulai bekerja, kami akan memindahkannya ke tempat lain.
Untuk menyentuh sel, Anda dapat memancarkan sinar ke dalam adegan dari posisi kursor mouse. Kita bisa menggunakan pendekatan yang sama seperti pada tutorial
Deformasi Mesh .
void Update () { if (Input.GetMouseButton(0)) { HandleInput(); } } void HandleInput () { Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(inputRay, out hit)) { TouchCell(hit.point); } } void TouchCell (Vector3 position) { position = transform.InverseTransformPoint(position); Debug.Log("touched at " + position); }
Sejauh ini, kodenya tidak melakukan apa-apa. Kita perlu menambahkan collider ke kisi sehingga balok bisa bertabrakan dengan sesuatu. Oleh karena itu, kami akan memberikan mesh collider
HexMesh
.
MeshCollider meshCollider; void Awake () { GetComponent<MeshFilter>().mesh = hexMesh = new Mesh(); meshCollider = gameObject.AddComponent<MeshCollider>(); โฆ }
Setelah triangulasi selesai, berikan jaring pada collider.
public void Triangulate (HexCell[] cells) { โฆ meshCollider.sharedMesh = hexMesh; }
Tidak bisakah kita menggunakan kotak collider?Kita bisa, tetapi itu tidak akan sama persis dengan garis besar kisi kita. Ya, dan kisi kami tidak akan tetap rata untuk waktu yang lama, tetapi ini adalah topik untuk tutorial mendatang.
Sekarang kita bisa menyentuh grid! Tapi sel mana yang kita sentuh? Untuk mengetahuinya, kita perlu mengonversi posisi sentuh ke koordinat segi enam. Ini berfungsi untuk
HexCoordinates
, jadi kami
HexCoordinates
menyatakan bahwa ia memiliki metode
FromPosition
statis.
public void TouchCell (Vector3 position) { position = transform.InverseTransformPoint(position); HexCoordinates coordinates = HexCoordinates.FromPosition(position); Debug.Log("touched at " + coordinates.ToString()); }
Bagaimana metode ini menentukan koordinat mana yang termasuk dalam posisi tersebut? Kita bisa mulai dengan membagi x dengan lebar horizontal segi enam. Dan karena koordinat Y adalah gambar cermin dari koordinat X, x negatif memberi kita y.
public static HexCoordinates FromPosition (Vector3 position) { float x = position.x / (HexMetrics.innerRadius * 2f); float y = -x; }
Tentu saja, ini akan memberi kita koordinat yang benar jika Z adalah nol. Kita harus bergeser lagi ketika bergerak di sepanjang Z. Setiap dua garis, kita harus bergeser ke kiri oleh satu unit.
float offset = position.z / (HexMetrics.outerRadius * 3f); x -= offset; y -= offset;
Akibatnya, nilai x dan y kami berubah menjadi bilangan bulat di tengah setiap sel. Oleh karena itu, membulatkannya ke bilangan bulat terdekat, kita harus mendapatkan koordinat. Kami juga menghitung Z dan dengan demikian mendapatkan koordinat akhir. int iX = Mathf.RoundToInt(x); int iY = Mathf.RoundToInt(y); int iZ = Mathf.RoundToInt(-x -y); return new HexCoordinates(iX, iZ);
Hasilnya terlihat menjanjikan, tetapi apakah koordinat ini benar? Dengan belajar dengan cermat, Anda mungkin menemukan bahwa kadang-kadang kita mendapatkan koordinat, yang jumlahnya tidak sama dengan nol! Mari aktifkan notifikasi untuk memastikan ini benar-benar terjadi. if (iX + iY + iZ != 0) { Debug.LogWarning("rounding error!"); } return new HexCoordinates(iX, iZ);
Kami sebenarnya menerima notifikasi. Bagaimana cara kita memperbaiki kesalahan ini? Itu hanya muncul di sebelah tepi antara segi enam. Artinya, pembulatan koordinat menyebabkan masalah. Koordinat mana yang dibulatkan ke arah yang salah? Semakin jauh kita bergerak dari pusat sel, semakin banyak pembulatan yang kita dapatkan. Oleh karena itu, logis untuk mengasumsikan bahwa koordinat yang dibulatkan kebanyakan tidak benar.Maka solusinya adalah dengan menjatuhkan koordinat dengan delta pembulatan terbesar dan membuatnya kembali dari nilai dua lainnya. Tetapi karena kita hanya perlu X dan Z, kita tidak bisa repot-repot menciptakan Y. if (iX + iY + iZ != 0) { float dX = Mathf.Abs(x - iX); float dY = Mathf.Abs(y - iY); float dZ = Mathf.Abs(-x -y - iZ); if (dX > dY && dX > dZ) { iX = -iY - iZ; } else if (dZ > dY) { iZ = -iX - iY; } }
Halaman mewarnai segi enam
Sekarang kita dapat menyentuh sel yang tepat, saatnya telah tiba untuk interaksi nyata. Mari kita ubah warna setiap sel yang kita masuki. Tambahkan untuk HexGrid
warna khusus dari sel default dan sel yang terpengaruh. public Color defaultColor = Color.white; public Color touchedColor = Color.magenta;
Pemilihan warna sel.Tambahkan ke HexCell
bidang warna umum. public class HexCell : MonoBehaviour { public HexCoordinates coordinates; public Color color; }
Tetapkan HexGrid.CreateCell
ke warna default. void CreateCell (int x, int z, int i) { โฆ cell.coordinates = HexCoordinates.FromOffsetCoordinates(x, z); cell.color = defaultColor; โฆ }
Kami juga perlu menambahkan HexMesh
informasi warna. List<Color> colors; void Awake () { โฆ vertices = new List<Vector3>(); colors = new List<Color>(); โฆ } public void Triangulate (HexCell[] cells) { hexMesh.Clear(); vertices.Clear(); colors.Clear(); โฆ hexMesh.vertices = vertices.ToArray(); hexMesh.colors = colors.ToArray(); โฆ }
Sekarang, ketika melakukan triangulasi, kita harus menambahkan data warna ke setiap segitiga. Untuk tujuan ini kami akan membuat metode terpisah. void Triangulate (HexCell cell) { Vector3 center = cell.transform.localPosition; for (int i = 0; i < 6; i++) { AddTriangle( center, center + HexMetrics.corners[i], center + HexMetrics.corners[i + 1] ); AddTriangleColor(cell.color); } } void AddTriangleColor (Color color) { colors.Add(color); colors.Add(color); colors.Add(color); }
Kembali ke HexGrid.TouchCell
. Pertama, konversi koordinat sel ke indeks array yang sesuai. Untuk kotak persegi, ini hanya X ditambah Z dikalikan dengan lebarnya, tetapi dalam kasus kami, kami juga harus menambahkan offset setengah Z. Lalu kami mengambil sel, mengubah warnanya, dan melakukan triangulasi mesh lagi.Apakah kita benar-benar perlu melakukan triangulasi ulang seluruh jala?, . . , , . .
public void TouchCell (Vector3 position) { position = transform.InverseTransformPoint(position); HexCoordinates coordinates = HexCoordinates.FromPosition(position); int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2; HexCell cell = cells[index]; cell.color = touchedColor; hexMesh.Triangulate(cells); }
Meskipun sekarang kita dapat mewarnai sel, perubahan visual belum terlihat. Ini karena shader tidak menggunakan warna titik secara default. Kita harus menulis sendiri. Buat shader default baru ( Aset / Buat / Shader / Default Shader Permukaan ). Hanya dua perubahan yang perlu dilakukan untuk itu. Pertama, tambahkan data warna ke struct inputnya. Kedua, kalikan Albedo dengan warna ini. Kami hanya tertarik pada saluran RGB, karena materialnya buram. Shader "Custom/VertexColors" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard fullforwardshadows #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; float4 color : COLOR; }; half _Glossiness; half _Metallic; fixed4 _Color; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb * IN.color; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } ENDCG } FallBack "Diffuse" }
Buat bahan baru menggunakan shader ini, dan kemudian buat jala menggunakan bahan ini. Berkat ini, warna sel akan muncul.Sel berwarna.Saya mendapatkan artefak bayangan aneh!Unity . , , Z-. .
paket unityEditor peta
Sekarang kita tahu bagaimana mengubah warna, mari kita buat editor dalam game sederhana. Fungsi ini tidak berlaku untuk kapabilitas HexGrid
, jadi kami akan mengubahnya TouchCell
menjadi metode umum dengan parameter warna tambahan. Hapus juga bidang touchedColor
. public void ColorCell (Vector3 position, Color color) { position = transform.InverseTransformPoint(position); HexCoordinates coordinates = HexCoordinates.FromPosition(position); int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2; HexCell cell = cells[index]; cell.color = color; hexMesh.Triangulate(cells); }
Buat komponen HexMapEditor
dan pindahkan Update
dan metode ke sana HandleInput
. Tambahkan bidang umum untuk merujuk pada kisi segi enam, array warna, dan bidang pribadi untuk melacak warna aktif. Akhirnya, tambahkan metode umum untuk memilih warna dan membuatnya awalnya memilih warna pertama. using UnityEngine; public class HexMapEditor : MonoBehaviour { public Color[] colors; public HexGrid hexGrid; private Color activeColor; void Awake () { SelectColor(0); } void Update () { if (Input.GetMouseButton(0)) { HandleInput(); } } void HandleInput () { Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(inputRay, out hit)) { hexGrid.ColorCell(hit.point, activeColor); } } public void SelectColor (int index) { activeColor = colors[index]; } }
Tambahkan kanvas lain, kali ini dengan mempertahankan pengaturan default. Tambahkan komponen ke dalamnya HexMapEditor
, tentukan beberapa warna dan sambungkan ke kisi segi enam. Kali ini kita memerlukan objek sistem acara, dan itu secara otomatis dibuat lagi.Editor peta segi enam empat warna.Tambahkan panel ke kanvas untuk menyimpan pemilih warna ( GameObject / UI / Panel ). Tambahkan grup sakelar ( Komponen / UI / Grup Beralih ). Buat panel kecil dan letakkan di sudut layar.Panel warna dengan grup sakelar.Sekarang isi panel dengan sakelar untuk setiap warna ( GameObject / UI / Toggle ). Selama kita tidak repot-repot membuat UI yang rumit, konfigurasi manual yang sederhana sudah cukup.Satu sakelar untuk setiap warna.Hidupkan sakelar pertama. Buat juga semua bagian sakelar dari grup sakelar sehingga hanya satu yang dapat dipilih pada satu waktu. Akhirnya, sambungkan ke metode SelectColor
editor kami. Ini dapat dilakukan dengan menggunakan tombol UI "+" pada event On Value Changed . Pilih objek editor peta, lalu pilih metode yang diinginkan dari daftar drop-down.Saklar pertama.Acara ini melewati argumen boolean yang menentukan apakah saklar dihidupkan setiap kali diubah. Tapi kami tidak peduli. Sebagai gantinya, kita harus secara manual melewati argumen integer yang sesuai dengan indeks warna yang ingin kita gunakan. Oleh karena itu, biarkan nilai 0 untuk sakelar pertama, atur nilai 1 ke detik, dan seterusnya.Kapan metode beralih acara dipanggil?. , , .
, , . , SelectColor
. , .
Pewarnaan dalam beberapa warna.Meskipun UI berfungsi, ada satu detail yang mengganggu. Untuk melihatnya, gerakkan panel sehingga menutupi kisi segi enam. Saat memilih warna baru, kami juga akan mewarnai sel di bawah UI. Artinya, kami secara bersamaan berinteraksi dengan UI dan dengan grid. Ini adalah perilaku yang tidak diinginkan.Ini dapat diperbaiki dengan menanyakan sistem kejadian jika ia menentukan lokasi kursor pada suatu objek. Karena dia hanya tahu tentang objek UI, ini akan memberi tahu kita bahwa kita berinteraksi dengan UI. Oleh karena itu, kita perlu memproses input sendiri hanya jika ini tidak terjadi. using UnityEngine; using UnityEngine.EventSystems; โฆ void Update () { if ( Input.GetMouseButton(0) && !EventSystem.current.IsPointerOverGameObject() ) { HandleInput(); } }
paket unityBagian 2: memadukan warna sel
Daftar isi
- Hubungkan tetangga.
- Interpolasi warna di antara segitiga.
- Buat area blending.
- Sederhanakan geometri.
Pada bagian sebelumnya, kami meletakkan dasar-dasar grid dan menambahkan kemampuan untuk mengedit sel. Setiap sel memiliki warna solid sendiri dan warna di perbatasan sel berubah secara dramatis. Dalam tutorial ini, kita akan membuat zona transisi yang mencampur warna sel tetangga.Transisi halus antar sel.Sel tetangga
Sebelum kita melakukan penghalusan antar warna sel, kita perlu mencari tahu sel mana yang berdekatan satu sama lain. Setiap sel memiliki enam tetangga yang dapat diidentifikasi ke arah titik mata angin. Kami akan mendapatkan petunjuk berikut: timur laut, timur, tenggara, barat daya, barat dan barat laut. Mari kita membuat enumerasi untuk mereka dan menempelkannya ke file skrip yang terpisah. public enum HexDirection { NE, E, SE, SW, W, NW }
Apa itu enum?enum
, . . , . , .
enum . , integer . , - , integer.
Enam tetangga, enam arah.Untuk menyimpan tetangga ini, tambahkan ke HexCell
array. Meskipun kami dapat menjadikannya umum, kami akan menjadikannya pribadi dan akan memberikan akses ke metode menggunakan arahan. Kami juga membuatnya serializable sehingga ikatan tidak hilang saat rekompilasi. [SerializeField] HexCell[] neighbors;
Apakah kita perlu menyimpan semua koneksi dengan tetangga?, . โ , .
Sekarang susunan tetangga ditampilkan di inspektur. Karena setiap sel memiliki enam tetangga, untuk prefab Hex Cell kami, kami menetapkan ukuran array 6.Ada ruang untuk enam tetangga di rumah pabrikan kami.Sekarang mari kita tambahkan metode umum untuk mendapatkan tetangga sel dalam satu arah. Karena nilai arah selalu dalam rentang dari 0 hingga 5, kita tidak perlu memeriksa apakah indeks berada dalam array. public HexCell GetNeighbor (HexDirection direction) { return neighbors[(int)direction]; }
Tambahkan metode untuk menentukan tetangga. public void SetNeighbor (HexDirection direction, HexCell cell) { neighbors[(int)direction] = cell; }
Hubungan tetangga adalah dua arah. Karena itu, ketika mengatur tetangga di satu arah, akan logis untuk segera mengatur tetangga di arah yang berlawanan. public void SetNeighbor (HexDirection direction, HexCell cell) { neighbors[(int)direction] = cell; cell.neighbors[(int)direction.Opposite()] = this; }
Tetangga di arah yang berlawanan.Tentu saja, ini menunjukkan bahwa kita dapat meminta petunjuk untuk tetangga yang berlawanan. Kami dapat menerapkan ini dengan membuat metode ekstensi untuk HexDirection
. Untuk mendapatkan arah yang berlawanan, Anda perlu menambahkan ke aslinya 3. Namun, ini hanya bekerja untuk tiga arah pertama, untuk sisanya Anda harus mengurangi 3. public enum HexDirection { NE, E, SE, SW, W, NW } public static class HexDirectionExtensions { public static HexDirection Opposite (this HexDirection direction) { return (int)direction < 3 ? (direction + 3) : (direction - 3); } }
Apa itu metode ekstensi?โ , - . โ , , , . this
. , .
? , , , . ? โ . , , .
Koneksi Tetangga
Kami dapat menginisialisasi tautan tetangga HexGrid.CreateCell
. Saat melintasi sel baris demi baris, dari kiri ke kanan, kita tahu sel mana yang telah dibuat. Ini adalah sel-sel yang dengannya kita dapat terhubung.Yang paling sederhana adalah senyawa E - W. Sel pertama dari setiap baris tidak memiliki tetangga sebelah timur. Tetapi semua sel lain memilikinya. Dan tetangga-tetangga ini diciptakan sebelum sel yang saat ini kami bekerja. Karena itu, kita dapat menghubungkannya.Koneksi dari E ke W selama pembuatan sel. void CreateCell (int x, int z, int i) { โฆ cell.color = defaultColor; if (x > 0) { cell.SetNeighbor(HexDirection.W, cells[i - 1]); } Text label = Instantiate<Text>(cellLabelPrefab); โฆ }
Tetangga timur dan barat terhubung.Kita perlu membuat dua koneksi dua arah lagi. Karena ini adalah koneksi antara garis yang berbeda dari grid, kami hanya dapat berkomunikasi dengan saluran sebelumnya. Ini artinya kita harus melewati baris pertama. if (x > 0) { cell.SetNeighbor(HexDirection.W, cells[i - 1]); } if (z > 0) { }
Karena garis-garisnya zig-zag, mereka perlu diproses secara berbeda. Pertama mari kita berurusan dengan garis genap. Karena semua sel di baris tersebut memiliki tetangga di SE, kita dapat menghubungkannya.Koneksi dari NW ke SE untuk saluran genap. if (z > 0) { if ((z & 1) == 0) { cell.SetNeighbor(HexDirection.SE, cells[i - width]); } }
Apa yang z & 1 lakukan?&&
โ , &
โ . , . 1, 1. , 10101010 & 00001111
00001010
.
. 0 1. 1, 2, 3, 4 1, 10, 11, 100. , 0.
, , . 0, .
Kita dapat terhubung dengan tetangga di SW, kecuali untuk sel pertama dari setiap baris yang tidak memilikinya.Koneksi dari NE ke SW untuk saluran genap. if (z > 0) { if ((z & 1) == 0) { cell.SetNeighbor(HexDirection.SE, cells[i - width]); if (x > 0) { cell.SetNeighbor(HexDirection.SW, cells[i - width - 1]); } } }
Garis ganjil mengikuti logika yang sama, tetapi dalam gambar cermin. Setelah menyelesaikan proses ini, semua tetangga di jaringan kami terhubung. if (z > 0) { if ((z & 1) == 0) { cell.SetNeighbor(HexDirection.SE, cells[i - width]); if (x > 0) { cell.SetNeighbor(HexDirection.SW, cells[i - width - 1]); } } else { cell.SetNeighbor(HexDirection.SW, cells[i - width]); if (x < width - 1) { cell.SetNeighbor(HexDirection.SE, cells[i - width + 1]); } } }
Semua tetangga terhubung.Tentu saja, tidak setiap sel terhubung dengan tepat enam tetangga. Sel pada batas kisi memiliki setidaknya dua dan tidak lebih dari lima tetangga. Dan ini harus diperhitungkan.Tetangga untuk setiap sel.paket unityPencampuran warna
Pencampuran warna akan mempersulit triangulasi setiap sel. Karena itu, mari kita pisahkan kode triangulasi di bagian yang terpisah. Karena kita sekarang memiliki petunjuk arah, mari kita gunakan alih-alih indeks numerik untuk menunjukkan bagian. void Triangulate (HexCell cell) { for (HexDirection d = HexDirection.NE; d <= HexDirection.NW; d++) { Triangulate(d, cell); } } void Triangulate (HexDirection direction, HexCell cell) { Vector3 center = cell.transform.localPosition; AddTriangle( center, center + HexMetrics.corners[(int)direction], center + HexMetrics.corners[(int)direction + 1] ); AddTriangleColor(cell.color); }
Sekarang, ketika kita menggunakan arah, akan lebih mudah untuk mendapatkan sudut dengan arah, dan tidak melakukan konversi ke indeks. AddTriangle( center, center + HexMetrics.GetFirstCorner(direction), center + HexMetrics.GetSecondCorner(direction) );
Untuk melakukan ini, Anda perlu menambahkan HexMetrics
dua metode statis. Sebagai bonus, ini memungkinkan kami untuk membuat array sudut pribadi. static Vector3[] corners = { new Vector3(0f, 0f, outerRadius), new Vector3(innerRadius, 0f, 0.5f * outerRadius), new Vector3(innerRadius, 0f, -0.5f * outerRadius), new Vector3(0f, 0f, -outerRadius), new Vector3(-innerRadius, 0f, -0.5f * outerRadius), new Vector3(-innerRadius, 0f, 0.5f * outerRadius), new Vector3(0f, 0f, outerRadius) }; public static Vector3 GetFirstCorner (HexDirection direction) { return corners[(int)direction]; } public static Vector3 GetSecondCorner (HexDirection direction) { return corners[(int)direction + 1]; }
Beberapa warna pada segitiga
Sejauh ini, metode HexMesh.AddTriangleColor
ini hanya memiliki satu argumen warna. Itu hanya bisa membuat segitiga dengan warna solid. Mari kita buat alternatif yang mendukung warna terpisah untuk setiap titik. void AddTriangleColor (Color c1, Color c2, Color c3) { colors.Add(c1); colors.Add(c2); colors.Add(c3); }
Sekarang kita dapat mulai mencampur warna! Mari kita mulai dengan hanya menggunakan warna tetangga untuk dua simpul lainnya. void Triangulate (HexDirection direction, HexCell cell) { Vector3 center = cell.transform.localPosition; AddTriangle( center, center + HexMetrics.GetFirstCorner(direction), center + HexMetrics.GetSecondCorner(direction) ); HexCell neighbor = cell.GetNeighbor(direction); AddTriangleColor(cell.color, neighbor.color, neighbor.color); }
Sayangnya, ini mengarah ke NullReferenceException
, karena sel-sel di perbatasan tidak memiliki enam tetangga. Apa yang harus kita lakukan ketika ada kekurangan tetangga? Mari bersikap pragmatis dan menggunakan sel itu sendiri sebagai penggantinya. HexCell neighbor = cell.GetNeighbor(direction) ?? cell;
Apa yang dilakukan operator ??null-coalescing operator. , a ?? b
โ a != null ? a : b
.
, - Unity . null
. .
Ada campuran warna, tetapi dilakukan dengan tidak benar.Ke mana label koordinat pergi?, UI.
Rata-rata warna
Pencampuran warna bekerja, tetapi hasilnya jelas salah. Warna di tepi heksagon harus rata-rata dari dua sel yang berdekatan. HexCell neighbor = cell.GetNeighbor(direction) ?? cell; Color edgeColor = (cell.color + neighbor.color) * 0.5f; AddTriangleColor(cell.color, edgeColor, edgeColor);
Mencampur iga.Meskipun kami mencampur pada bagian tepinya, kami masih mendapatkan batas warna yang tajam. Ini terjadi karena setiap simpul dari segi enam dibagi oleh tiga segi enam.Tiga tetangga, empat warna.Ini berarti bahwa kita juga perlu mempertimbangkan tetangga di arah sebelumnya dan selanjutnya. Artinya, kita mendapatkan empat warna dalam dua set tiga.Mari kita tambahkan HexDirectionExtensions
dua metode tambahan untuk transisi yang nyaman ke arah sebelumnya dan selanjutnya. public static HexDirection Previous (this HexDirection direction) { return direction == HexDirection.NE ? HexDirection.NW : (direction - 1); } public static HexDirection Next (this HexDirection direction) { return direction == HexDirection.NW ? HexDirection.NE : (direction + 1); }
Sekarang kita bisa mendapatkan ketiga tetangga dan melakukan pencampuran tiga arah. HexCell prevNeighbor = cell.GetNeighbor(direction.Previous()) ?? cell; HexCell neighbor = cell.GetNeighbor(direction) ?? cell; HexCell nextNeighbor = cell.GetNeighbor(direction.Next()) ?? cell; AddTriangleColor( cell.color, (cell.color + prevNeighbor.color + neighbor.color) / 3f, (cell.color + neighbor.color + nextNeighbor.color) / 3f );
Campur di sudut-sudut.Jadi kita mendapatkan transisi warna yang benar, dengan pengecualian perbatasan mesh. Sel perbatasan tidak konsisten dengan warna tetangga yang hilang, jadi di sini kita masih melihat batas yang tajam. Namun, secara umum, pendekatan kami saat ini tidak memberikan hasil yang baik. Kami membutuhkan strategi yang lebih baik.paket unityArea pencampuran
Mencampur seluruh permukaan segi enam menyebabkan kekacauan kabur. Kami tidak dapat melihat sel secara jelas. Hasil dapat sangat ditingkatkan dengan mencampurkan hanya di sebelah tepi segi enam. Dalam hal ini, wilayah bagian dalam segi enam akan mempertahankan warna yang solid.Naungan sen terus menerus dengan area pencampuran.Berapa ukuran daerah padat dibandingkan dengan daerah pencampuran? Distribusi yang berbeda menghasilkan hasil yang berbeda pula. Kami akan mendefinisikan area ini sebagai sebagian kecil dari radius luar. Biarkan sama dengan 75%. Ini akan membawa kita ke dua metrik baru, berjumlah 100%. public const float solidFactor = 0.75f; public const float blendFactor = 1f - solidFactor;
Dengan membuat faktor pengisian padat baru ini, kita dapat menulis metode untuk mendapatkan sudut hexagon internal yang solid. public static Vector3 GetFirstSolidCorner (HexDirection direction) { return corners[(int)direction] * solidFactor; } public static Vector3 GetSecondSolidCorner (HexDirection direction) { return corners[(int)direction + 1] * solidFactor; }
Sekarang, ubah HexMesh.Triangulate
sehingga menggunakan sudut teduh padat ini, bukan sudut aslinya. Kami meninggalkan warna yang sama untuk saat ini. AddTriangle( center, center + HexMetrics.GetFirstSolidCorner(direction), center + HexMetrics.GetSecondSolidCorner(direction) );
Segi enam padat tanpa tepi.Triangulasi area pencampuran
Kita perlu mengisi ruang kosong yang kita buat dengan mengurangi segitiga. Di setiap arah, ruang ini memiliki bentuk trapesium. Untuk menutupinya, Anda bisa menggunakan quadrangle (quad). Oleh karena itu, kami akan membuat metode untuk menambahkan segi empat dan warnanya.Tulang rusuk trapesium. void AddQuad (Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4) { int vertexIndex = vertices.Count; vertices.Add(v1); vertices.Add(v2); vertices.Add(v3); vertices.Add(v4); triangles.Add(vertexIndex); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 1); triangles.Add(vertexIndex + 2); triangles.Add(vertexIndex + 3); } void AddQuadColor (Color c1, Color c2, Color c3, Color c4) { colors.Add(c1); colors.Add(c2); colors.Add(c3); colors.Add(c4); }
Kami membuat ulang HexMesh.Triangulate
sehingga segitiga menerima satu warna, dan segi empat melakukan campuran antara warna solid dan warna dua sudut. void Triangulate (HexDirection direction, HexCell cell) { Vector3 center = cell.transform.localPosition; Vector3 v1 = center + HexMetrics.GetFirstSolidCorner(direction); Vector3 v2 = center + HexMetrics.GetSecondSolidCorner(direction); AddTriangle(center, v1, v2); AddTriangleColor(cell.color); Vector3 v3 = center + HexMetrics.GetFirstCorner(direction); Vector3 v4 = center + HexMetrics.GetSecondCorner(direction); AddQuad(v1, v2, v3, v4); HexCell prevNeighbor = cell.GetNeighbor(direction.Previous()) ?? cell; HexCell neighbor = cell.GetNeighbor(direction) ?? cell; HexCell nextNeighbor = cell.GetNeighbor(direction.Next()) ?? cell; AddQuadColor( cell.color, cell.color, (cell.color + prevNeighbor.color + neighbor.color) / 3f, (cell.color + neighbor.color + nextNeighbor.color) / 3f ); }
Bercampur dengan tulang rusuk trapesium.Jembatan antara tulang rusuk
Gambar semakin membaik, tetapi pekerjaan belum selesai. Mencampur warna antara dua tetangga terkontaminasi oleh sel tetangga. Untuk menghindari ini, kita perlu memotong sudut-sudut dari trapesium dan mengubahnya menjadi persegi panjang. Setelah itu, ia akan membuat jembatan antara sel dan tetangganya, meninggalkan celah di sisinya.Jembatan antara tulang rusuk.Kita dapat menemukan posisi baru v3
dan v4
, mulai dengan v1
dan v2
, dan kemudian bergerak di sepanjang jembatan langsung ke tepi sel. Apa yang akan menjadi perpindahan jembatan? Kita dapat menemukannya dengan mengambil titik tengah antara dua sudut yang sesuai, dan kemudian menerapkan koefisien pencampuran untuk itu. Ini akan dilakukan HexMetrics
. public static Vector3 GetBridge (HexDirection direction) { return (corners[(int)direction] + corners[(int)direction + 1]) * 0.5f * blendFactor; }
Kembali ke HexMesh
, sekarang akan logis untuk menambahkan opsi AddQuadColor
yang hanya membutuhkan dua warna. void AddQuadColor (Color c1, Color c2) { colors.Add(c1); colors.Add(c1); colors.Add(c2); colors.Add(c2); }
Ubahlah Triangulate
sehingga menciptakan jembatan campuran yang tepat antar tetangga. Vector3 bridge = HexMetrics.GetBridge(direction); Vector3 v3 = v1 + bridge; Vector3 v4 = v2 + bridge; AddQuad(v1, v2, v3, v4); HexCell prevNeighbor = cell.GetNeighbor(direction.Previous()) ?? cell; HexCell neighbor = cell.GetNeighbor(direction) ?? cell; HexCell nextNeighbor = cell.GetNeighbor(direction.Next()) ?? cell; AddQuadColor(cell.color, (cell.color + neighbor.color) * 0.5f);
Jembatan yang dicat dengan benar dengan ruang sudut.Mengisi celah
Sekarang kita telah membentuk celah segitiga di persimpangan tiga sel. Kami mendapatkan celah ini dengan memotong sisi segitiga trapesium. Mari kita dapatkan segitiga ini kembali.Pertama, pertimbangkan segitiga yang menghubungkan tetangga sebelumnya. Verteks pertamanya memiliki warna sel. Warna puncak kedua adalah campuran tiga warna. Dan puncak terakhir akan memiliki warna yang sama dengan titik di tengah jembatan. Color bridgeColor = (cell.color + neighbor.color) * 0.5f; AddQuadColor(cell.color, bridgeColor); AddTriangle(v1, center + HexMetrics.GetFirstCorner(direction), v3); AddTriangleColor( cell.color, (cell.color + prevNeighbor.color + neighbor.color) / 3f, bridgeColor );
Hampir semuanya siap.Segitiga lain bekerja dengan cara yang sama, kecuali bahwa jembatan tidak menyentuh ketiga, tetapi puncak kedua. AddTriangle(v2, v4, center + HexMetrics.GetSecondCorner(direction)); AddTriangleColor( cell.color, bridgeColor, (cell.color + neighbor.color + nextNeighbor.color) / 3f );
Pewarnaan penuh.Sekarang kami memiliki area campuran yang indah yang dapat kami berikan dalam ukuran berapa pun. Tepi dapat dibuat buram atau setajam yang Anda inginkan. Tetapi Anda dapat melihat bahwa pencampuran dekat perbatasan mesh masih belum diimplementasikan dengan benar. Dan lagi kita akan membiarkannya nanti, fokus pada topik lain untuk saat ini.Namun transisi antar warna masih jelek. . .
paket unityRib Rib
Lihatlah topologi kisi kami. Formulir apa yang terlihat di sini? Jika Anda tidak memperhatikan perbatasan, maka kita dapat membedakan tiga jenis formulir yang berbeda. Ada segi enam satu warna, segi empat dua warna dan segitiga tiga warna. Ketiga warna ini muncul di persimpangan tiga sel.Tiga struktur visual.Jadi, setiap dua segi enam dihubungkan oleh satu jembatan persegi panjang. Dan setiap tiga segi enam dihubungkan oleh satu segitiga. Namun, kami melakukan triangulasi yang lebih kompleks. Sekarang kita menggunakan dua segi empat bukan satu untuk menghubungkan sepasang segi enam. Dan untuk menghubungkan tiga segi enam, kami menggunakan enam segitiga. Ini terlalu berlebihan. Selain itu, jika kita terhubung langsung ke satu bentuk, maka kita tidak perlu rata-rata warna. Oleh karena itu, kita akan dapat bertahan dengan kompleksitas yang lebih sedikit, lebih sedikit pekerjaan dan lebih sedikit segitiga.Lebih sulit dari yang diperlukan.Mengapa kita membutuhkan ini?, . , , . , , . , , .
Bridging langsung
Sekarang jembatan kami di antara tulang rusuk terdiri dari dua segi empat. Untuk memperluas mereka ke segi enam berikutnya, kita perlu menggandakan panjang jembatan. Ini berarti bahwa kita tidak perlu lagi rata-rata dua sudut HexMetrics.GetBridge
. Sebagai gantinya, kami cukup menambahkannya dan kemudian mengalikannya dengan faktor pencampuran. public static Vector3 GetBridge (HexDirection direction) { return (corners[(int)direction] + corners[(int)direction + 1]) * blendFactor; }
Jembatan membentang sepanjang dan saling tumpang tindih.Jembatan sekarang membuat koneksi langsung antara segi enam. Tapi kami masih menghasilkan dua segi empat per koneksi, satu di setiap arah. Artinya, hanya satu dari mereka yang harus membuat jembatan antara dua sel.Mari sederhanakan kode triangulasi kami terlebih dahulu. Kami akan menghapus semua yang terkait dengan segitiga tepi dan pencampuran warna. Kemudian pindahkan kode yang menambahkan segi empat jembatan ke metode baru. Kami melewati dua simpul pertama ke metode ini sehingga kami tidak perlu menghitung ulang. void Triangulate (HexDirection direction, HexCell cell) { Vector3 center = cell.transform.localPosition; Vector3 v1 = center + HexMetrics.GetFirstSolidCorner(direction); Vector3 v2 = center + HexMetrics.GetSecondSolidCorner(direction); AddTriangle(center, v1, v2); AddTriangleColor(cell.color); TriangulateConnection(direction, cell, v1, v2); } void TriangulateConnection ( HexDirection direction, HexCell cell, Vector3 v1, Vector3 v2 ) { HexCell neighbor = cell.GetNeighbor(direction) ?? cell; Vector3 bridge = HexMetrics.GetBridge(direction); Vector3 v3 = v1 + bridge; Vector3 v4 = v2 + bridge; AddQuad(v1, v2, v3, v4); AddQuadColor(cell.color, neighbor.color); }
Sekarang kita dapat dengan mudah membatasi triangulasi senyawa. Untuk memulainya, kami akan menambahkan jembatan hanya ketika bekerja dengan koneksi NE. if (direction == HexDirection.NE) { TriangulateConnection(direction, cell, v1, v2); }
Jembatan hanya ke arah NE.Tampaknya kita dapat menutupi semua senyawa dengan melakukan triangulasi hanya dalam tiga arah pertama: NE, E dan SE. if (direction <= HexDirection.SE) { TriangulateConnection(direction, cell, v1, v2); }
Semua jembatan dan jembatan internal di perbatasan.Kami membahas semua koneksi antara dua sel tetangga. Tapi kami juga punya beberapa jembatan dari sel ke mana-mana. Mari kita singkirkan mereka, keluar TriangulateConnection
saat tetangga kita keluar. Artinya, kita tidak perlu lagi mengganti tetangga yang hilang dengan sel itu sendiri. void TriangulateConnection ( HexDirection direction, HexCell cell, Vector3 v1, Vector3 v2 ) { HexCell neighbor = cell.GetNeighbor(direction); if (neighbor == null) { return; } โฆ }
Hanya jembatan internal.Sambungan segitiga
Sekarang kita perlu menutup celah segitiga lagi. Mari kita lakukan ini untuk menghubungkan segitiga ke tetangga berikutnya. Dan ini lagi perlu dilakukan hanya ketika tetangga ada. void TriangulateConnection ( HexDirection direction, HexCell cell, Vector3 v1, Vector3 v2 ) { โฆ HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (nextNeighbor != null) { AddTriangle(v2, v4, v2); AddTriangleColor(cell.color, neighbor.color, nextNeighbor.color); } }
Apa yang akan menjadi posisi puncak ketiga? Saya dimasukkan sebagai pengganti v2
, tetapi ini jelas salah. Karena setiap tepi segitiga ini terhubung ke jembatan, kita dapat menemukannya dengan berjalan di sepanjang jembatan ke tetangga berikutnya. AddTriangle(v2, v4, v2 + HexMetrics.GetBridge(direction.Next()));
Kami melakukan triangulasi penuh lagi.Apakah kita sudah selesai? Belum, karena sekarang kami sedang membuat segitiga yang tumpang tindih. Karena ketiga sel memiliki satu koneksi segitiga yang sama, kita perlu menambahkannya hanya untuk dua koneksi. Karena itu, NE dan E. akan melakukannya. if (direction <= HexDirection.E && nextNeighbor != null) { AddTriangle(v2, v4, v2 + HexMetrics.GetBridge(direction.Next())); AddTriangleColor(cell.color, neighbor.color, nextNeighbor.color); }
paket unityBagian 3: ketinggian
Daftar isi
- Tambahkan tinggi sel.
- Triangulasi lereng.
- Masukkan tepian.
- Kombinasikan tepian dan tebing.
Di bagian tutorial ini, kami akan menambahkan dukungan untuk berbagai tingkat ketinggian dan membuat transisi khusus di antara mereka.Ketinggian dan tepian.Tinggi sel
Kami membagi peta kami menjadi sel-sel terpisah yang meliputi area datar. Sekarang kita akan memberikan masing-masing sel tingkat ketinggiannya sendiri. Kami akan menggunakan level ketinggian diskrit untuk menyimpannya sebagai bidang bilangan bulat di HexCell
. public int elevation;
Seberapa besar masing-masing level ketinggian berikutnya? Kita dapat menggunakan nilai apa pun, jadi mari kita tetapkan itu sebagai konstanta lain HexMetrics
. Kami akan menggunakan langkah lima unit sehingga transisi terlihat jelas. Dalam game nyata, saya akan menggunakan langkah yang lebih kecil. public const float elevationStep = 5f;
Ubah sel
Sampai sekarang, kita hanya bisa mengubah warna sel, tetapi sekarang kita bisa mengubah ketinggiannya. Karena itu, metode HexGrid.ColorCell
ini tidak cukup untuk kita. Selain itu, di masa mendatang, kami dapat menambahkan opsi pengeditan sel lainnya, sehingga kami membutuhkan pendekatan baru.Ubah nama ColorCell
menjadi GetCell
dan buatlah itu, alih-alih mengatur warna sel, ia mengembalikan sel pada posisi yang diberikan. Karena metode ini tidak mengubah apa pun, kita perlu segera melakukan triangulasi sel. public HexCell GetCell (Vector3 position) { position = transform.InverseTransformPoint(position); HexCoordinates coordinates = HexCoordinates.FromPosition(position); int index = coordinates.X + coordinates.Z * width + coordinates.Z / 2; return cells[index]; }
Sekarang editor akan berurusan dengan perubahan sel. Setelah menyelesaikan pekerjaan, grid perlu triangulasi lagi. Untuk melakukan ini, tambahkan metode umum HexGrid.Refresh
. public void Refresh () { hexMesh.Triangulate(cells); }
Ubah HexMapEditor
sehingga dia bisa bekerja dengan metode baru. Mari kita beri dia metode baru EditCell
, yang akan menangani semua perubahan pada sel, setelah itu akan memperbarui grid. void HandleInput () { Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if (Physics.Raycast(inputRay, out hit)) { EditCell(hexGrid.GetCell(hit.point)); } } void EditCell (HexCell cell) { cell.color = activeColor; hexGrid.Refresh(); }
Kita dapat mengubah ketinggian hanya dengan menetapkan tingkat ketinggian yang diinginkan sel yang diinginkan. int activeElevation; void EditCell (HexCell cell) { cell.color = activeColor; cell.elevation = activeElevation; hexGrid.Refresh(); }
Seperti halnya warna, kita memerlukan metode untuk mengatur level ketinggian aktif, yang akan kita kaitkan dengan UI. Untuk memilih nilai dari interval ketinggian, kami menggunakan slider. Karena slider bekerja dengan float, metode kami memerlukan parameter tipe float. Kami hanya akan mengubahnya menjadi integer. public void SetElevation (float elevation) { activeElevation = (int)elevation; }
Tambahkan slider ( GameObject / Create / Slider ) ke kanvas dan letakkan di bawah bilah warna. Kami membuatnya vertikal, dari bawah ke atas, sehingga secara visual sesuai dengan tingkat ketinggian. Kami membatasinya ke bilangan bulat dan membuat interval yang sesuai, misalnya, dari 0 hingga 6. Kemudian kami lampirkan event On Value Changed ke metode SetElevation
objek Hex Map Editor . Metode harus dipilih dari daftar dinamis sehingga disebut dengan nilai slider.Slider tinggi.Visualisasi tinggi
Saat mengganti sel, kami sekarang mengatur warna dan tinggi. Meskipun di inspektur kita dapat melihat bahwa ketinggian benar-benar berubah, proses triangulasi masih mengabaikannya.Cukup bagi kita untuk mengubah posisi lokal vertikal sel ketika mengubah ketinggian. Untuk kenyamanan, mari jadikan metode ini HexCell.elevation
pribadi dan tambahkan properti umum HexCell.Elevation
. public int Elevation { get { return elevation; } set { elevation = value; } } int elevation;
Sekarang kita dapat mengubah posisi vertikal sel saat mengedit ketinggian. set { elevation = value; Vector3 position = transform.localPosition; position.y = value * HexMetrics.elevationStep; transform.localPosition = position; }
Tentu saja, ini membutuhkan perubahan kecil untuk HexMapEditor.EditCell
. void EditCell (HexCell cell) { cell.color = activeColor; cell.Elevation = activeElevation; hexGrid.Refresh(); }
Sel dengan ketinggian berbeda.Apakah collider mesh berubah agar sesuai dengan ketinggian baru?Unity mesh collider null. , , null . . ( ) .
Ketinggian sel sekarang terlihat, tetapi ada dua masalah. Pertama-tama label sel menghilang di bawah sel yang terangkat. Kedua, koneksi antar sel mengabaikan tinggi. Mari kita perbaiki.Ubah posisi label sel
Saat ini, label UI untuk sel dibuat dan ditempatkan hanya satu kali, setelah itu kita melupakannya. Untuk memperbarui posisi vertikal mereka, kita perlu melacaknya. Mari beri semua orang HexCell
tautan ke RectTransform
label UI mereka sehingga Anda dapat memperbaruinya nanti. public RectTransform uiRect;
Tetapkan mereka di akhir HexGrid.CreateCell
. void CreateCell (int x, int z, int i) { โฆ cell.uiRect = label.rectTransform; }
Sekarang kita dapat memperluas properti HexCell.Elevation
sehingga juga mengubah posisi UI sel. Karena mesh kanvas hexagon diputar, label perlu dipindahkan ke arah negatif sepanjang sumbu Z, dan bukan di sisi positif dari sumbu Y. set { elevation = value; Vector3 position = transform.localPosition; position.y = value * HexMetrics.elevationStep; transform.localPosition = position; Vector3 uiPosition = uiRect.localPosition; uiPosition.z = elevation * -HexMetrics.elevationStep; uiRect.localPosition = uiPosition; }
Tag dengan tinggi.Pembuatan lereng
Sekarang kita perlu mengubah koneksi sel datar ke lereng. Ini dilakukan di HexMesh.TriangulateConnection
. Dalam kasus koneksi tepi, kita perlu mendefinisikan ulang ketinggian ujung jembatan yang lain. Vector3 bridge = HexMetrics.GetBridge(direction); Vector3 v3 = v1 + bridge; Vector3 v4 = v2 + bridge; v3.y = v4.y = neighbor.Elevation * HexMetrics.elevationStep;
Dalam hal sambungan sudut, kita perlu melakukan hal yang sama dengan jembatan ke tetangga berikutnya. if (direction <= HexDirection.E && nextNeighbor != null) { Vector3 v5 = v2 + HexMetrics.GetBridge(direction.Next()); v5.y = nextNeighbor.Elevation * HexMetrics.elevationStep; AddTriangle(v2, v4, v5); AddTriangleColor(cell.color, neighbor.color, nextNeighbor.color); }
Koneksi dengan mempertimbangkan tinggi akun.Sekarang kami memiliki dukungan untuk sel di ketinggian yang berbeda dengan sambungan miring yang benar di antara mereka. Tapi jangan berhenti di situ. Kami akan membuat lereng ini lebih menarik.paket unitySambungan iga dengan tepian
Lereng lurus tidak terlihat sangat menarik. Kami dapat membaginya menjadi beberapa langkah dengan menambahkan langkah-langkah. Pendekatan ini digunakan dalam game Endless Legend.Misalnya, kita dapat memasukkan dua tepian pada setiap kemiringan. Akibatnya, satu lereng besar berubah menjadi tiga yang kecil, di antaranya ada dua bidang datar. Untuk melakukan triangulasi skema semacam itu, kita harus memisahkan setiap koneksi dalam lima tahap.Dua tepian di lereng.Kami dapat mengatur jumlah langkah untuk kemiringan HexMetrics
dan menghitung jumlah tahapan berdasarkan ini. public const int terracesPerSlope = 2; public const int terraceSteps = terracesPerSlope * 2 + 1;
Idealnya, kita bisa menginterpolasi setiap langkah di sepanjang lereng. Tapi ini tidak sepenuhnya sepele, karena koordinat Y hanya boleh berubah pada tahap aneh. Kalau tidak, kita tidak akan mendapatkan tepian rata. Mari kita tambahkan metode interpolasi khusus untuk ini HexMetrics
. public static Vector3 TerraceLerp (Vector3 a, Vector3 b, int step) { return a; }
Interpolasi horisontal sederhana jika kita mengetahui ukuran langkah interpolasi. public const float horizontalTerraceStepSize = 1f / terraceSteps; public static Vector3 TerraceLerp (Vector3 a, Vector3 b, int step) { float h = step * HexMetrics.horizontalTerraceStepSize; ax += (bx - ax) * h; az += (bz - az) * h; return a; }
Bagaimana cara interpolasi antara dua nilai bekerja?a dan b t . t 0, a . 1, b . t - 0 1, a dan b . : (1โt)a+tb .
, (1โt)a+tb=aโta+tb=a+t(bโa) . a b (bโa) . , .
Untuk mengubah Y hanya pada tahap ganjil, kita bisa menggunakan ( s t e p + 1 ) / 2 .
Jika kita menggunakan pembagian integer, maka itu akan mengubah seri 1, 2, 3, 4 menjadi 1, 1, 2, 2. public const float verticalTerraceStepSize = 1f / (terracesPerSlope + 1); public static Vector3 TerraceLerp (Vector3 a, Vector3 b, int step) { float h = step * HexMetrics.horizontalTerraceStepSize; ax += (bx - ax) * h; az += (bz - az) * h; float v = ((step + 1) / 2) * HexMetrics.verticalTerraceStepSize; ay += (by - ay) * v; return a; }
Mari kita tambahkan metode untuk interpolasi birai untuk warna juga. Hanya interpolasi mereka seolah-olah koneksi datar. public static Color TerraceLerp (Color a, Color b, int step) { float h = step * HexMetrics.horizontalTerraceStepSize; return Color.Lerp(a, b, h); }
Triangulasi
Ketika triangulasi koneksi tepi menjadi lebih rumit, kami menghapus kode yang sesuai dari HexMesh.TriangulateConnection
dan menempatkannya dalam metode yang terpisah. Dalam komentar, saya akan menyimpan kode sumber untuk merujuknya di masa mendatang. void TriangulateConnection ( HexDirection direction, HexCell cell, Vector3 v1, Vector3 v2 ) { โฆ Vector3 bridge = HexMetrics.GetBridge(direction); Vector3 v3 = v1 + bridge; Vector3 v4 = v2 + bridge; v3.y = v4.y = neighbor.Elevation * HexMetrics.elevationStep; TriangulateEdgeTerraces(v1, v2, cell, v3, v4, neighbor);
Mari kita mulai dari langkah pertama dari proses. Kami akan menggunakan metode interpolasi khusus kami untuk membuat quad pertama. Dalam hal ini, kemiringan pendek harus dibuat, lebih curam dari yang asli. void TriangulateEdgeTerraces ( Vector3 beginLeft, Vector3 beginRight, HexCell beginCell, Vector3 endLeft, Vector3 endRight, HexCell endCell ) { Vector3 v3 = HexMetrics.TerraceLerp(beginLeft, endLeft, 1); Vector3 v4 = HexMetrics.TerraceLerp(beginRight, endRight, 1); Color c2 = HexMetrics.TerraceLerp(beginCell.color, endCell.color, 1); AddQuad(beginLeft, beginRight, v3, v4); AddQuadColor(beginCell.color, c2); }
Langkah pertama dalam membuat langkan.Sekarang kita akan segera melanjutkan ke tahap terakhir, melewatkan semuanya di antaranya. Ini akan melengkapi koneksi ujung-ujungnya, meski sejauh ini dengan bentuk yang tidak beraturan. AddQuad(beginLeft, beginRight, v3, v4); AddQuadColor(beginCell.color, c2); AddQuad(v3, v4, endLeft, endRight); AddQuadColor(c2, endCell.color);
Langkah terakhir dalam membuat langkan.Langkah menengah dapat ditambahkan melalui loop. Pada setiap tahap, dua simpul sebelumnya menjadi yang pertama. Hal yang sama berlaku untuk warna. Setelah menghitung vektor dan warna baru, quad lainnya ditambahkan. AddQuad(beginLeft, beginRight, v3, v4); AddQuadColor(beginCell.color, c2); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color c1 = c2; v3 = HexMetrics.TerraceLerp(beginLeft, endLeft, i); v4 = HexMetrics.TerraceLerp(beginRight, endRight, i); c2 = HexMetrics.TerraceLerp(beginCell.color, endCell.color, i); AddQuad(v1, v2, v3, v4); AddQuadColor(c1, c2); } AddQuad(v3, v4, endLeft, endRight); AddQuadColor(c2, endCell.color);
Semua langkah perantara.Sekarang semua sambungan tepi memiliki dua tepian, atau nomor lain yang Anda tentukan HexMetrics.terracesPerSlope
. Tentu saja, sampai kita telah membuat tepian untuk sambungan sudut, kita akan meninggalkan ini untuk nanti.Semua sambungan tepi memiliki tepian.paket unityJenis koneksi
Mengubah semua sambungan tepi ke tepian bukanlah ide yang bagus. Mereka hanya terlihat bagus ketika perbedaan ketinggian hanya satu tingkat. Tetapi dengan perbedaan yang lebih besar, tepian sempit dibuat dengan celah besar di antara mereka, dan ini tidak terlihat sangat indah. Selain itu, kita tidak perlu membuat tepian untuk semua sambungan.Mari kita meresmikan ini dan mendefinisikan tiga jenis tepi: pesawat, kemiringan, dan tebing. Mari kita membuat enumerasi untuk ini. public enum HexEdgeType { Flat, Slope, Cliff }
Bagaimana cara menentukan jenis koneksi yang kita hadapi? Untuk melakukan ini, kita dapat menambahkan HexMetrics
metode yang menggunakan dua tingkat ketinggian. public static HexEdgeType GetEdgeType (int elevation1, int elevation2) { }
Jika ketinggiannya sama, maka kita akan memiliki tulang rusuk yang rata. public static HexEdgeType GetEdgeType (int elevation1, int elevation2) { if (elevation1 == elevation2) { return HexEdgeType.Flat; } }
Jika perbedaan level sama dengan satu langkah, maka ini adalah kemiringan. Tidak masalah jika naik atau turun. Dalam semua kasus lain, kami mendapat istirahat. public static HexEdgeType GetEdgeType (int elevation1, int elevation2) { if (elevation1 == elevation2) { return HexEdgeType.Flat; } int delta = elevation2 - elevation1; if (delta == 1 || delta == -1) { return HexEdgeType.Slope; } return HexEdgeType.Cliff; }
Mari kita juga menambahkan metode yang nyaman HexCell.GetEdgeType
untuk mendapatkan tipe tepi sel dalam arah tertentu. public HexEdgeType GetEdgeType (HexDirection direction) { return HexMetrics.GetEdgeType( elevation, neighbors[(int)direction].elevation ); }
Tidakkah kita perlu memeriksa apakah ada tetangga di arah ini?, , . , NullReferenceException
. , , - . , . .
, , , . - , NullReferenceException
.
Buat tepian hanya untuk lereng
Sekarang kita dapat menentukan jenis koneksi, kita dapat memutuskan apakah akan memasukkan tepian. Ubah HexMesh.TriangulateConnection
sehingga ia membuat tepian hanya untuk lereng. if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(v1, v2, cell, v3, v4, neighbor); }
Pada titik ini, kita dapat menghapus komentar dari kode yang sebelumnya dikomentari sehingga dapat menangani pesawat dan kliping. if (cell.GetEdgeType(direction) == HexEdgeType.Slope) { TriangulateEdgeTerraces(v1, v2, cell, v3, v4, neighbor); } else { AddQuad(v1, v2, v3, v4); AddQuadColor(cell.color, neighbor.color); }
Langkah-langkah dibuat hanya di lereng.paket unityLangkan dengan tepian
Sambungan sudut lebih kompleks daripada sambungan tepi karena mereka terlibat bukan dalam dua, tetapi dalam tiga sel. Setiap sudut terhubung ke tiga tepi, yang bisa berupa bidang, lereng atau tebing. Karena itu, ada banyak kemungkinan konfigurasi. Seperti halnya tulang rusuk, lebih baik kita menambahkan HexMesh
triangulasi ke metode baru.Metode baru kami akan membutuhkan simpul sudut segitiga dan sel yang terhubung. Untuk kenyamanan, mari kita mengatur koneksi untuk mengetahui sel mana yang memiliki ketinggian terkecil. Setelah itu, kita bisa mulai bekerja dari kiri bawah dan kanan.Sambungan sudut. void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { AddTriangle(bottom, left, right); AddTriangleColor(bottomCell.color, leftCell.color, rightCell.color); }
Sekarang saya TriangulateConnection
harus menentukan sel mana yang paling rendah. Pertama kita periksa apakah sel triangulasi di bawah tetangganya atau pada tingkat yang sama dengan yang terendah. Jika demikian, maka kita dapat menggunakannya sebagai sel terendah. void TriangulateConnection ( HexDirection direction, HexCell cell, Vector3 v1, Vector3 v2 ) { โฆ HexCell nextNeighbor = cell.GetNeighbor(direction.Next()); if (direction <= HexDirection.E && nextNeighbor != null) { Vector3 v5 = v2 + HexMetrics.GetBridge(direction.Next()); v5.y = nextNeighbor.Elevation * HexMetrics.elevationStep; if (cell.Elevation <= neighbor.Elevation) { if (cell.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(v2, cell, v4, neighbor, v5, nextNeighbor); } } } }
Jika pemeriksaan terdalam gagal, maka ini berarti bahwa tetangga berikutnya adalah sel terendah. Untuk orientasi yang tepat, kita harus memutar segitiga berlawanan arah jarum jam. if (cell.Elevation <= neighbor.Elevation) { if (cell.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(v2, cell, v4, neighbor, v5, nextNeighbor); } else { TriangulateCorner(v5, nextNeighbor, v2, cell, v4, neighbor); } }
Jika tes pertama gagal, maka Anda perlu membandingkan dua sel tetangga. Jika tetangga tulang rusuk adalah yang terendah, maka Anda perlu mengubah arah jarum jam, jika tidak - berlawanan arah jarum jam. if (cell.Elevation <= neighbor.Elevation) { if (cell.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(v2, cell, v4, neighbor, v5, nextNeighbor); } else { TriangulateCorner(v5, nextNeighbor, v2, cell, v4, neighbor); } } else if (neighbor.Elevation <= nextNeighbor.Elevation) { TriangulateCorner(v4, neighbor, v5, nextNeighbor, v2, cell); } else { TriangulateCorner(v5, nextNeighbor, v2, cell, v4, neighbor); }
Putar berlawanan arah jarum jam, tanpa putaran, rotasi searah jarum jam.Triangulasi Lereng
Untuk mengetahui cara melakukan triangulasi sudut, kita perlu memahami jenis tepian yang kita hadapi. Untuk menyederhanakan tugas ini, mari kita tambahkan ke HexCell
metode lain yang mudah untuk mengenali kemiringan antara dua sel. public HexEdgeType GetEdgeType (HexCell otherCell) { return HexMetrics.GetEdgeType( elevation, otherCell.elevation ); }
Kami menggunakan metode baru ini HexMesh.TriangulateCorner
untuk menentukan jenis tepi kiri dan kanan. void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { HexEdgeType leftEdgeType = bottomCell.GetEdgeType(leftCell); HexEdgeType rightEdgeType = bottomCell.GetEdgeType(rightCell); AddTriangle(bottom, left, right); AddTriangleColor(bottomCell.color, leftCell.color, rightCell.color); }
Jika kedua tulang rusuk adalah lereng, maka kita akan memiliki tepian di kiri dan kanan. Selain itu, karena sel bawah adalah yang terendah, kita tahu bahwa lereng ini naik. Selain itu, sel-sel kiri dan kanan memiliki ketinggian yang sama, yaitu koneksi tepi atas rata. Kami dapat menunjuk kasus ini sebagai "slope-slope-plane", atau MTP.Dua lereng dan sebuah pesawat, SSP.Kami akan memeriksa apakah kami berada dalam situasi ini, dan jika demikian, maka kami akan memanggil metode baru TriangulateCornerTerraces
. Setelah itu, kita akan kembali dari metode. Masukkan cek ini sebelum kode triangulasi lama sehingga menggantikan segitiga asli. void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { HexEdgeType leftEdgeType = bottomCell.GetEdgeType(leftCell); HexEdgeType rightEdgeType = bottomCell.GetEdgeType(rightCell); if (leftEdgeType == HexEdgeType.Slope) { if (rightEdgeType == HexEdgeType.Slope) { TriangulateCornerTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); return; } } AddTriangle(bottom, left, right); AddTriangleColor(bottomCell.color, leftCell.color, rightCell.color); } void TriangulateCornerTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { }
Karena kita tidak melakukan apapun di dalam TriangulateCornerTerraces
, beberapa persimpangan sudut dengan dua lereng akan menjadi kosong. Apakah koneksi menjadi kosong atau tidak tergantung pada sel mana yang lebih rendah.Ada kekosongan.Untuk mengisi kekosongan, kita perlu menghubungkan langkan kiri dan kanan melalui spasi. Pendekatan di sini sama dengan untuk menggabungkan tepi, tetapi di dalam segitiga tiga warna, bukan segi empat dua warna. Mari kita mulai lagi dengan tahap pertama, yang sekarang menjadi segitiga. void TriangulateCornerTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { Vector3 v3 = HexMetrics.TerraceLerp(begin, left, 1); Vector3 v4 = HexMetrics.TerraceLerp(begin, right, 1); Color c3 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, 1); Color c4 = HexMetrics.TerraceLerp(beginCell.color, rightCell.color, 1); AddTriangle(begin, v3, v4); AddTriangleColor(beginCell.color, c3, c4); }
Tahap pertama dari segitiga.Dan kita kembali ke tahap terakhir. Ini adalah segi empat yang membentuk trapesium. Satu-satunya perbedaan dari koneksi edge di sini adalah bahwa kita tidak berurusan dengan dua, tetapi dengan empat warna. AddTriangle(begin, v3, v4); AddTriangleColor(beginCell.color, c3, c4); AddQuad(v3, v4, left, right); AddQuadColor(c3, c4, leftCell.color, rightCell.color);
Tahap terakhir dari segi empat.Semua tahapan di antara mereka juga empat persegi panjang. AddTriangle(begin, v3, v4); AddTriangleColor(beginCell.color, c3, c4); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v3; Vector3 v2 = v4; Color c1 = c3; Color c2 = c4; v3 = HexMetrics.TerraceLerp(begin, left, i); v4 = HexMetrics.TerraceLerp(begin, right, i); c3 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, i); c4 = HexMetrics.TerraceLerp(beginCell.color, rightCell.color, i); AddQuad(v1, v2, v3, v4); AddQuadColor(c1, c2, c3, c4); } AddQuad(v3, v4, left, right); AddQuadColor(c3, c4, leftCell.color, rightCell.color);
Semua tahapan.Dua Variasi Kemiringan
Kasing dengan dua lereng memiliki dua variasi dengan orientasi yang berbeda, tergantung pada sel mana yang berada di bawah. Kita dapat menemukannya dengan memeriksa kombinasi kiri-kanan untuk bidang miring dan bidang miring.ATP dan MSS.Jika tepi kanan rata, maka kita harus mulai membuat tepian di sebelah kiri, dan bukan di bawah. Jika tepi kiri rata, maka Anda harus mulai dari kanan. if (leftEdgeType == HexEdgeType.Slope) { if (rightEdgeType == HexEdgeType.Slope) { TriangulateCornerTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); return; } if (rightEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( left, leftCell, right, rightCell, bottom, bottomCell ); return; } } if (rightEdgeType == HexEdgeType.Slope) { if (leftEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); return; } }
Karena ini, tepian akan mengelilingi sel tanpa gangguan sampai mereka mencapai tebing atau ujung peta.Tepian yang solid.paket unityPenggabungan lereng dan tebing
Bagaimana dengan menghubungkan lereng dan tebing? Jika kita tahu bahwa tepi kiri adalah kemiringan dan tepi kanan adalah tebing, lalu apa yang akan menjadi tepi atas? Itu tidak bisa datar, tetapi bisa berupa lereng atau tebing.SOS dan COO.Mari kita tambahkan metode baru untuk menangani semua kasus lereng-tebing. void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { }
Ini harus disebut sebagai opsi terakhir TriangulateCorner
ketika tepi kiri adalah kemiringan. if (leftEdgeType == HexEdgeType.Slope) { if (rightEdgeType == HexEdgeType.Slope) { TriangulateCornerTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); return; } if (rightEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( left, leftCell, right, rightCell, bottom, bottomCell ); return; } TriangulateCornerTerracesCliff( bottom, bottomCell, left, leftCell, right, rightCell ); return; } if (rightEdgeType == HexEdgeType.Slope) { if (leftEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); return; } }
Bagaimana kita melakukan pelacakan ini? Tugas ini dapat dibagi menjadi dua bagian: bawah dan atas.Bagian bawah
Bagian bawah memiliki tepian di sebelah kiri dan tebing di sebelah kanan. Kita perlu menggabungkan keduanya. Cara termudah untuk melakukan ini adalah dengan menekan tepian sehingga mereka bertemu di sudut kanan. Ini akan menaikkan tepian.Kompresi tepian.Tetapi pada kenyataannya, kami tidak ingin mereka bertemu di sudut kanan, karena ini akan mengganggu tepian yang mungkin ada di atas. Selain itu, kita bisa berurusan dengan tebing yang sangat tinggi, yang dengannya kita menjadi sangat tajam dan segitiga yang tipis. Sebagai gantinya, kami akan memampatkan mereka ke titik batas yang terletak di sepanjang tebing.Kompresi di perbatasan.Mari kita posisi titik batas satu tingkat di atas sel bawah. Anda dapat menemukannya dengan interpolasi berdasarkan perbedaan ketinggian. void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (rightCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, right, b); Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b); }
Untuk memastikan bahwa kami mendapatkannya dengan benar, kami menutupi seluruh bagian bawah dengan satu segitiga. float b = 1f / (rightCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, right, b); Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b); AddTriangle(begin, left, boundary); AddTriangleColor(beginCell.color, leftCell.color, boundaryColor);
Segitiga bawah.Setelah menempatkan perbatasan di tempat yang tepat, kita dapat melanjutkan ke triangulasi tepian. Mari kita mulai lagi hanya dari tahap pertama. float b = 1f / (rightCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, right, b); Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b); Vector3 v2 = HexMetrics.TerraceLerp(begin, left, 1); Color c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, 1); AddTriangle(begin, v2, boundary); AddTriangleColor(beginCell.color, c2, boundaryColor);
Tahap pertama kompresi.Kali ini, tahap terakhir juga akan menjadi segitiga. AddTriangle(begin, v2, boundary); AddTriangleColor(beginCell.color, c2, boundaryColor); AddTriangle(v2, left, boundary); AddTriangleColor(c2, leftCell.color, boundaryColor);
Tahap terakhir dari kompresi.Dan semua langkah menengah juga segitiga. AddTriangle(begin, v2, boundary); AddTriangleColor(beginCell.color, c2, boundaryColor); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v2; Color c1 = c2; v2 = HexMetrics.TerraceLerp(begin, left, i); c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, i); AddTriangle(v1, v2, boundary); AddTriangleColor(c1, c2, boundaryColor); } AddTriangle(v2, left, boundary); AddTriangleColor(c2, leftCell.color, boundaryColor);
Tepian terkompresi.Bisakah kita menjaga tingkat langkan?, , , . . , . .
Penyelesaian sudut
Setelah selesai bagian bawah, Anda bisa pergi ke atas. Jika tepi atas adalah kemiringan, maka sekali lagi kita perlu menghubungkan tepian dan tebing. Jadi mari kita pindahkan kode ini ke metode terpisah. void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (rightCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, right, b); Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b); TriangulateBoundaryTriangle( begin, beginCell, left, leftCell, boundary, boundaryColor ); } void TriangulateBoundaryTriangle ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 boundary, Color boundaryColor ) { Vector3 v2 = HexMetrics.TerraceLerp(begin, left, 1); Color c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, 1); AddTriangle(begin, v2, boundary); AddTriangleColor(beginCell.color, c2, boundaryColor); for (int i = 2; i < HexMetrics.terraceSteps; i++) { Vector3 v1 = v2; Color c1 = c2; v2 = HexMetrics.TerraceLerp(begin, left, i); c2 = HexMetrics.TerraceLerp(beginCell.color, leftCell.color, i); AddTriangle(v1, v2, boundary); AddTriangleColor(c1, c2, boundaryColor); } AddTriangle(v2, left, boundary); AddTriangleColor(c2, leftCell.color, boundaryColor); }
Sekarang menyelesaikan bagian atas akan mudah. Jika kita memiliki kemiringan, tambahkan segitiga yang diputar dari perbatasan. Kalau tidak, segitiga sederhana sudah cukup. void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (rightCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, right, b); Color boundaryColor = Color.Lerp(beginCell.color, rightCell.color, b); TriangulateBoundaryTriangle( begin, beginCell, left, leftCell, boundary, boundaryColor ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, leftCell, right, rightCell, boundary, boundaryColor ); } else { AddTriangle(left, right, boundary); AddTriangleColor(leftCell.color, rightCell.color, boundaryColor); } }
Triangulasi lengkap kedua bagian.Kasing Cermin
Kami memeriksa kasus-kasus "lereng-tebing". Ada juga dua kotak cermin, masing-masing memiliki tebing di sebelah kiri.OSS dan CCA.Kami akan menggunakan pendekatan sebelumnya, dengan sedikit perbedaan karena perubahan orientasi. Kami menyalin TriangulateCornerTerracesCliff
dan mengubahnya sesuai. void TriangulateCornerCliffTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (leftCell.Elevation - beginCell.Elevation); Vector3 boundary = Vector3.Lerp(begin, left, b); Color boundaryColor = Color.Lerp(beginCell.color, leftCell.color, b); TriangulateBoundaryTriangle( right, rightCell, begin, beginCell, boundary, boundaryColor ); if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { TriangulateBoundaryTriangle( left, leftCell, right, rightCell, boundary, boundaryColor ); } else { AddTriangle(left, right, boundary); AddTriangleColor(leftCell.color, rightCell.color, boundaryColor); } }
Tambahkan kasus ini ke TriangulateCorner
. if (leftEdgeType == HexEdgeType.Slope) { โฆ } if (rightEdgeType == HexEdgeType.Slope) { if (leftEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); return; } TriangulateCornerCliffTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); return; }
OSS dan CCA triangulasi.Tebing ganda
Satu-satunya kasus nonplanar yang tersisa adalah sel bawah dengan tebing di kedua sisi. Dalam hal ini, tulang rusuk bagian atas dapat berupa apa saja - rata, miring atau tebing. Kami hanya tertarik pada kasus "tebing-tebing-lereng", karena hanya akan memiliki tepian.Faktanya, ada dua versi berbeda dari "cliff-cliff-slope", tergantung pada sisi mana yang lebih tinggi. Mereka adalah gambar cermin satu sama lain. Mari kita tunjuk sebagai OOSP dan OOSL.OOSP dan OOSL.Kita dapat membahas kedua kasus TriangulateCorner
dengan memanggil metode TriangulateCornerCliffTerraces
dan TriangulateCornerTerracesCliff
dengan rotasi sel yang berbeda. if (leftEdgeType == HexEdgeType.Slope) { โฆ } if (rightEdgeType == HexEdgeType.Slope) { โฆ } if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { if (leftCell.Elevation < rightCell.Elevation) { TriangulateCornerCliffTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); } else { TriangulateCornerTerracesCliff( left, leftCell, right, rightCell, bottom, bottomCell ); } return; }
Namun, ini menciptakan triangulasi yang aneh. Ini karena sekarang kita melakukan triangulasi dari atas ke bawah. Karena itu, perbatasan kami diinterpolasi sebagai negatif, yang salah. Solusinya di sini adalah untuk selalu memiliki interpolator positif. void TriangulateCornerTerracesCliff ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (rightCell.Elevation - beginCell.Elevation); if (b < 0) { b = -b; } โฆ } void TriangulateCornerCliffTerraces ( Vector3 begin, HexCell beginCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { float b = 1f / (leftCell.Elevation - beginCell.Elevation); if (b < 0) { b = -b; } โฆ }
Triangulasi OOSP dan OOSL.Sapu
Kami memeriksa semua kasus yang memerlukan penanganan khusus untuk memastikan triangulasi tepian yang benar.Triangulasi lengkap dengan tepian.Kami dapat membersihkan sedikit dengan TriangulateCorner
menyingkirkan operator return
dan menggunakan blok sebagai gantinya else
. void TriangulateCorner ( Vector3 bottom, HexCell bottomCell, Vector3 left, HexCell leftCell, Vector3 right, HexCell rightCell ) { HexEdgeType leftEdgeType = bottomCell.GetEdgeType(leftCell); HexEdgeType rightEdgeType = bottomCell.GetEdgeType(rightCell); if (leftEdgeType == HexEdgeType.Slope) { if (rightEdgeType == HexEdgeType.Slope) { TriangulateCornerTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); } else if (rightEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( left, leftCell, right, rightCell, bottom, bottomCell ); } else { TriangulateCornerTerracesCliff( bottom, bottomCell, left, leftCell, right, rightCell ); } } else if (rightEdgeType == HexEdgeType.Slope) { if (leftEdgeType == HexEdgeType.Flat) { TriangulateCornerTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); } else { TriangulateCornerCliffTerraces( bottom, bottomCell, left, leftCell, right, rightCell ); } } else if (leftCell.GetEdgeType(rightCell) == HexEdgeType.Slope) { if (leftCell.Elevation < rightCell.Elevation) { TriangulateCornerCliffTerraces( right, rightCell, bottom, bottomCell, left, leftCell ); } else { TriangulateCornerTerracesCliff( left, leftCell, right, rightCell, bottom, bottomCell ); } } else { AddTriangle(bottom, left, right); AddTriangleColor(bottomCell.color, leftCell.color, rightCell.color); } }
Blok terakhir else
mencakup semua kasing yang belum ditutup. Kasus-kasus ini adalah RFP (pesawat-pesawat-pesawat), OOP, LLC dan LLC. Semuanya ditutupi oleh satu segitiga..unitypackage