Gua 3D Tak Terbatas dalam Persatuan

8

Seorang teman dan saya berharap untuk membuat game di Unity di mana Anda terbang melalui gua 3D tanpa batas yang dapat berputar dan berputar ke segala arah (meskipun jelas tidak sampai pada titik bahwa belokan tidak mungkin dilakukan). Kami berpikir tentang membuat sejumlah "potongan" terowongan yang masing-masing melengkung dalam jumlah tertentu, dan memunculkan masing-masing di ujung yang sebelumnya.

Tetapi kami tidak tahu bagaimana memastikan bahwa mulut dari satu potongan terowongan selalu sejajar sempurna (baik dalam posisi dan rotasi) dengan ujung yang sebelumnya. Adakah yang bisa memberikan saran tentang cara mencapai ini?

Apakah kita melakukannya dengan cara yang benar, atau adakah cara yang lebih baik untuk secara prosedural menghasilkan gua? Poin bonus: Akan luar biasa jika gua bisa berubah dalam diameter dan / atau bentuk juga, meskipun itu hanya saus.

richardmherndon
sumber

Jawaban:

6

Jarang ada "cara yang benar" atau "cara yang salah" ketika datang ke desain game. Ada banyak, banyak cara untuk mengatasi masalah ini, tetapi berikut adalah beberapa pendekatan yang mungkin untuk dijelajahi:

  • Batasi potongan-potongan terowongan untuk memulai dan mengakhiri hanya dalam arah tertentu; misalnya hanya sepanjang sumbu. Maka Anda hanya perlu melacak offset dari awal hingga akhir segmen, bersama dengan enum yang menggambarkan arah gerakan pada awal dan akhir segmen. Dengan cara ini Anda tidak perlu khawatir tentang memutar jerat terowongan Anda sama sekali, asalkan Anda selalu memilih yang berikutnya sehingga mulai dengan arah yang sama dengan yang terakhir berakhir.

  • Buat setiap segmen mulai dari asal ruang model lokalnya dengan terowongan berjalan sepanjang sumbu tertentu (+ X, + Z, atau -Z akan menjadi pilihan yang paling logis, tetapi semua model harus menggunakan yang sama), lalu simpan posisi ujung terowongan dan arah akhir perjalanan dalam beberapa cara sehingga jaring berikutnya dapat ditransformasikan dengan benar. (matriks transformasi mungkin adalah cara termudah untuk menyimpan informasi ini, tetapi Anda juga bisa menggunakan vektor perpindahan + angka empat, angka empat ganda, perpindahan + vektor basis baru, perpindahan + rotasi sudut euler, dll.)

  • Secara prosedural menghasilkan gua Anda dengan mengalirkan data titik baru ke beberapa jerat. Anda dapat melakukan ini menggunakan Meshkelas . Saat membuat data titik baru, cara termudah mungkin untuk memilih titik di suatu tempat dengan arah yang kira-kira sama dengan segmen gua sebelumnya, lalu biarkan pusat gua bergerak ke titik itu. Kemudian Anda dapat menggunakan koordinat silindris untuk secara prosedural membuat detail di dinding gua. Anggap saja mengekstrusi ujung silinder, kemudian secara individu menerjemahkan setiap simpul lebih dekat atau lebih jauh dari pusat silinder itu.

Setiap solusi yang menggunakan segmen premade akan mengharuskan Anda untuk memastikan bahwa semua jerat memiliki bentuk dan diameter yang sama di sekitar pusat terowongan, tetapi Anda dapat menyiasati hal ini dengan meminta segmen tersebut tumpang tindih sampai batas tertentu, dan masing-masing segmen menyala di ujung. Jika dilakukan dengan benar, seharusnya tidak terlalu jelas bagi pemain bahwa ada jahitan.

Di sisi lain, jika Anda menggunakan geometri yang sepenuhnya dihasilkan secara prosedural, Anda akan memiliki lebih banyak pekerjaan untuk memastikan bahwa Anda tidak menghasilkan bagian yang tidak mungkin untuk dilintasi, dan Anda mungkin mengalami masalah dengan deteksi tabrakan.

