Bagaimana saya membuat peta benua untuk permainan saya

gambar

Bagian 1. SVG dan sistem koordinat


Sampai baru-baru ini, ukuran peta di game Dragons Abound saya sudah diperbaiki dan agak non-deterministik. Saya menganggap mereka "regional" - bukan peta seluruh dunia, tetapi bagian-bagian penting, seperti, misalnya, pantai barat Amerika Serikat atau bagian Eropa. Saya cukup senang dengan skala ini, tetapi saya ingin sedikit bereksperimen dengan permainan untuk melihat apakah saya dapat menghasilkan peta seluruh dunia (atau setidaknya yang lebih besar). Tapi sebelum saya mulai, mari kita bicara sedikit tentang peta dunia fantasi.

Dunia adalah ruang besar. Kebanyakan kartu "dunia" fantasi bahkan tidak mirip dengan ukuran sebenarnya. Ambil contoh, Middle-earth, tempat aksi Lord of the Rings terjadi:


Meskipun tampaknya dunia yang besar ditangkap, nyatanya, Dunia Tengah diciptakan atas dasar Eropa.


Yaitu, peta sebenarnya dari "dunia" untuk dunia Tolkien akan menjadi sekitar 50 kali lebih besar dari peta Dunia Tengah (!). Bahkan, sebagian besar peta dunia fantasi yang saya lihat mencerminkan suatu wilayah seukuran benua:


Ini tampaknya menjadi area terbesar yang divisualisasikan dengan baik dalam gaya kartu fantasi.

Artinya, tugas menghasilkan "peta dunia" nyata mungkin terlalu ambisius. Lebih baik bertujuan membuat peta benua (atau bagian dari benua). (Namun, masih lebih nyaman untuk menganggap kartu sebagai ukuran "dunia".) Jadi, ukuran apa kartu itu? Jika kartu Dragons Abound saat ini memiliki ukuran “subkontinental”, maka kita dapat mengasumsikan bahwa Anda perlu membuat kartu 8-10 kali lebih besar.

Sebelum saya beralih ke tugas menghasilkan peta besar, saya perlu lebih memahami berbagai sistem koordinat yang digunakan dalam game saya. Saya meminjam banyak sistem koordinat dari kode sumber Martin O'Leary, dan interaksinya dapat membingungkan bahkan ketika Anda bekerja dengan mereka selama dua tahun. Biasanya saya bisa melakukan tanpa bereksperimen dengan mereka, tetapi jelas bahwa saya harus melakukan ini untuk menghasilkan peta besar.

Untuk mulai dengan, "dunia" peta regional saat ini sedang dihasilkan dalam satuan persegi. Setiap peta regional memiliki koordinat dari (-0,5, -0,5) hingga (0,5, 0,5), dan titik asal (0,0) ada di tengah-tengah wilayah.


Salah satu keanehan di sini adalah bahwa sumbu Y terbalik dibandingkan dengan apa yang kami pelajari dalam geometri sekolah. -0,5 di bagian atas peta, dan 0,5 di bagian bawah. Dalam grafik komputer, sumbu Y sering terbalik. Saya mendengar bahwa ini dijelaskan dengan cara monitor pertama (TV) melakukan pemindaian dari atas ke bawah, yaitu, garis pemindaian pertama berada di atas, yang berikutnya tepat di bawahnya, dan seterusnya, yaitu, indeks Y dari garis pemindaian berubah dari nol di atas ke beberapa angka positif di bawah. Bagaimanapun, sistem koordinat yang sama digunakan dalam format SVG (Scalable Vector Graphics), itulah sebabnya Naga juga Berlimpah .

Sistem koordinat ini tidak tergantung pada bagaimana peta ditampilkan. Ini hanya sistem tanpa dimensi untuk menciptakan dunia - kota ini berada di (0,12875, -0.223), perbatasan melewati dari (0,337, 0,010) menjadi (0,333, 0,017), dan seterusnya. Dan meskipun peta regional saya saat ini terbatas pada kisaran 0,5 hingga -0,5, ini bukan batas sistem koordinat. Saya dapat menciptakan dunia di luar batas-batas ini.

Sistem koordinat berikutnya adalah apa yang disebut SVG viewbox. Ini mengatur koordinat sebenarnya yang akan digunakan untuk menggambar grafik. Sebagai contoh, Dragons Abound di awal menetapkan viewbox ke koordinat (-500, -500) dan memiliki lebar 1000 dan tinggi 1000:


(Ada kesalahan ketik pada gambar, bagian atas sumbu Y harus -500, maaf.)

Anda mungkin memperhatikan bahwa dalam kasus ini, transformasi antara sistem koordinat pertama dan kedua hanya terdiri dari mengalikan semua dengan 1000. Yaitu. untuk menggambar sesuatu, gim menemukan koordinat objek ini, mengalikannya dengan 1000, dan menggambar SVG dalam koordinat ini.

Yaitu, saya bisa menggunakan koordinat viewbox untuk memungkinkan saya menggambar garis dari (0, 0) hingga (250, 250). Tetapi pada kenyataannya, saya tidak ingin menggambar garis dari (0, 0) hingga (250, 250) di layar komputer. Ini berarti bahwa jika saya ingin menampilkan peta di titik lain di layar, maka saya harus mengubah koordinat semua objek peta dan menggambar ulang mereka. Itu akan menjadi pekerjaan besar.

Untuk mengontrol koordinat tampilan grafik di layar, SVG memiliki sistem koordinat ketiga yang disebut viewport. Viewport adalah bagian halaman di mana gambar harus digambar (di halaman web, ini adalah elemen <svg>). Ia memiliki lebar, tinggi, dan lokasi. Lokasi adalah koordinat sudut kiri atas viewport. Artinya, jika saya akan menampilkan peta di viewport dengan koordinat (30, 100), yang memiliki tinggi dan lebar 800, maka sistem koordinat viewport akan terlihat seperti ini:


