0

I am a bit of a noob at coding so sorry if I'm missing something obvious.

I am working on procedurally generating a junkyard of finite boundaries. I used a 2D unity tutorial (part 1 of 3 found here: https://www.youtube.com/watch?v=qAf9axsyijY&ab_channel=Blackthornprod ) to make a skeleton for my code but I have had issues with ground tiles spawning overlapping. I have tried a few fixes online but nothing gets the job completely done.

Physics.Overlap has made the code work much better but there are still regular overlaps. I suspect the overlapping tiles may be spawning at the exact same instance which makes the Overlap command return 0 colliders errantly, but I have no clue how to test that or avoid it.

Here is my code. It is almost a direct copy from the vid above.:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class spawnTile : MonoBehaviour
{
    public int openingDirection;
    //1 = N
    //2 = E
    //3 = S
    //4 = W

    private GroundTemplates templates;
    //Stores tiles to spawn

    private int rand;
    private bool spawned = false;
    private bool IsInBounds = false;
    public LayerMask checkBox;
    //Layermask to check if there is already ground at spawn point
    public Vector3 chunkScale;
    //size of check area for dup spawns
    void Start()
    {
        templates = GameObject.FindGameObjectWithTag("GroundTiles").GetComponent<GroundTemplates>();
        StartCoroutine(Spawn());
    }
    public IEnumerator Spawn()
    {
        Collider[] hitColliders = Physics.OverlapBox(transform.position, chunkScale , Quaternion.identity, checkBox); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
        yield return new WaitForSeconds(.2f); 
        Debug.Log("colliders: " + hitColliders.Length);
        if (spawned == false )
        {
            if (openingDirection == 1 && IsInBounds == true && hitColliders.Length == 0)
            {
                rand = Random.Range(0, templates.NorthRooms.Length);
                Instantiate(templates.NorthRooms[rand], transform.position, templates.NorthRooms[rand].transform.rotation);
            }
            else if (openingDirection == 2 && IsInBounds == true && hitColliders.Length == 0)
            {
                rand = Random.Range(0, templates.EastRooms.Length);
                Instantiate(templates.EastRooms[rand], transform.position, templates.EastRooms[rand].transform.rotation);
            }
            else if (openingDirection == 3 && IsInBounds == true && hitColliders.Length == 0)
            {
                rand = Random.Range(0, templates.SouthRooms.Length);
                Instantiate(templates.SouthRooms[rand], transform.position, templates.SouthRooms[rand].transform.rotation);
            }
            else if (openingDirection == 4 && IsInBounds == true && hitColliders.Length == 0)
            {
                rand = Random.Range(0, templates.WestRooms.Length);
                Instantiate(templates.WestRooms[rand], transform.position, templates.WestRooms[rand].transform.rotation);
            }
            spawned = true;
           
        }
    }



    private void OnTriggerEnter(Collider other)
    {
        
             if (other.CompareTag("Spawn") && (other.GetComponent<spawnTile>().spawned == true))
        {
            Destroy(gameObject);
            Debug.Log("Destroyed");
        }
             else if (other.CompareTag("InBounds"))
        {
            IsInBounds = true;
        }
    }
}

Some pics of the output:

a generated map

two overlapping tiles

spawn point prefab that code is attached to

ground tile prefab example

I assume there are some redundancies in the code from multiple debugging attempts, so lmk if I can streamline things.

Overboard
  • 3
  • 1

2 Answers2

0

A quick san of the videos showed no indication that the original author use a Coroutine to generate the new tile.

For each tile you generate, you check to see if there's an overlap, then you wait 0.2 seconds. It's that waiting there that could very easily allow a different tile to also check the same space and find no tiles overlapping. So two or more tiles will see no overlaps, and all write to the same position.

Might I suggest cleaning up the code, remove that forced delay and trying this instead:

void Start ( )
{
    templates = GameObject.FindGameObjectWithTag ( "GroundTiles" ).GetComponent<GroundTemplates> ( );

    var hitColliders = Physics.OverlapBox ( transform.position, chunkScale, Quaternion.identity, checkBox ); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
    Debug.Log ( "colliders: " + hitColliders.Length );
    if ( !spawned )
    {
        if ( IsInBounds && hitColliders.Length == 0 )
        {
            GameObject [ ] rooms = null;
            switch ( openingDirection )
            {
                case 1: rooms = templates.NorthRooms; break;
                case 2: rooms = templates.EastRooms; break;
                case 3: rooms = templates.SouthRooms; break;
                case 4: rooms = templates.WestRooms; break;
                default: return;
            }

            var room = rooms [ UnityEngine.Random.Range ( 0, rooms.Length ) ];
            Instantiate ( room, transform.position, room.transform.rotation );
        }
        spawned = true;
    }
}
Milan Egon Votrubec
  • 3,696
  • 2
  • 10
  • 24
0

I got the code working with help from Milan in this thread. After using his code. I used Serialized fields to see what conditions weren't being met and found that the InBounds check wasn't passing. So I reworked the check so that it occurs in Start() rather than OnTriggerEnter();

I just came to this solution, so let me know if you have useful feedback.

Here is the new code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class betterTileSpawner : MonoBehaviour
{
    public int openingDirection;
    //1 = N
    //2 = E
    //3 = S
    //4 = W

    public LayerMask checkBox;
    //Layermask to check if there is already ground at spawn point
    public Vector3 chunkScale;
    //size of check area for dup spawns
    private Collider Boundarys;
    //Used for 
    private GroundTemplates templates;
    //Stores tiles to spawn

    private int rand;
    [SerializeField]
    private bool spawned = false;
    [SerializeField]
    private bool IsInBounds = false;
    [SerializeField]
    private string Helper;
    [SerializeField]
    private int NumColliders;

    IEnumerator Start()
    {
        templates = GameObject.FindGameObjectWithTag("GroundTiles").GetComponent<GroundTemplates>();
        yield return new WaitForSeconds(.01f);
        var hitColliders = Physics.OverlapBox(transform.position, chunkScale, Quaternion.identity, checkBox); // makes an array of objects inside chunk with layer mask checkBox. Used for checking if ground is present
        Boundarys = GameObject.FindGameObjectWithTag("InBounds").GetComponent<Collider>();
        Debug.Log("colliders: " + hitColliders.Length);
        NumColliders = hitColliders.Length;
        Helper = "didn't do any ifs";


        if (Boundarys.bounds.Contains(transform.position))
            { IsInBounds = true; }

        if (!spawned)
            {  

            Helper = "Not Spawned " + NumColliders;
            Debug.Log("Not Spawned");
                if (IsInBounds && hitColliders.Length == 0)
                {
                    GameObject[] rooms = null;
                        switch (openingDirection)
                        {
                            case 1: rooms = templates.NorthRooms; break;
                            case 2: rooms = templates.EastRooms; break;
                            case 3: rooms = templates.SouthRooms; break;
                            case 4: rooms = templates.WestRooms; break;
                            default: Debug.Log("Default case"); break;
                        }

                    var room = rooms[UnityEngine.Random.Range(0, rooms.Length)];
                    Instantiate(room, transform.position, room.transform.rotation);

                    Helper = "Should have spawned, wenth through code";
                    Debug.Log("Spawned");
                    spawned = true;
                }
            
            }
    }


    private void OnTriggerEnter(Collider other)
    {

        if (other.CompareTag("Spawn") && (other.GetComponent<betterTileSpawner>().spawned == true))
        {
            Destroy(gameObject);
            Debug.Log("Destroyed");
        }
        else if (other.CompareTag("Ground"))
        {
            Destroy(gameObject);
            Debug.Log("Destroyed");
        }
    }
}
Overboard
  • 3
  • 1