Ingat, dengan game "tak terbatas" apa pun, Anda harus mengetahui batasan representasi floating point. Jika pemain terlalu jauh dari asal dunia, menjadi mudah kehilangan presisi dalam perhitungan floating point (ketika dua nilai besar dikurangkan dari satu sama lain, misalnya). Untuk menghindari hal ini, Anda dapat membuat dunia bergerak di sekitar pemain, daripada pemain bergerak di dunia, tetapi biasanya lebih mudah untuk hanya memeriksa posisi pemain begitu sering, dan jika mereka terlalu jauh dari asal, membangun kembali dunia dengan pemain di atau dekat tempat asalnya.

bcrist
sumber
2
+1 terutama untuk komentar 'tidak benar' (walaupun saya harus sedikit tidak setuju: ada banyak, banyak cara salah ...)
Steven Stadnicki
Terima kasih banyak! Kami akhirnya menggunakan beberapa potongan terowongan yang berbeda dengan lokasi akhir yang telah diketahui sebelumnya dan arah, menetapkan penanda di lokasi / sudut tersebut, dan menempatkan setiap potongan baru relatif terhadap penanda potongan sebelumnya. Memang keren untuk melakukan sesuatu yang lebih rumit, tetapi untuk saat ini, generasi prosedural yang lebih sah jauh dari jangkauan keterampilan dan batas waktu kami. Terima kasih lagi!
richardmherndon
3

Inilah teknik yang saya coba-coba baru-baru ini. Prototipe RenderMonkey saya menunjukkan bagian ngarai bergaya badlands, tetapi prinsip yang sama harus bekerja di gua.

Idenya adalah mulai dengan ubin yang generik, benar-benar membosankan, dengan tepi yang mudah diprediksi sehingga mudah untuk disejajarkan tanpa jahitan atau celah:

Ubin kusam dan dapat diprediksi

Ubin awal ini bisa berupa bentuk yang telah Anda modelkan, atau tabung makaroni yang dihasilkan secara prosedural dari geometri silinder (formulir ini adalah varian dari saran bcrist dan Steven Stadnicki). Menggunakan model yang Anda buat membuatnya lebih mudah untuk menangani topologi sewenang-wenang seperti jalur bercabang, atau tempat menarik seperti gua terbuka. Ini masih mungkin dengan prosedural murni (lihat saran Gyroninja tentang teknik metaball), tetapi menantang.

Setelah ubin ditempatkan ke dunia, pindahkan simpulnya menggunakan fungsi noise yang diterapkan di worldspace. Ini menjaga konektivitas dan kelancaran antar ubin (karena simpul bertepatan memiliki input ruang dunia yang sama, dan mendapatkan output perpindahan yang sama), tetapi membuat setiap ubin terlihat unik dan organik:

Itu lebih menarik

Tekstur dan normals juga diterapkan di worldspace - di sini menggunakan pemetaan triplanar - sehingga ubin yang berdekatan benar-benar mulus, tanpa kendala pembungkus UV yang rumit.

Juga lebih menarik

Harapannya adalah bahwa teknik seperti ini memberi Anda kemudahan perencanaan dan kontrol desain level dari peta ubin, tanpa pengulangan yang terlihat atau struktur yang tampak mekanis dalam hasil yang dapat dimainkan.

Anda dapat menggunakan mesh res rendah dengan hanya komponen noise frekuensi rendah yang diterapkan untuk membuat representasi collision. Seperti yang dicatat oleh bcrist, Anda harus mengontrol amplitudo maksimum kebisingan relatif terhadap radius dan ketajaman belokan terowongan, untuk memastikannya tidak pernah terjepit sepenuhnya.

Satu catatan lebih lanjut: jika gua Anda benar-benar tak terbatas, Anda mungkin perlu "membukanya kembali" secara berkala saat pemain bergerak semakin jauh dari tempat asalnya. Karena bilangan floating point kehilangan presisi pada magnitudo tinggi, fisika dan artefak rendering dapat merayap masuk pada jarak ekstrem. Jika Anda melakukan ini, Anda ingin noise ruang dunia Anda menjadi periodik dalam skala besar, dengan periode yang sama persis dengan offset recentering Anda, sehingga Anda tidak menemukan jahitan setelah recentering.

DMGregory
sumber
2