Dalam SVG, viewbox sistem koordinat dan viewport terhubung satu sama lain, dan SVG sendiri berurusan dengan transisi di antara mereka. Kami cukup menggambar di sistem koordinat viewbox, dan semua yang ditampilkan ditampilkan di lokasi viewport yang sesuai. (Beberapa masalah muncul saat membuat viewbox dan viewport dengan rasio aspek yang berbeda. Kemudian objek dipotong atau diregangkan, tergantung pada nilai atribut preserveAspectRatio . Saya sarankan untuk tidak melakukan ini sama sekali.)

Ringkasnya: sebuah kota yang terletak di koordinat dunia (0,10, 0,33) digambarkan dalam koordinat (100, 330) dan ditampilkan di layar dalam (110, 764).

Sekarang Anda bisa mengerti mengapa ini bisa membingungkan!

Apa yang terjadi jika saya mengubah masing-masing sistem koordinat ini? Misalkan dalam sistem koordinat pertama saya akan menghasilkan dunia mulai dari -0,25 hingga 0,25 pada setiap sumbu. Maka dunia yang dihasilkan akan empat kali lebih kecil dari dunia yang biasa dan hanya mengisi bagian tengah viewport:


(Anda juga dapat melihat artefak di tepian yang biasanya disembunyikan.) Demikian pula, jika saya menggandakan ukuran sistem koordinat pertama (SK), kami tidak akan melihat sebagian besar peta, karena itu akan berada di luar tepi viewport.

Apa yang terjadi jika saya menggandakan ukuran viewbox? Nah, jika saya juga menggandakan rasio antara SC pertama dan viewbox (1000-2000), maka tidak ada yang akan banyak berubah. Jika rasio tetap sama dengan 1000, maka peta akan dibelah dua lagi.


Namun, kali ini kartu memiliki luas awal 1x1. Kita dapat kembali melihat artefak di tepian yang biasanya disembunyikan (misalnya, bagian hutan yang cembung). Anda juga dapat melihat bahwa pola samudra salah - saya pasti memiliki beberapa asumsi tentang ukuran kotak view. Selain itu, tampaknya kompas tidak terletak di sudut peta, tetapi di sudut kotak tampilan.

Dan sebaliknya, jika saya mengurangi ukuran viewbox menjadi setengahnya, ini akan membuat efek zoom peta:


Di sini kita hanya melihat seperempat tengah peta. Ini bukan cara yang mudah untuk memperbesar, karena hanya setengah dari peta yang menampilkan beberapa masalah - misalnya, penanda kota "South Owenson" melampaui layar. Selain itu, itu menggandakan ukuran font dan hal-hal lain yang tidak saya butuhkan.

Aspek yang lebih berguna dari kotak tampilan mengubah asal. Sejauh ini, kotak tampilan telah dipusatkan di peta, tetapi ini tidak perlu. Misalnya, saya bisa memindahkan peta ke kanan dengan memusatkan kotak view pada titik di sisi kiri peta:


Kita dapat kembali melihat efek pada perbatasan dan beberapa masalah lain, tetapi pada dasarnya peta telah bergerak ke kanan. Kegunaannya mungkin tidak jelas, tetapi bayangkan saya akan menghasilkan peta yang lebarnya dua kali lipat dari peta biasa. Secara default, tampilannya seperti ini:


Itu terlihat seperti peta lain, tetapi pada kenyataannya itu hanya bagian tengah dari peta yang lebih besar. Yaitu, sekarang saya dapat mengubah kotak tampilan untuk mentransfer bagian lain peta ke jendela ikhtisar:


Di sini saya memindahkan sudut pandang ke kiri, jadi kita melihat bagian dunia di sebelah timur dari tampilan asli. Beberapa nama di peta telah berubah karena Dragons Abound melakukan fungsi tertentu (misalnya, memberikan nama ke objek) berdasarkan apakah mereka terlihat. Saya akan perlu mengubah ini sehingga ketika memindahkan kotak centang peta tetap konstan. Namun, nantinya saya akan dapat memindahkan viewbox pada peta besar dan menghasilkan peta regional dari area yang diinginkan. Yaitu, saya bisa membuat dan menampilkan peta besar seukuran benua, tetapi saya juga bisa menghasilkan peta regional area dalam peta besar.

Untuk meringkas: permainan menggunakan tiga sistem koordinat. Yang pertama adalah SC abstrak untuk objek-objek dunia. Yang kedua adalah viewbox, yang mendefinisikan area yang terlihat di dunia. Yang ketiga adalah viewport, itu mengontrol di mana peta akan digambar di layar. Untuk menggambar dunia yang lebih besar, saya perlu memperluas SC pertama. Untuk menampilkan lebih banyak di layar, Anda perlu memperluas kotak tampilan. Saya juga dapat memindahkan kotak tampilan untuk menampilkan berbagai bagian dunia besar.

Bagian 2. Membuat kartu permanen


Pada bagian sebelumnya, saya menjelajahi sistem koordinat dan belajar cara menggerakkan viewport SVG untuk hanya menampilkan bagian individual dari dunia besar. Namun, pendekatan ini memiliki beberapa masalah, karena sebelumnya saya berasumsi bahwa segala sesuatu yang tidak terlihat oleh kita tidak masalah. Pada bagian ini, saya akan menghilangkan asumsi-asumsi ini sehingga memungkinkan untuk menghasilkan dan melihat berbagai bagian peta besar yang tidak dapat diubah.

Masalah dengan penempatan nama, yang saya jelaskan di bagian sebelumnya, jelas terlihat pada dua jenis satu kartu ini:


Di sini adalah dunia yang sama, hanya pandangan yang bergeser ke kiri:


Anda mungkin memperhatikan bahwa geografinya sama, tetapi banyak nama telah berubah. Karena dalam dua jenis objek yang berbeda terlihat, dan proses pembuatan nama dikendalikan oleh angka acak, nama yang berbeda diperoleh.

