Infinite 3D Cave dans Unity


8

Un ami et moi espérons faire un jeu dans Unity dans lequel vous volerez à travers une grotte 3D infinie qui peut se tordre et s'enrouler dans toutes les directions (mais évidemment pas au point que les virages sont impossibles à faire). Nous pensions créer un certain nombre de «morceaux» de tunnel qui courbent chacun une certaine quantité, et engendrer chacun à la fin de celui qui l'a précédé.

Mais nous ne savons pas comment nous assurer que l'embouchure d'une pièce de tunnel s'aligne toujours parfaitement (en position et en rotation) avec la fin de la précédente. Quelqu'un peut-il donner des conseils sur la façon d'y parvenir?

Allons-nous même dans le bon sens, ou existe-t-il une meilleure façon de générer de façon procédurale la grotte? Points bonus: Ce serait génial si la grotte pouvait également changer de diamètre et / ou de forme, bien que ce ne soit que de la sauce.

Réponses:


6

Il y a rarement une «bonne façon» ou une «mauvaise façon» en matière de conception de jeux. Il existe de nombreuses façons de résoudre ce problème, mais voici quelques approches possibles à explorer:

  • Contraindre les pièces du tunnel au début et à la fin uniquement dans certaines directions; par exemple uniquement le long des axes. Ensuite, il vous suffit de garder une trace du décalage du début à la fin d'un segment, ainsi que des énumérations décrivant la direction du mouvement au début et à la fin du segment. De cette façon, vous n'avez pas à vous soucier de faire pivoter vos maillages de tunnel, tant que vous choisissez toujours le suivant de telle sorte qu'il commence dans la même direction que la dernière.

  • Faites commencer chaque segment à l'origine de son espace modèle local avec le tunnel se déplaçant le long d'un axe spécifique (+ X, + Z ou -Z seraient les choix les plus logiques, mais tous les modèles devraient utiliser le même), puis mémoriser la position de l'extrémité du tunnel et la direction finale de déplacement d'une manière ou d'une autre afin que le maillage suivant puisse être transformé correctement. (une matrice de transformation est probablement le moyen le plus simple de stocker ces informations, mais vous pouvez également utiliser un vecteur de déplacement + quaternion, quaternion double, déplacement + nouveaux vecteurs de base, déplacement + rotations d'angle euler, etc.)

  • Générez de façon procédurale votre grotte en diffusant de nouvelles données de sommets sur quelques mailles. Vous pouvez le faire en utilisant la Meshclasse . Lors de la génération de nouvelles données de sommets, le moyen le plus simple est probablement de choisir un point quelque part dans approximativement la même direction que le segment de grotte précédent, puis de laisser le centre de la grotte se déplacer vers ce point. Ensuite, vous pouvez utiliser des coordonnées cylindriques pour créer des détails procéduraux sur les murs de la grotte. Considérez-le comme une extrusion à l'extrémité d'un cylindre, puis une translation individuelle de chaque sommet plus près ou plus loin du centre de ce cylindre.

Toute solution qui utilise des segments prédéfinis vous obligera à vous assurer que toutes les mailles ont la même forme et le même diamètre autour du centre du tunnel, mais vous pouvez contourner cela quelque peu en faisant chevaucher les segments dans une certaine mesure et en faisant évaser chaque segment aux extrémités. Si c'est bien fait, il ne devrait pas être trop évident pour le joueur qu'il y a une couture.

D'un autre côté, si vous optez pour une géométrie entièrement générée de manière procédurale, vous aurez plus de travail pour vous assurer de ne pas générer de sections impossibles à parcourir, et vous pourriez rencontrer des problèmes avec la détection de collision.

Gardez à l'esprit que pour tout jeu "infini", vous devez être conscient des limites des représentations en virgule flottante. Si le joueur s'éloigne trop de l'origine du monde, il devient facile de perdre en précision dans les calculs en virgule flottante (lorsque deux grandes valeurs sont soustraites l'une de l'autre, par exemple). Pour éviter cela, vous pouvez faire bouger le monde autour du joueur, plutôt que le joueur se déplace à travers le monde, mais il est généralement plus facile de vérifier la position du joueur de temps en temps, et s'ils sont trop loin de l'origine, reconstruisez le monde avec le joueur à ou près de l'origine.


2
+1 spécialement pour le commentaire `` pas de bonne façon '' (même si je dois être légèrement en désaccord: il y a beaucoup, beaucoup de mauvaises façons ...)
Steven Stadnicki

Merci beaucoup! Nous avons fini par utiliser quelques pièces de tunnel différentes avec des emplacements et des directions de fin connus à l'avance, en plaçant des marqueurs à ces emplacements / angles et en plaçant chaque nouvelle pièce par rapport au marqueur de l'ancienne pièce. Cela aurait été cool de faire quelque chose de plus élaboré, mais pour le moment, une génération procédurale plus légitime était bien en dehors de notre gamme de compétences et de notre limite de temps. Merci encore!
richardmherndon