Anda bisa memodelkan gua Anda sebagai urutan titik, masing-masing dengan ukuran terkait, dengan garis yang menghubungkannya. Kemudian perlakukan setiap titik dan garis sebagai metaballs dan metacylinders. Ini memberi Anda bentuk dasar untuk gua Anda, yang Anda mungkin ingin mulai menambahkan variasi, seperti dengan mengimbangi simpul secara acak.

Gyroninja
sumber
2

Berikut pendekatan lain untuk pembuatan prosedural yang belum secara eksplisit disebutkan: menguliti spline. Anda dapat menggunakan versi Hermite Splines(yang memberikan kurva posisi interpolasi dan garis singgung) untuk menentukan kurva: ketika saatnya untuk menghasilkan segmen baru, cukup pilih posisi (kira-kira ke arah segmen sebelumnya, seperti kata bcrist) dan arah (kira-kira sama arah - misalnya, dalam beberapa kerucut yang didefinisikan dengan baik dari arah sebelumnya), kemudian gunakan posisi + arah baru dan posisi + arah Anda sebelumnya untuk membangun 'tulang belakang' baru untuk gua Anda. Setelah Anda memiliki tulang punggung ini, Anda dapat mengulitinya dengan konstruksi silindris: tentukan posisi dan garis singgung dari (misalnya) 10 poin di sepanjang kurva, gunakan posisi / garis singgung itu untuk menemukan 'bingkai' ortogonal, dan kemudian gunakan bingkai ini untuk membangun segmen silinder. Satu peringatan kecil dengan ini adalah bahwa gua tidak bisa melengkung juga banyak, atau Anda mungkin mengalami masalah persimpangan diri.

EDIT: Berikut ini rincian pseudocode kasar dari algoritma:

Parameters:
  L = (average) segment length,
  V = segment length variation,
  R = cylinder radius,
  T = segment angular variation
  S = number of 'rings' per segment

Setup:
Choose an initial point P_0 and direction D_0 (for concreteness' sake, these can be
the origin and the X axis).  Set P_prev and D_prev to these values.
Initialize u_prev to be the Y axis and v_prev to be the Y and Z axes.
  (Note that (D_prev, u_prev, v_prev) form a mutually-orthogonal 'coordinate frame')

Generate a segment (do this as many times as you want):
{
  Choose a (temporary) direction D within a cone of size T around the previous direction D_prev
  Choose a segment length L_cur = at random from within the range [L-V, L+V].
  Set the current terminal point P_cur to P_prev+D*L_cur - this is the position
  we'll interpolate to
  Set the current terminal direction D_cur to a direction chosen at random from
  within a cone of size T around the previous direction.  (There are good ways
  of doing this - if you look back through gamedev.SE you should find some)
  'Build' the Hermite spline H that goes from (P_prev, D_prev) to (P_cur, D_cur)

  Now, skin that spline:
  for ( i = 1; i <= S; i++ ) {
    find the position P of the hermite spline H at t=i/S
    find the direction D of the spline at t (this will be just the derivative)
    'transport' the orthogonal frame to the new spot: for instance,
      v_new = D x u_prev
      u_new = v_new x D
    (note that this keeps u_new, v_new close to their old values, and orthogonal
    to each other and to D)
    Use the previous and current frames and positions to build a cylindrical 'ring':
    For theta from 0 to 2pi {
      find the points (P+(u_new, v_new, D) * (cos theta, sin theta, 0))
      and connect them to their counterparts from the previous ring
      (note that that multiplication is a matrix-vector multiply)
    }
    update u_prev and v_prev to u_new and v_new
  }
  update the other prev variables to their 'new' values
}

Ini jelas kodesemu yang sangat kasar; jika ada bagian yang tidak jelas, beri tahu saya dan saya akan coba jelaskan, tetapi akan sulit untuk menutupi semua detail tanpa hanya dump kode besar ...

Steven Stadnicki
sumber
(Kebetulan, jika Anda ingin kodesemu untuk pendekatan ini beri tahu saya; saya harus melakukan sesuatu seperti ini di pekerjaan sebelumnya, jadi saya akhirnya mengerjakan semua detail kecil.)
Steven Stadnicki
Saya ingin tahu implementasi Anda; Saya telah melakukan sesuatu yang agak mirip sekali juga tetapi menggunakan kurva Bezier kubik 3D sebagai gantinya.
bcrist