Melihat kode itu, saya menemukan bahwa hampir semua objek diberi nama tergantung pada visibilitasnya. Tetapi hanya ada satu pengecualian yang membuatnya sulit untuk memberi nama ke semua objek berikutnya. Dalam kasus kami, pengecualian adalah Dragons Abound hanya menghasilkan garis pantai yang terlihat. Alasannya sangat membingungkan. Sebenarnya, ada "garis pantai" di sepanjang seluruh tepi dunia, tetapi penciptaan garis ini menghancurkan bagian dari logika program, karena ia merangkul seluruh dunia. Untuk menghindarinya, saya hanya membuat garis pantai yang terlihat saja. Sekarang karena peta dapat meluas jauh melampaui viewport, solusi ini tidak terlihat bagus. Sebagai gantinya, saya harus berhenti membuat garis pantai ketika saya semakin dekat ke tepi sebenarnya dari peta. (Yang saya masih meninggalkan layar untuk menyembunyikan masalah di tepinya.)

Setelah menghilangkan masalah ini, nama-nama pada kedua kartu tetap konstan:


dan:


Catatan lain untuk masa depan: jika saya menggunakan fitur interaktivitas untuk mengubah nama-nama objek peta, perubahan ini tidak akan dibuat ulang dalam bentuk saat ini, dan mungkin bahkan tidak akan dapat direproduksi. Ini layak dipertimbangkan.

Jika Anda melihat lebih dekat pada peta sebelumnya, Anda dapat melihat bahwa wilayah laut di dekat bagian tengah bawah peta memiliki label menggantung "Pulau Meb". Itu terjadi karena Dragons Abound sebenarnya percaya bahwa wilayah lautan adalah sebuah pulau. Saya tidak akan membahas perincian teknis, tetapi sangat sulit untuk membedakan pulau dari danau ketika mereka melampaui peta. Algoritma ini membingungkan terhadap perubahan yang saya buat pada generasi garis pantai yang tak terlihat, dan untuk menghindari masalah seperti itu, ini perlu diperbaiki.

Sekarang, mari kita lipat empat ukuran peta dan perlihatkan hanya seperempatnya di jendela peta:


Secara umum, semuanya terlihat baik (ada sistem sungai yang menarik di peta, sebuah danau besar, tetapi Anda dapat melihat bahwa kota sangat langka. Itu terjadi karena Dragons Abound menghasilkan 10-20 kota. Interval ini sangat cocok untuk dunia berukuran normal, tetapi buruk, ketika ukurannya empat kali lebih besar. Oleh karena itu, intervalnya perlu diubah sesuai dengan ukuran relatif dunia. Mungkin, perlu dilakukan di beberapa tempat.

Ini kartu yang sama setelah memperbaiki masalah:


Sekarang di peta ada jumlah yang lebih logis dari kota-kota, tetapi ini menunjukkan kita masalah lain. Anda dapat melihat banyak nama tambahan di tepi peta, misalnya, Nanmrummombrook, Marwynnley dan Noyewood di sudut kiri bawah. Ini terjadi karena metode kode penempatan mencoba menempatkannya di tempat yang terlihat. Sebelumnya, prosedur ini tidak pernah perlu khawatir tentang label di luar layar, karena dalam peta ukuran regional seluruh dunia biasanya terlihat. Tapi sekarang mungkin ada kota dan benda lain yang terletak di luar layar. Oleh karena itu, saya perlu menambahkan logika ke prosedur penempatan label yang tidak mencoba membuat label untuk objek peta yang tidak terlihat.


Sekarang gambarnya lebih logis. Di sebelah kanan, Cumden hampir tidak terlihat di peta, tetapi label masih terletak di tempat itu terlihat.

Ada satu aspek yang tidak langsung terlihat pada peta besar: jumlah lokasi di dunia tidak berubah. Meskipun peta (dalam arti tertentu) telah menjadi 4 kali lebih besar, luas totalnya masih dibatasi oleh jumlah lokasi yang sama. Tahap awal pembuatan peta adalah untuk menutupi dunia dengan diagram Voronoi dengan jumlah lokasi yang konstan. Artinya, ketika peta menjadi lebih besar, sel-sel Voronoi juga menjadi lebih besar.

Akan logis untuk skala jumlah lokasi sesuai dengan ukuran peta, tetapi sayangnya, ketergantungan kecepatan eksekusi Naga Berlimpah pada jumlah lokasi jauh lebih buruk daripada linear, yaitu, menghasilkan peta dengan sejumlah besar lokasi dapat memakan banyak waktu. Berikut adalah contoh kartu dengan resolusi quad (jumlah lokasi):


Lokasi yang ditambahkan mengubah proses pembuatan, sehingga medannya berbeda dari peta yang ditunjukkan di atas, tetapi di atasnya Anda dapat melihat detail tambahan di pantai.

Untungnya, ketika memprofilkan kinerja menghasilkan kartu besar, saya perhatikan bahwa sebagian besar waktu prosesor dihabiskan oleh masalah yang jelas. Setelah debugging, saya menghilangkan yang paling mengganggu, yang memungkinkan saya untuk membuat peta lebih banyak lagi. Ini seluruh kartu 4x:


Zoom 25% dilakukan. Sepertinya ukuran peta maksimum yang dapat ditampilkan Chrome. Prosedur generasi dunia dapat memproses peta yang lebih besar, tetapi mencoba menampilkannya, peramban mogok. Dalam hal ini, Firefox tampaknya lebih fungsional; Ini dapat menampilkan kartu 9 kali lebih besar dari ukuran aslinya. Ini adalah bagian dari peta seperti itu - saya meninggalkannya dalam ukuran penuh, sehingga Anda dapat membukanya di jendela terpisah untuk lebih memahami ukuran dan detailnya.


Firefox mampu menghasilkan peta dengan ukuran ini, tetapi saya hanya bisa mengambil tangkapan layar pada ukuran maksimum jendela browser. Saya memiliki fungsi untuk menyimpan peta sebagai file PNG, tetapi hanya dapat menyimpan bagian peta yang ditampilkan. Saya pikir Anda dapat menggulir peta, menangkap layar individual dan menghubungkannya bersama-sama, tetapi itu akan memakan waktu.

Solusi terbaik adalah menyimpan SVG itu sendiri sehingga dapat dibuka di program seperti Inkscape.

Saya dulu bisa memotong dan menempelkan peta SVG ke Inkscape, tetapi SVG untuk peta dunia sangat besar sehingga ketika saya mencoba untuk memotong browser crash! Untungnya, saya menemukan FileSaver.js dan Anda dapat menggunakannya untuk menyimpan SVG langsung ke file, dan kemudian membukanya di Inkscape, sehingga menciptakan gambar yang sangat besar.

Setidaknya secara teoritis. Ketika saya mencoba membuka peta ini di Inkscape, saya menemukan beberapa masalah.

Masalah pertama adalah bahwa asumsi Inkscape berbeda dari asumsi Chrome dan Firefox dalam cara membuka SVG. Secara khusus, jika warna isian tidak ditentukan dalam jalur, maka peramban menganggap bahwa tidak ada isian; Inkscape menganggap garis besar diisi dengan hitam. Karena itu, ketika saya membuka SVG yang disimpan di Inkscape, itu hampir sepenuhnya hitam, karena lapisan paling atas dari peta tidak mengandung warna isian. Ini dapat diperbaiki dengan menentukan "fill: none" di tempat yang diperlukan sehingga garis besar tampak sama di kedua browser dan Inkscape.

Masalah kedua adalah bahwa Inkscape memiliki kesalahan dalam memproses topeng. Inkscape tampaknya membuat topeng dengan hanya satu elemen, dan menangani topeng dengan beberapa elemen dengan buruk. Dragons Abound menciptakan banyak topeng dengan beberapa elemen. Anda dapat mengatasi masalah ini dengan mengelompokkan semua elemen dari setiap topeng game ke dalam satu elemen grup (opsional).

Masalah ketiga terkait dengan gambar dan sumber daya lainnya yang dapat diunduh. Dalam SVG asli, referensi untuk mereka ditunjukkan dalam bentuk relatif, misalnya, "gambar / background0.png". Sumber saya diatur sedemikian rupa sehingga server web terpisah yang saya gunakan dapat menemukan sumber daya ini di tempat yang ditentukan. Ketika saya mengambil SVG yang sama dan membukanya di Inkscape, jalur relatif ini diperlakukan sebagai URL "file:" dan Inkscape mencari sumber daya relatif ke folder tempat SVG disimpan. Masalah ini dapat dengan mudah dielakkan dengan menyimpan SVG ke folder di mana sudah ada sumber daya di tempat yang tepat; bisa berupa folder root yang sama yang digunakan oleh server web, atau tempat lain di mana ada salinan sumber daya di sepanjang jalur (relatif) yang sama.

Masalah keempat adalah font. Dragons Abound menggunakan font web dan font yang disimpan secara lokal; keduanya dalam format WOFF2. Di browser, mereka diterapkan ke teks menggunakan gaya font-family CSS, dan sebelum membuat peta, semua font yang mungkin diunggah ke halaman web untuk siap digunakan. Ketika file yang sama dibuka di gim, mencari font di direktori font sistem, dan sepertinya tidak ada direktori font lain yang dapat ditentukan dengan cara apa pun. Solusi sederhana (setidaknya pada mesin tempat saya berkembang) adalah menginstal font yang digunakan oleh game ke direktori font sistem. Namun, ini tidak sesederhana kelihatannya, karena nama font harus cocok, dan pada Windows tidak ada cara mudah untuk mengubah nama font. Tapi, tentu saja, skema seperti itu tidak akan berfungsi pada komputer yang semua font yang diperlukan tidak diinstal. Solusi yang lebih portabel adalah dengan menanamkan font SVG di peta . Ini akan ada dalam daftar TODO saya.

Pada akhirnya, saya datang ke antarmuka pembuatan peta ini:


Bidang input yang luas menentukan ukuran total dunia, di mana 1x1 adalah ukuran peta sumber. Ukuran vbx (kotak tampilan) menentukan ukuran fragmen dunia yang ditampilkan pada peta; pada tangkapan layar, ia juga memiliki nilai 1x1, artinya peta akan menampilkan seluruh dunia. Bidang pusat vbx menentukan lokasi pusat peta di dunia; 0, 0 adalah pusat dunia. Terakhir, parameter SVG menentukan jumlah piksel layar per 1 unit ukuran kotak tampilan; pada nilai 775, peta 1x1 akan ditampilkan di layar dalam jumlah 775x775 piksel. Ini nyaman ketika saya membuat peta yang sangat besar. Dengan mengatur parameter ke nilai rendah (misalnya, 150 piksel), saya dapat memuat peta besar di layar secara keseluruhan.

Dengan mengubah enam parameter ini, saya dapat mengontrol ukuran dunia dan sebagian kecil dari dunia yang ditampilkan pada peta. Tombol Generate berfungsi persis seperti yang Anda duga; tombol Display hanya menampilkan bagian dari dunia, yaitu, saya dapat menghasilkan dunia, dan kemudian menampilkan bagian-bagiannya sendiri, mengubah parameter kotak pandang tanpa perlu membuat ulang dunia. (Seorang programmer akan lebih baik mengimplementasikan ini sebagai penskalaan dan pengguliran.) Tombol Simpan PNG menyimpan peta yang terlihat sebagai file PNG; Tombol Simpan SVG menyimpan file SVG dari seluruh peta. Tombol Test It digunakan untuk menjalankan kode uji, yang berubah selama pengembangan berbagai fungsi.

Sekarang saya dapat menghasilkan dan mencerminkan semua bagian dari dunia besar, saya bisa beralih ke mengadaptasi bentuk tanah ke peta yang lebih besar.

Bagian 3. Bentuk Sushi


Setelah membuat berbagai perubahan di bagian sebelumnya, sekarang saya dapat menghasilkan dunia yang jauh lebih besar dari yang sebelumnya (hingga 8 kali lebih banyak) dan menyimpannya sebagai gambar grafik besar:


(Buka gambar di jendela terpisah untuk melihat peta dalam resolusi penuh 4800x2400 di Flickr.)

Saya menghasilkan peta-peta ini menggunakan generasi prosedural yang sama yang menciptakan peta regional. Peta yang ditunjukkan di atas memiliki bentuk benua yang cukup teratur dan beberapa pulau terluar yang menarik. Namun, itu terutama tergantung pada keberuntungan. Ini peta lain:


Kartu ini hanya kekacauan dari pulau-pulau dan keju sushi Swiss.

Berikut adalah contoh lain, sesuatu di antara dua kartu sebelumnya. Ini tidak sepenuhnya realistis, tetapi mungkin menarik untuk lingkungan fantasi:


Ini adalah daratan yang sangat luas, tetapi ada cukup banyak bentuk tanah yang aneh, dan secara umum dunia tidak terlihat "nyata". (Meskipun bagi seseorang dunia seperti itu tampaknya cukup cocok untuk fantasi.) Jadi bentuk apa yang harus dimiliki oleh peta "dunia"?

Sebagian besar peta dunia fantasi yang saya lihat mewakili benua pulau besar (dengan pulau-pulau kecil di sekitarnya), misalnya, seperti peta Andelen ini :


Atau semenanjung benua, seperti pada peta Angorun ini :


Dari waktu ke waktu, peta seluruhnya terdiri dari tanah atau beberapa benua pulau, tetapi mereka lebih cenderung menjadi pengecualian terhadap aturan tersebut.

Untuk memulai, mari mencari tahu generasi "pulau" benua. Ternyata dalam permainan saya, sudah ada fungsi yang menghasilkan pulau tengah besar di peta dengan mempertimbangkan ukuran peta, jadi itu harus cocok untuk menghasilkan bentuk utama benua. Kebisingan dan pulau-pulau tambahan akan mengurus sisanya.


Saya tidak mengharapkan laut tengah yang besar di peta ini, tetapi ini adalah kejutan yang menyenangkan. Ini adalah contoh lain:


Masalah dengan fungsi pulau tengah adalah bahwa ia dimulai dengan lingkaran yang cocok untuk peta persegi yang ditunjukkan oleh saya, tetapi tidak terlalu baik untuk yang persegi panjang. (Di bawah ini adalah contoh dengan sedikit distorsi, sehingga bentuk dasar lebih jelas terlihat.)


Ini mudah diperbaiki dengan menutupi sushi alih-alih lingkaran dengan elips (terdistorsi), diambil sesuai dengan ukuran peta:


Pulau-pulau pusat ini diskalakan untuk mengisi peta, tetapi dalam banyak kasus untuk peta benua kita perlu meninggalkan "perbatasan" di sekitar benua. Dua parameter mengontrol ukuran peta yang mengisi pulau di sepanjang sumbu X dan Y.


Berikut adalah sistem manajemen perbatasan yang sama dengan distorsi yang lebih logis:


Dapat dilihat bahwa bagian timur dan barat peta tetap menjadi lautan. (Anda dapat membuka peta di jendela terpisah untuk mempelajarinya lebih hati-hati.) Ini berarti bahwa peta menampilkan seluruh dunia (dan ujung kanan dan kirinya dapat dihubungkan) atau bagian dari dunia yang dapat dihubungkan ke peta lain yang juga memiliki lautan dari tepi yang sesuai.

Seorang pembaca yang penuh perhatian yang telah mempelajari peta sebelumnya mungkin telah memperhatikan bahwa pola laut dan daratan berhenti di tengah peta.Sebelumnya, saya hanya punya peta ukuran 1x1, jadi ukuran pola laut dan daratan disesuaikan dengan peta ini. Pada peta yang lebih besar, saya perlu memasang pola secara manual di peta, jadi saya menambahkan fitur ini. (Ada cara di SVG untuk melakukan ubin pola, tetapi di Chrome itu mengandung bug, jadi saya tidak bisa menggunakannya.) Ini adalah fitur yang baik, karena sekarang saya dapat menggunakan pola daratan dan lautan yang lebih kecil yang secara otomatis akan ubin. Saya tidak tahu mengapa saya tidak menyadarinya sebelumnya!

Jadi, sekarang benua pulau berfungsi dengan baik, dan kita akan beralih ke implementasi benua "semenanjung" - peta di mana benua muncul di peta dari tepiannya.


Dalam hal ini, benua tidak dapat runtuh di tiga sisi. Tetapi fitur utama dari peta tersebut adalah bahwa mereka memiliki koneksi darat yang signifikan antara benua yang ditampilkan pada peta dan tanah di luar peta.

Cara termudah untuk menyediakan koneksi seperti itu di luar peta adalah dengan menetapkan permukaan laut yang rendah selama pembuatan. Jadi kami akan meningkatkan luas lahan yang ditampilkan di peta, yang meningkatkan kemungkinan massa tanah yang besar dan terjadinya daratan (alih-alih laut) di sepanjang tepi peta.


Tentu saja, ini tidak menjamin bahwa massa tanah akan sangat menarik, dan memang itu akan disatukan:


Kesamaan satu benua dapat dibuat menggunakan generasi yang sama dari benua pulau, tetapi pada saat yang sama menggeser pulau ke tepi peta. Anda mendapatkan sesuatu seperti ini:


Dapat dilihat bahwa benua (terutama) adalah pulau tengah, bergeser ke atas dan ke kanan. Karena ini adalah benua dan tidak diperlukan untuk mempertahankan bentuk pulau yang ketat, Anda dapat menambahkan lebih banyak distorsi pada bentuknya.


Jelas, ada banyak pendekatan lain untuk generasi bantuan, tetapi dua ini, setidaknya, memberi saya kesempatan untuk menghasilkan bentuk-bentuk tanah yang paling umum pada skala benua.

Pembaca yang penuh perhatian dapat melihat bentuk hutan yang aneh dalam bentuk garis-garis pada banyak peta benua. Lain kali saya akan mulai berurusan dengan masalah model angin dan bioma yang menyebabkan keanehan ini.

Bagian 4. Model angin


Seperti yang dikatakan, ukuran benua di peta Dragons Abound mulai menunjukkan pola cuaca dan bioma yang tidak realistis. Contoh ini menunjukkan bahwa hutan berbaris di sepanjang arah angin yang ada:


Alasannya bukan dalam kode yang rusak; model cuaca dan bioma terlalu sederhana, dan dalam skala besar hal ini menjadi jelas. Untuk mengatasi masalah ini, saya mulai dengan merevisi model angin.

Saya ingin model angin saya lebih mencerminkan dinamika angin bumi: sel Hadley , angin perdagangan , dan sejenisnya. Dinamika semacam itu dapat membantu menghilangkan pola cuaca aneh di peta benua. Namun, ketika mereka ditambahkan, ketidakpuasan menyakitkan dengan model angin Dragons Abound , lambat dan terlalu rumit , kembali terungkap . (Baca tentang implementasi awal model angin di sini..) Setelah memikirkan hal ini selama beberapa hari, saya memutuskan bahwa sebagian besar masalah bermuara pada kenyataan bahwa peta permainan disajikan sebagai diagram Voronoi . (Atau lebih tepatnya, triangulasi Delaunay diagram Voronoi.) Ini memiliki banyak keuntungan ketika menghasilkan medan - dalam kombinasi dengan kebisingan, dapat menciptakan massa daratan yang tampak alami. Itulah mengapa sangat sering digunakan untuk menghasilkan bantuan. Tetapi karena masing-masing segitiga memiliki ukuran dan orientasi yang berbeda, setiap perhitungan, termasuk model angin yang menggunakan sel tetangga, menjadi sangat rumit. Akan lebih mudah untuk memodelkan angin melalui kisi-kisi area identik yang berjarak sama. Selain itu, model angin kemungkinan besar tidak harus sedetail tanah.

Tapi saya tidak ingin sepenuhnya meninggalkan diagram Voronoi yang mendasari Dragons Abound . (Minimal, ini akan membutuhkan penulisan ulang hampir seluruh program!) Sebagai gantinya, saya ingin bereksperimen dengan mengikat peta ke kisi yang seragam, menjalankan model angin di dalamnya, dan kemudian melakukan pengikatan terbalik. Jika kehilangan dalam menyalin bolak-balik tidak terlalu besar, maka ini dapat membuat model angin lebih cepat dan lebih mudah.

Grid mana yang harus saya gunakan? Idealnya, kisi-kisi harus terdiri dari daerah yang sama jaraknya dari tetangganya. Dan deskripsi ini seperti kisi segi enam.


Faktanya, kisi segi enam adalah cara terbaik untuk membagi permukaan datar menjadi bidang yang sama.

Langkah selanjutnya adalah menentukan bagaimana mewakili kisi segi enam dalam program saya. Saya mencari di jaringan sedikit bantuan, dan setiap tautan mengembalikan saya ke halaman tentang kisi-kisi segi enam Amit Patel ( terjemahan dalam Habré). Mungkin, perlu untuk memulainya; Baik juga untuk pertama-tama menjelajahi situs web Red Blob Games jika Anda mencari informasi tentang penerapan mekanisme permainan. Amit menjelaskan lebih baik daripada saya, jadi jika ada sesuatu yang tidak jelas, maka baca halamannya.

Pilihan pertama yang harus dibuat adalah cara menyimpan kisi segi enam. Cara termudah adalah dengan menyimpannya sebagai array dua dimensi, maka saya perlu kemampuan untuk mengikat sel-sel grid ke array. Ada banyak opsi di sini (baca halaman Amit ), tetapi saya akan menggunakan apa yang ia sebut ganjil-r:


Angka-angka di setiap sel adalah indeks sel dalam array dua dimensi. (Gambar dicuri dari halaman Amit . Di halamannya itu interaktif, jadi saya sarankan Anda untuk bereksperimen dengan mereka.)

Setelah membuat pilihan, sekarang saya harus belajar cara melampirkan indeks ke kisi-kisi segi enam. Misalnya, jika saya mencari sel heksagonal (3, 3), lalu seperti apa tetangganya? Jika setiap sel memiliki lebar 5 piksel, lalu apa yang akan menjadi koordinat pusat sel (3, 3)? Dan sebagainya.Berurusan dengan ini bisa sulit, jadi aku senang Amit melakukannya untukku.

Dengan asumsi kita dapat mencuri semua yang kita butuhkan dari Amit, pertama-tama saya harus mencari cara mengatur hexagon di peta. Pada tahap ini, saya belum membutuhkan sebuah array, saya bisa berpura-pura bahwa itu adalah dan melihat di mana segi enam akan berada. Jika saya tahu lokasi heksagon, maka saya cukup membagi lebar peta dengan jarak horizontal di antara mereka untuk mendapatkan jumlah kolom, dan melakukan hal yang sama secara vertikal untuk mendapatkan jumlah baris, setelah itu saya menggambar segi enam di setiap tempat:


Segi enam ini jauh lebih besar daripada yang akan saya gunakan untuk model angin, tetapi mereka menunjukkan kepada saya bahwa semuanya ditempatkan dengan benar.

Di sini saya membuka tepi peta dan hanya menggambar segi enam pusat dan di perbatasan untuk memeriksa apakah saya benar-benar menutup seluruh peta:


Bagian atas dan bawah berada di luar peta, tetapi keberadaan beberapa sel di luar negeri tidak penting jika saya tidak melewatkan bagian peta.

Langkah selanjutnya adalah membuat array untuk kisi-kisi segi enam dan pasang semua segitiga Delaunay ke segi enam yang sesuai. Karena Javascript tidak mendukung indeks array negatif , saya perlu menggeser sel (0, 0) dari pusat peta ke sudut kanan atas. Setelah melakukan ini, saya berkeliling semua segitiga Delaunay dan menambahkannya ke sel yang sesuai dari kisi segi enam. Saya dapat memverifikasi ini dengan mewarnai segi enam yang mengandung tanah:


Untuk menentukan apakah sebuah sel adalah tanah, saya rata-rata ketinggian semua lokasi yang termasuk dalam sel ini. Anda mungkin memperhatikan bahwa untuk beberapa heksagon pantai rata-rata di bawah nol bahkan di hadapan tanah. Anda juga dapat menggunakan ketinggian maksimum semua lokasi di segi enam:


Dalam hal ini, pencarian berlangsung di arah yang berlawanan - segi enam ditandai sebagai tanah jika ada tanah di dalamnya. Mana yang lebih baik tergantung pada apa yang Anda butuhkan.

Dalam hal apa pun, saya dapat meningkatkan akurasi dengan mengurangi ukuran segi enam:


Sekarang pantai telah menjadi jauh lebih baik, tetapi masalah baru telah muncul - banyak segi enam internal yang tidak dianggap tanah. Ini terjadi karena ketika segi enam menjadi cukup kecil, di dalam beberapa di antaranya tidak ada segitiga Delaunay sama sekali. Karena itu, mereka tidak memiliki "tinggi." (Ini juga menunjukkan ketidakteraturan segitiga Delaunay.)

Anda dapat menggunakan solusi ini - ambil ketinggian segitiga yang hilang sebagai rata-rata tetangga mereka, atau sebagai maksimum tetangga mereka.


Secara umum, ketika pengikatan antara segi enam dan lokasi bukan satu-satu, koreksi diperlukan untuk mengisi informasi yang hilang.

Sekarang saya telah meletakkan grid segi enam di peta, kita dapat mulai menerapkan model angin. Gagasan utama dari model angin adalah untuk mensimulasikan beberapa angin (angin perdagangan) dan mendistribusikannya ke seluruh peta sampai mencapai keadaan imobilitas. Pada tingkat segi enam (atau tingkat lokasi, jika saya melakukannya pada segitiga Delaunay) ini mencakup dua tahap: (1) kami merangkum semua angin memasuki segi enam saat ini, dan (2) kami menentukan bagaimana angin yang dijumlahkan meninggalkan segi enam.

Tahap pertama cukup sederhana. Setiap segi enam memiliki enam tetangga, dan masing-masing segi enam ini berkontribusi. Jika kita menganggap masing-masing angin yang memasuki segi enam sebagai vektor, maka total angin dalam sel akan menjadi jumlah dari vektor-vektor ini, mis. dua angin yang sangat berlawanan dengan kekuatan yang sama saling meniadakan.

Tahap kedua (menentukan bagaimana total angin meninggalkan segi enam) membutuhkan pemikiran. Kasus paling sederhana adalah angin bertiup langsung melalui segi enam:


Dalam hal ini, kami berharap angin bergerak ke sel berikutnya tanpa perubahan. (Di sini, vektor merah adalah angin asli, dan biru adalah vektor dari angin yang merambat.)

Tetapi bagaimana jika angin tidak bertiup langsung ke sel tetangga?


Dalam hal ini, tampaknya logis bahwa sebagian angin bertiup ke segi enam tepat di atasnya, bagian lain ke dalam sel yang terletak berlawanan arah jarum jam dari yang ini, dan proporsinya harus bergantung pada arah titik panah. Dalam kasus kami, bagian utama angin akan pindah ke sel atas, dan lebih sedikit ke sel tetangga.

Juga penting untuk memutuskan arah angin yang menyebar. Salah satu opsi adalah mempertahankan arah angin asli:


Tampaknya ini adalah opsi yang paling realistis, tetapi ada satu lagi - untuk mengubah arah angin sesuai dengan tepi segi enam yang bersinggungan:


Pendekatan ini kurang akurat, tetapi memiliki keuntungan: angin yang masuk akan selalu berada di salah satu dari enam arah, yang dapat menyederhanakan perhitungan.

Kesulitan lain muncul ketika mempertimbangkan medan. Apa yang harus terjadi ketika sebuah gunung ditiup angin?


Dalam hal ini, sebagian dari angin melewati gunung (mungkin menciptakan presipitasi), tetapi sebagian dari angin berputar ke samping.


Oleh karena itu, cara angin keluar dari segi enam tergantung pada arahnya, serta topografi di sel-sel tetangga.

Sekarang mari kita bicara tentang cara merepresentasikan vektor. Ada dua opsi utama. Pertama, vektor dapat direpresentasikan sebagai nilai X dan Y, misalnya seperti ini:


Jika kita menggambar vektor mulai dari (0, 0), maka (X, Y) adalah koordinat dari titik akhir. Catatan seperti itu membuatnya sangat mudah untuk menjumlahkan vektor. Kami hanya menjumlahkan semua nilai (X, Y) dan mendapatkan vektor baru:


Pilihan lain adalah menggunakan sudut dan panjang vektor:


Dalam bentuk ini, mudah untuk melakukan operasi seperti memutar vektor atau mengubah panjangnya.
Untuk sebagian besar operasi yang diperlukan dalam model angin, opsi pertama lebih baik, tetapi dalam beberapa kasus yang kedua lebih baik, sehingga akan lebih mudah untuk beralih di antara mereka jika perlu. Agar tidak menemukan kembali roda, saya mencari perpustakaan vektor untuk Javascript dan Victor.js sepenuhnya muncul , jadi saya mengambil keuntungan dari itu.

Saya akan mulai dengan menambahkan vektor angin ke setiap segi enam dan melihat apakah saya dapat memvisualisasikannya:


Terlihat bagus sejauh ini.

Langkah selanjutnya adalah memeriksa apakah saya dapat dengan benar membagi vektor angin dan mendistribusikannya ke sel berikutnya. Pertama, Anda perlu menghitung sudut yang mengarah ke sel lain. Saya menemukan jawabannya lagi di halaman Amit :


Yaitu, vektor pada 0 derajat menunjuk ke segi enam di sebelah kanan, pada 60 derajat - ke segi enam di kanan bawah, dan seterusnya. Vektor yang menunjuk di antara dua arah ini dibagi secara proporsional antara dua sel - yaitu, vektor pada sudut 30 derajat akan dibagi secara merata antara sel ke kanan dan sel dari bawah ke kanan. Setiap vektor terletak di suatu tempat di antara sudut-sudut wajah dari dua sel yang bersebelahan, jadi lihat saja sudut vektor angin, cari tahu bahwa ia jatuh di antara sudut-sudut pusat dari dua segi enam, dan kemudian membaginya secara proporsional di antara kedua segi enam ini.

Misalnya, jika vektor angin memiliki sudut 22 derajat:


kemudian 38/60 dari nilai merambat ke sel di sebelah kanan, dan 22/60 dari nilai vektor ke sel di kanan bawah. Jika vektor disajikan sebagai pasangan nilai X dan Y, maka Anda dapat mendistribusikannya dengan mengalikan setiap nilai vektor asli dengan fraksi (misalnya, dengan 22/60), dan kemudian menambahkannya ke vektor angin di hexagon baru.

Untuk menguji ini, saya dapat mengatur angin di arah yang berbeda dan menempatkannya di bagian atas dan samping peta dan melihat apakah mereka dapat menyebar dengan benar di peta. Ketika angin bertabrakan, mereka harus digabungkan dan memilih arah rata-rata dengan peningkatan kecepatan:


Di sini kita melihat bahwa angin bertemu di sepanjang diagonal dan bergabung untuk berhembus ke sudut bawah.

Langkah selanjutnya adalah memperhitungkan pengaruh tanah terhadap angin. Tentu saja, model angin nyata sangat kompleks, tetapi saya terutama tertarik pada bagaimana geografi tanah mempengaruhi angin permukaan. Pada tingkat paling sederhana, ini adalah pengaruh ketinggian tanah dan dataran rendah pada arah dan kecepatan angin. Saya bereksperimen dengan banyak pendekatan berbeda, tetapi sebagai hasilnya saya menetapkan dua aturan sederhana :

  1. Angin berpaling dari rintangan.
  2. Angin melambat saat naik, dan berakselerasi, jatuh.

Hambatan terjadi ketika angin berhembus ke dalam sel dengan ketinggian yang lebih tinggi, misalnya, ke dalam sel dengan gunung. Ketika ini terjadi, saya melihat dua sel di mana angin bertiup, dan mengubah sudut angin sehingga lebih mengarah ke yang lebih rendah dari dua segi enam. Besarnya sudut perubahan tergantung pada perbedaan ketinggian dua sel, jadi ketika angin berhembus menjadi dua sel tetangga dengan gunung, arahnya berubah sedikit, tetapi jika ia berhembus ke dalam sel dengan gunung dan sel dengan dataran, maka itu akan berubah lebih ke arah sel kosong:


Kekuatan angin bisa disesuaikan. Pada peta di atas, ia terlalu kuat, yang akan mengarah pada munculnya banyak bioma yang tidak realistis. Berikut ini arti yang lebih logis:


Sebagian besar pergerakan angin yang disebabkan oleh relief masih tetap ada, tetapi celah besar dan lembah angin kencang telah menjadi lebih kecil.

Fitur lain yang meningkatkan realisme adalah dispersi angin. Misalnya, peta di atas menunjukkan bahwa angin bertiup ke barat tepat di atas kota Breeches. Meskipun berhembus jarak jauh, itu tidak pernah menghilang seperti yang kita harapkan. Ketika angin yang berhembus bertemu dengan udara lain, biasanya angin bertiup bersama dengannya. Untuk mensimulasikan ini, saya dapat mengambil sebagian kecil angin yang bertiup di setiap segi enam dan mendistribusikannya ke semua sel tetangga. Beginilah tampilan peta di atas dengan sebaran kecil:


Seperti yang Anda lihat, sekarang angin di atas Breeches mulai menghilang sedikit.

Operasi ini menentukan sebagian besar arah angin. Bagian kedua adalah perlambatan angin ketika naik dan akselerasi saat menurunkan. Saya dapat menyadari hal ini dengan melihat ketinggian relatif sel dari mana angin datang, dan ketinggian sel tempat angin bertiup, dan mempercepat / memperlambatnya jika perlu.

Begini tampilannya:


Sekarang kita melihat bahwa bagian dari angin yang bertiup melalui pegunungan tinggi di bagian tengah pulau terputus. Dan sebaliknya - beberapa angin baru muncul di bagian barat pulau itu, tempat udara bergerak dari daratan yang relatif tinggi ke laut. (Ini angin sepoi-sepoi ! Meskipun tidak terlalu: mekanismenya berbeda di sana.)

Sekarang saya dapat mengganti angin baru ke dalam algoritma presipitasi yang ada. Ini perbandingannya (angin tua di kiri, angin baru di kanan):


(Klik pada gambar untuk melihat versi yang lebih besar.) Jelas, ada perbedaan antara model angin. Di kedua peta, angin bertiup dari timur. Pegunungan di dekat pusat peta membelokkan angin ke selatan, menyebabkan hujan lebat dan membuat rawa dan hutan di selatan pegunungan. Di bagian bawah, angin bertiup tanpa gangguan, dan hutan terbentuk di sepanjang bagian timur pulau. Dalam model angin asli, angin yang cukup melewati pegunungan tengah dan rawa-rawa untuk membuat hutan di bagian barat pulau. Dalam model baru, sebagian besar angin terputus dan membentuk biomassa berumput di sisi jauh pegunungan.

Model lama memiliki variabilitas parameter acak (dalam interval yang diberikan), dan ada kemungkinan bahwa beberapa kombinasi dari parameter ini akan memberikan gambar lebih seperti peta baru. Namun pada kenyataannya, kita tidak perlu mereproduksi perilaku yang tepat dari model lama, hanya model yang menciptakan hasil yang terlihat meyakinkan.

Inti dari semua ini adalah untuk mempercepat dan menyederhanakan generasi angin sehingga perilaku angin baru dapat ditambahkan ke peta benua. Apakah saya melakukannya? Saya membuat profil model angin asli dan model berbasis hexagon baru. Ternyata model baru itu 15-20 kali lebih cepat daripada yang asli (!). Ini adalah akselerasi yang sangat signifikan yang tidak banyak berpengaruh pada kartu. Percobaan memperjelas bahwa model ini tidak terlalu sensitif terhadap ukuran segi enam, jadi jika perlu, saya dapat mempercepat algoritma lebih banyak lagi dengan meningkatkan ukuran sel.

Lain kali kita akan bekerja menggunakan model angin baru untuk menerapkan pola angin skala benua, dan kemudian menghubungkannya dengan model curah hujan dan bioma.

Source: https://habr.com/ru/post/id429620/


All Articles