Search Unity

Tile/prefab based terrain generation

Discussion in 'World Building' started by ProtagonistKun, Nov 28, 2018.

  1. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    Hello,

    I have a sizeable list of prefabs that are made to generate my terrain with (level tiles).
    What I want to do is check whether or not a prefab is allowed to stand next to another one.
    I thought this could be done by looking at the faces and checking if they overlap (also same size)

    In an example where 2 boxes are considered, both are the same size with a diagonal top side, meaning one side is half the size of its opposite. only the faces that are the same dimensions should be considered to be placed next to each other.

    This way the tiles can be placed, based on the faces themselves. Removing the need to specify every side of every block with every possible connection and its orientation, which sounds very inefficient.

    The problem is I have no idea how to get started on this (or if my idea would be performant at runtime).
    Therefore I came here to ask if anyone has any guidance on how I can approach this.
     
  2. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    I've tried to do this but for some reason the Debug.Log is outputted 72 times. while there are 24 vertices (and 6 faces). Anyone got any leads on what I am doing wrong here

    Code (CSharp):
    1. public class LevelGenerator : MonoBehaviour
    2. {
    3.     public GameObject other;
    4.  
    5.     void Start()
    6.     {
    7.         var meshComponent = GetComponent<MeshFilter>();
    8.         var mesh2 = other.GetComponent<MeshFilter>();
    9.  
    10.         var vertices = meshComponent.mesh.vertices;
    11.         var vertices2 = mesh2.mesh.vertices;
    12.  
    13.         for (int i = 0; i < vertices.Length; i++)
    14.         {
    15.             vertices[i] += transform.position;
    16.         }
    17.  
    18.         for (int i = 0; i < vertices2.Length; i++)
    19.         {
    20.             vertices2[i] += other.transform.position;
    21.         }
    22.  
    23.         foreach (var t1 in vertices)
    24.         {
    25.             foreach (var t in vertices2)
    26.             {
    27.                 if (t1 == t)
    28.                 {
    29.                     Debug.Log("Found");
    30.                 }
    31.             }
    32.         }
    33.     }
    34. }
     
  3. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    You might be better off using standard tile sizes and raycasts from the center toward whatever side you're checking, usually against a temporary box/mesh collider that you might scale.

    This has the added advantage that it isn't necessarily based directly on the mesh, which means it is very "prefabbable" (if that's not a word, it is now, lol) so that you can make your block behaviors easily this way. If this is edit time only, or only applied to small numbers of prefabs at a time, it should be performant enough too.

    I'd be curious to see your solution to this if you try it. Been tossing this around my head for some time. Just haven't had the time to implement it myself yet. :)
     
  4. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    It is meant for a relatively small amount, I would say about 50 max so it fills up the screen.
    I ve attached a picture of some of the meshes I plan to use. I want to generate these. Their pivot is centered to where they should attach so placing them in locations of 1 unit apart will have correct result. I don't think I can get a raycast to work for this as I need to know the position of the connecting polygon. Unless you can raycast on an element and get the face it hit instead of the object (which I don't think is an option...)
    upload_2018-12-2_15-5-31.png
     
  5. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    I haven't done this myself, but if I remember correctly, in the past you could get the triangle a raycast hit. You'd have to get the list of triangles first though.

    https://docs.unity3d.com/ScriptReference/RaycastHit-triangleIndex.html

    If this proves too complex (which it really does seem to be -- I'm not sure how you plan to differentiate the triangles on the mesh -- maybe perhaps by normal direction or vertex colors?), there is also a tutorial video on youtube that teaches how to make gizmos in the sceneview to "pick a prefab" by clicking on the side of a tile, which swaps it to another tile prefab by clicking on the custom gizmo. The gizmo's transform is positioned at a particular location on the "tile" (e.g. on any face or whatever). You could simply programmatically align the new prefab tile instance on the proper side/gizmo rather than replace the prefab with the gizmo. Using multiple gizmos on a single tile (which disappear when a side/face is clicked via gizmo) could quickly be a bit of a low-tech way of doing it (especially since you only have a small number of these), which could save you a lot of time in the short-term. -- Just an idea.
     
  6. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    I tried the raycasting but working with the triangles was a bit counterintuitive. So I tried to use what I had before attempting to work out a solution. Appearantly I was using the wrong way of converting coordinates from local to world space. I also plan to store the objects and dont do these calculations at runtime.

    When I execute the below code on the objects in the image, I get 4 matching vertices. Which is the correct amount. The next step is to complicate the object and see if it still works
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class LevelGenerator : MonoBehaviour
    5. {
    6.     public GameObject Other;
    7.  
    8.     Vector3[] GetVerticesWorldPosition(MeshFilter meshFilter)
    9.     {
    10.         var vertices = meshFilter.mesh.vertices;
    11.  
    12.         for (int i = 0; i < vertices.Length; i++)
    13.         {
    14.             vertices[i] = meshFilter.transform.TransformPoint(vertices[i]);
    15.         }
    16.  
    17.         return vertices;
    18.     }
    19.  
    20.     List<Vector3> GetMatchingVertices(GameObject first, GameObject second)
    21.     {
    22.         List<Vector3> vertPositions = new List<Vector3>();
    23.  
    24.         var verticesFirst = GetVerticesWorldPosition(first.GetComponent<MeshFilter>());
    25.         var verticesSecond = GetVerticesWorldPosition(second.GetComponent<MeshFilter>());
    26.  
    27.         foreach (var v1 in verticesFirst)
    28.         {
    29.             foreach (var v2 in verticesSecond)
    30.             {
    31.                 if (v1 == v2)
    32.                 {
    33.                     vertPositions.AddIfNotContains(v1);
    34.                     break;
    35.                 }
    36.             }
    37.         }
    38.  
    39.         return vertPositions;
    40.     }
    41.  
    42.     void OnDrawGizmos()
    43.     {
    44.         var matching = GetMatchingVertices(gameObject, Other);
    45.  
    46.         foreach (var vector3 in matching)
    47.         {
    48.             Gizmos.color = Color.red;
    49.             Gizmos.DrawSphere(vector3, 0.05f);
    50.         }
    51.     }
    52. }
    53.  
    upload_2018-12-4_11-3-17.png
     
    Last edited: Dec 4, 2018
  7. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    How was this working out for you? Did you find a way to do this with more complex geo?
     
  8. ProtagonistKun

    ProtagonistKun

    Joined:
    Nov 26, 2015
    Posts:
    352
    This works! One minor change however was to exchange the vertex position comparison to a distance check. This gives me a certain range of tolerance and increases the precision. It doesnt matter how many vertices or how big the mesh is. this will work (although the bigger the mesh, the long it would take to calculate the vertices) but i ll cross that bridge when I get there