3

Voici une technique que j'ai expérimentée récemment. Mon prototype RenderMonkey montre une section de canyon de style badlands, mais le même principe devrait fonctionner dans les grottes.

L'idée est de commencer avec des carreaux génériques, carrément ennuyeux, avec des bords prévisibles simples afin qu'ils soient faciles à aligner sans coutures ni lacunes:

Carrelage terne et prévisible

Ces tuiles de départ peuvent être des formes que vous avez modélisées ou des tubes de macaroni de géométrie cylindrique générés de manière procédurale (cette forme est une variante des suggestions de bcrist et Steven Stadnicki). L'utilisation de modèles que vous avez créés facilite la gestion d'une topologie arbitraire comme les chemins de branchement ou les points d'intérêt comme les cavernes ouvertes. Ceci est toujours possible avec une procédure pure (voir la suggestion de Gyroninja sur les techniques de métabille), mais difficile.

Une fois qu'une tuile est placée dans le monde, déplacez ses sommets à l'aide des fonctions de bruit appliquées dans l'espace mondial. Cela préserve la connectivité et la transparence entre les tuiles (puisque les sommets coïncidents ont la même entrée d'espace mondial et obtiennent la même sortie de déplacement), mais donne à chaque tuile un aspect unique et organique:

C'est plus intéressant

La texture et les normales sont également appliquées dans l'espace mondial - ici en utilisant la cartographie triplanaire - de sorte que les carreaux adjacents sont complètement sans soudure, sans contraintes de déballage UV difficiles.

Aussi plus intéressant

L'espoir est qu'une technique comme celle-ci vous donne la facilité de planification et de contrôle de conception de niveau d'une carte carrelée, sans répétition visible ni structure d'aspect mécanique dans le résultat jouable.

Vous pouvez utiliser un maillage de résolution inférieure avec uniquement les composants de bruit basse fréquence appliqués pour créer la représentation de collision. Comme le note bcrist, vous devrez contrôler l'amplitude maximale du bruit par rapport au rayon et à la netteté des virages du tunnel, pour vous assurer qu'il ne se coince jamais complètement.

Une autre remarque: si votre grotte est vraiment infinie, vous devrez peut-être la "recentrer" périodiquement à mesure que le joueur s'éloigne de plus en plus de l'origine. Étant donné que les nombres à virgule flottante perdent en précision à des magnitudes élevées, la physique et les artefacts de rendu peuvent s'introduire à des distances extrêmes. Si vous faites cela, vous souhaiterez que votre bruit d'espace mondial soit périodique sur une grande échelle, avec la période correspondant exactement à votre décalage de recentrage, afin que vous ne rencontriez pas de coutures après recentrage.


2

Vous pouvez modéliser votre grotte comme une séquence de points, chacun avec une taille associée, avec des lignes les reliant. Ensuite , traiter chaque point et la ligne comme metaballs et metacylinders. Cela vous donne une forme de base pour votre grotte, à laquelle vous voudrez peut-être commencer à ajouter des variations, par exemple en décalant les sommets de manière aléatoire.


2

Voici une autre approche de la génération procédurale qui n'a pas encore été explicitement mentionnée: le skin spline. Vous pouvez utiliser une version de Hermite Splines(qui fournissent une courbe interpolant les positions et les tangentes) pour définir les courbes: quand il est temps de générer un nouveau segment, il suffit de choisir une position (à peu près dans la direction du segment précédent, comme le dit bcrist) et une direction (à peu près dans la même direction - par exemple, dans un cône bien défini de la direction précédente), puis utilisez la nouvelle position + direction et votre position + direction précédente pour construire une nouvelle «colonne vertébrale» pour votre grotte. Une fois que vous avez cette épine dorsale, vous pouvez la revêtir d'une construction cylindrique: déterminez les positions et les tangentes de (par exemple) 10 points le long de la courbe, utilisez ces positions / tangentes pour trouver un `` cadre '' orthogonal, puis utilisez ces cadres pour construire des segments cylindriques. Une petite mise en garde à ce sujet est que la grotte ne peut pas trop se courber beaucoup, ou bien vous pouvez rencontrer des problèmes d'auto-intersection.

EDIT: Voici une ventilation approximative du pseudocode de l'algorithme:

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
}

C'est évidemment un pseudocode très grossier; s'il y en a un qui n'est pas clair, faites le moi savoir et je vais essayer de l'expliquer, mais il va être difficile de couvrir tous les détails sans juste un énorme vidage de code ...


(Soit dit en passant, si vous voulez un pseudocode pour cette approche, faites-le moi savoir; j'ai dû faire quelque chose comme ça lors d'un travail précédent, donc j'ai fini par travailler sur tous les petits détails.)
Steven Stadnicki

Je serais curieux de voir votre implémentation; J'ai également fait quelque chose de similaire une fois, mais en utilisant des courbes de Bézier cubiques 3D à la place.
bcrist le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.