Infinite 3D Cave in Unity


8

Un amico e io speriamo di fare un gioco in Unity in cui voli attraverso un'infinita grotta 3D che può torcere e avvolgere in qualsiasi direzione (anche se ovviamente non al punto che le curve sono impossibili da realizzare). Stavamo pensando di creare un numero di "pezzi" di tunnel che ciascuno curvasse una certa quantità e di generarli ciascuno alla fine di quello precedente.

Ma non abbiamo idea di come assicurarci che la bocca di un tunnel sia sempre perfettamente allineata (sia in posizione che in rotazione) con la fine di quella precedente. Qualcuno può offrire qualche consiglio su come realizzare questo?

Lo stiamo facendo nel modo giusto o esiste un modo migliore per generare proceduralmente la caverna? Punti bonus: sarebbe fantastico se la caverna potesse cambiare anche in diametro e / o forma, anche se sarebbe solo sugo.

Risposte:


6

Raramente c'è un "modo giusto" o "modo sbagliato" quando si tratta di progettazione del gioco. Esistono molti, molti modi per risolvere questo problema, ma qui ci sono alcuni possibili approcci da esplorare:

  • Vincola i pezzi del tunnel per iniziare e finire solo in determinate direzioni; per esempio solo lungo gli assi. Quindi devi solo tenere traccia dell'offset dall'inizio alla fine di un segmento, insieme agli enumerati che descrivono la direzione del movimento all'inizio e alla fine del segmento. In questo modo non devi preoccuparti di ruotare le maglie del tunnel, purché tu scelga sempre quella successiva in modo che inizi nella stessa direzione in cui è finita l'ultima.

  • Fai in modo che ogni segmento inizi all'origine del suo spazio modello locale con il tunnel che percorre un asse specifico (+ X, + Z o -Z sarebbero le scelte più logiche, ma tutti i modelli dovrebbero usare lo stesso), quindi memorizzare la posizione dell'estremità del tunnel e la direzione di marcia finale in qualche modo in modo che la maglia successiva possa essere trasformata correttamente. (una matrice di trasformazione è probabilmente il modo più semplice per memorizzare queste informazioni, ma è anche possibile utilizzare un vettore di spostamento + quaternione, doppio quaternione, spostamento + nuovi vettori di base, spostamento + rotazioni dell'angolo di eulero, ecc.)

  • Genera proceduralmente la tua caverna trasmettendo nuovi dati di vertici su poche mesh. Puoi farlo usando la Meshclasse . Quando si generano nuovi dati vertici, il modo più semplice è probabilmente quello di selezionare un punto da qualche parte nella stessa direzione del precedente segmento della caverna, quindi lasciare che il centro della caverna si sposti verso quel punto. Quindi puoi utilizzare le coordinate cilindriche per creare proceduralmente i dettagli sulle pareti della caverna. Pensa a come estrudere l'estremità di un cilindro, quindi traducendo individualmente ogni vertice più vicino o più lontano dal centro di quel cilindro.

Qualsiasi soluzione che utilizza segmenti premade richiederà di assicurarsi che tutte le mesh abbiano la stessa forma e diametro attorno al centro del tunnel, ma è possibile aggirare il problema facendo in modo che i segmenti si sovrappongano fino a un certo punto e che ogni segmento diventi alle estremità. Se fatto bene, non dovrebbe essere troppo ovvio per il giocatore che c'è una cucitura.

D'altra parte, se si sceglie la geometria interamente generata proceduralmente, si avrà più lavoro per assicurarsi di non generare sezioni impossibili da attraversare e che si potrebbero riscontrare problemi con il rilevamento delle collisioni.

Ricorda, con qualsiasi gioco "infinito", dovresti essere consapevole dei limiti delle rappresentazioni in virgola mobile. Se il giocatore si allontana troppo dall'origine mondiale, diventa facile perdere precisione nei calcoli in virgola mobile (ad esempio quando vengono sottratti due valori grandi l'uno dall'altro). Per evitarlo, puoi far muovere il mondo attorno al giocatore, piuttosto che il giocatore si muova attraverso il mondo, ma di solito è più semplice controllare la posizione del giocatore ogni tanto, e se sono troppo lontani dall'origine, ricostruisci il mondo con il giocatore vicino o vicino all'origine.


2
+1 soprattutto per il commento "no right way" (anche se dovrò essere leggermente in disaccordo: ci sono molti, molti modi sbagliati ...)
Steven Stadnicki,

Grazie mille! Abbiamo finito per usare alcuni diversi pezzi del tunnel con posizioni finali e direzioni note, posizionando i marker in quelle posizioni / angoli e posizionando ogni nuovo pezzo rispetto al marker del pezzo precedente. Sarebbe stato bello fare qualcosa di più elaborato, ma per il momento, una generazione procedurale più legittima era ben al di fuori della nostra gamma di abilità e limite di tempo. Grazie ancora!
richardmherndon,

3

Ecco una tecnica che ho sperimentato di recente. Il mio prototipo di RenderMonkey mostra una sezione di canyon in stile calanchi, ma lo stesso principio dovrebbe funzionare nelle grotte.

L'idea è di iniziare con tessere che sono generiche, decisamente noiose, con bordi semplici e prevedibili, quindi sono facili da allineare senza cuciture o lacune:

Piastrella opaca e prevedibile

Queste tessere di partenza potrebbero essere forme che hai modellato o tubi di maccheroni di geometria cilindrica generati proceduralmente (questa forma è una variante dei suggerimenti di bcrist e Steven Stadnicki). L'uso dei modelli che hai creato semplifica la gestione di topologie arbitrarie come percorsi di diramazione o punti di interesse come caverne aperte. Questo è ancora possibile con puro procedimento (vedi il suggerimento di Gyroninja sulle tecniche di metaball), ma stimolante.

Una volta posizionata una piastrella nel mondo, sposta i suoi vertici usando le funzioni di rumore applicate nello spazio del mondo. Ciò preserva la connettività e la continuità tra le tessere (poiché i vertici coincidenti hanno lo stesso input di spazio del mondo e ottengono lo stesso output di spostamento), ma rendono ogni tessera unica e organica:

Questo è più interessante

Trama e normali vengono applicati anche nello spazio del mondo - qui usando la mappatura triplanare - in modo che le piastrelle adiacenti siano completamente senza soluzione di continuità, senza complicati vincoli di scartamento UV.

Anche più interessante

La speranza è che una tecnica come questa ti dia la facilità di pianificazione e controllo del livello di progettazione di una mappa piastrellata, senza ripetizione visibile o struttura dall'aspetto meccanico nel risultato giocabile.

È possibile utilizzare una mesh a bassa risoluzione con solo i componenti di rumore a bassa frequenza applicati per creare la rappresentazione della collisione. Come osserva Bcrist, dovrai controllare la massima ampiezza del rumore rispetto al raggio e alla nitidezza delle curve del tunnel, per assicurarti che non si stacchi mai completamente.

Un'ulteriore nota: se la tua caverna è davvero infinita, potresti doverla "periodicamente" periodicamente man mano che il giocatore si sposta sempre più lontano dall'origine. Poiché i numeri in virgola mobile perdono precisione ad altezze elevate, la fisica e gli artefatti di rendering possono insinuarsi a distanze estreme. Se lo fai, vorrai che il tuo rumore dello spazio del mondo sia periodico su larga scala, con il periodo esattamente corrispondente al tuo offset di recente, quindi non incontri cuciture dopo il recente.


2

Puoi modellare la tua caverna come una sequenza di punti, ciascuno con una dimensione associata, con linee che li collegano. Quindi tratta ogni punto e linea come metaballs e metacylinders. Questo ti dà una forma di base per la tua caverna, a cui potresti voler iniziare ad aggiungere variazioni, ad esempio sfalsando casualmente i vertici.


2

Ecco un altro approccio alla generazione procedurale che non è stato ancora esplicitamente menzionato: spline skinning. Puoi usare una versione di Hermite Splines(che forniscono una curva che interpola posizioni e tangenti) per definire le curve: quando è il momento di generare un nuovo segmento, basta scegliere una posizione (approssimativamente nella direzione del segmento precedente, come dice bcrist) e una direzione (approssimativamente nella stessa direzione - ad es., all'interno di un cono ben definito della direzione precedente), quindi utilizzare la nuova posizione + direzione e la posizione precedente + direzione per costruire una nuova "colonna vertebrale" per la tua caverna. Una volta che hai questa spina dorsale, puoi scuoterla con una costruzione cilindrica: determina le posizioni e le tangenti di (ad esempio) 10 punti lungo la curva, usa quelle posizioni / tangenti per trovare un 'frame' ortogonale, quindi usa questi frame per costruire segmenti cilindrici. Un piccolo avvertimento è che la grotta non può curvarsi troppo molto, altrimenti potresti incorrere in problemi di autointersezione.

EDIT: Ecco una suddivisione approssimativa dello pseudocodice dell'algoritmo:

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
}

Questo è ovviamente molto pseudocodice approssimativo; se c'è qualcosa che non è chiaro fammelo sapere e proverò a spiegare, ma sarà difficile coprire tutti i dettagli senza un enorme dump di codice ...


(Per inciso, se vuoi uno pseudocodice per questo approccio fammi sapere; dovevo fare qualcosa di simile a questo in un precedente lavoro, quindi ho finito per elaborare tutti i piccoli dettagli.)
Steven Stadnicki,

Sarei curioso di vedere la tua implementazione; Ho fatto qualcosa in qualche modo simile anche una volta, ma usando invece curve cubiche 3D di Bezier.
bcrist
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.