2D game spawn overlapping

Hello,

apologies in advance if it’s a question that has been asked before by others. I have tried to implement the suggested methods but it still did not work. I am trying to make sure there is no overlap when the cards are spawned.

The scene produces some cards, 10 here in the picture below, one by one, in random positions. When the button is clicked, it produces new cards again.

For anyone interested in checking the working code that produces the behavior described:

public class Overlap : MonoBehaviour
{
 
   float xRandomPos, yRandomPos;
   Vector3 finalRandomPos;
   public int numToGenerate;
   public List<GameObject> listOfObjects;
   public GameObject background;
   MeshCollider borders;

void Start()
    {
        borders = background.GetComponent<MeshCollider>();
        WrappedCoroutine();
    }

    /// <summary>
    /// For the Button to click
    /// </summary>
    public void WrappedCoroutine()
    {
        StartCoroutine(Coroutine());
    }

    /// <summary>
    /// Main method
    /// </summary>
    /// <returns></returns>
    public IEnumerator Coroutine()
    {
        //coroutine
        WaitForSeconds wait = new WaitForSeconds(1f);

        //what to generate
        int randomItemFromListIndex;
        GameObject randomItemFromList;
for (int i = 0; i < numToGenerate; i++)
        {
   
                //the object
                randomItemFromListIndex = Random.Range(0, listOfObjects.Count);
                randomItemFromList = listOfObjects[randomItemFromListIndex];

                //where
                xRandomPos = Random.Range(borders.bounds.min.x, borders.bounds.max.x);
                yRandomPos = Random.Range(borders.bounds.min.y, borders.bounds.max.y);
                finalRandomPos = new Vector3(xRandomPos, yRandomPos, 0f);
                //setUpperLower(finalRandomPos);
       
                //randomItemFromList.transform.rotation same as Quaternion.identity?
                Instantiate(randomItemFromList, finalRandomPos, Quaternion.identity);
                yield return wait;
       
        }
    }

    /// <summary>
    /// Restarts the current scene when the button is clicked
    /// </summary>
    public void Restart()
    {
        SceneManager.LoadScene("Overlap");
    }

}

Now, this below was the first attempt to modify the same code, so that it would not overlap, and it did not work:

public class Overlap : MonoBehaviour
{
    //------------------------------new changes in attempt to avoid overlappping------------------------
    public Collider2D[] colliders;
    public float radius;
    int safetyNet = 0;
    //---------------------------------------------------------------------------------------------------
 
    float xRandomPos, yRandomPos;
    Vector3 finalRandomPos;
    public int numToGenerate;
    public List<GameObject> listOfObjects;
    public GameObject background;
    MeshCollider borders;
 
    void Start()
    {
        borders = background.GetComponent<MeshCollider>();
        WrappedCoroutine();
    }

    /// <summary>
    /// For the Button to click
    /// </summary>
    public void WrappedCoroutine()
    {
        StartCoroutine(Coroutine());
    }

    /// <summary>
    /// Main method
    /// </summary>
    /// <returns></returns>
    public IEnumerator Coroutine()
    {
        //coroutine
        WaitForSeconds wait = new WaitForSeconds(1f);

        //what to generate
        int randomItemFromListIndex;
        GameObject randomItemFromList;


        for (int i = 0; i < numToGenerate; i++)
        {
   
                //the object
                randomItemFromListIndex = Random.Range(0, listOfObjects.Count);
                randomItemFromList = listOfObjects[randomItemFromListIndex];

                //where
                xRandomPos = Random.Range(borders.bounds.min.x, borders.bounds.max.x);
                yRandomPos = Random.Range(borders.bounds.min.y, borders.bounds.max.y);
                finalRandomPos = new Vector3(xRandomPos, yRandomPos, 0f);

                //------------------------------new changes in attempt to avoid overlappping------------------------
                if (PreventOverlap(finalRandomPos))
                {
                    Instantiate(randomItemFromList, finalRandomPos, Quaternion.identity);
                    yield return wait;
                }
                //---------------------------------------------------------------------------------------------------

        }
    }

 
 
    //------------------------------new changes in attempt to avoid overlappping------------------------
    bool PreventOverlap(Vector3 finalRandomPos)
    {
        //NB: it's an array
        colliders = Physics2D.OverlapCircleAll(transform.position, radius);
        for (int i = 0; i < colliders.Length; i++)
        {
            //checking the bounds of each collider and
            //return false if the intended generating position falls within the bounds
            Vector3 centerPoint = colliders[i].bounds.center;
            float width = colliders[i].bounds.extents.x;
            float height = colliders[i].bounds.extents.y;
            //from which the main points can be derived
            float left = centerPoint.x - width;
            float right = centerPoint.x + width;
            float top = centerPoint.y - height;
            float bottom = centerPoint.y + height;
   
            //if it's falling within the left and right or top and bottom
            if (finalRandomPos.x >= left && finalRandomPos.x <= right)
            {
                //not here
                return false;
            }
            if(finalRandomPos.y >= top && finalRandomPos.y <= bottom)
            {
                //not here
                return false;
            }
        }
        return true;
    }
     //-----------------------------------------------------------------------------------------
 
    /// <summary>
    /// Restarts the current scene when the button is clicked
    /// </summary>
    public void Restart()
    {
        SceneManager.LoadScene("Overlap");
    }

}

The code above does not produce any changes to the program. Any suggestions as to where it might have gone wrong? Alternatively, this below again is another unsuccessful attempt to modify the code:

public class Overlap : MonoBehaviour
{
    //------------------------------new changes in attempt to avoid overlappping------------------------
    //min------of the rectangle we want to check for collision
    Vector2 upperLeft = new Vector2();
    //max------of the rectangle we want to check for collision
    Vector2 lowerRight = new Vector2();
    //we will set the appropriate x and y for these when the time is right: more efficient from a memory perspective

    float xRandomPos, yRandomPos, halfWidth, halfHeight;

    //---------------------------------------------------------------------------------------------------
 
    Vector3 finalRandomPos;
    public int numToGenerate;
    public List<GameObject> listOfObjects;
    public GameObject background;
    MeshCollider borders;
 
    void Start()
    {
        borders = background.GetComponent<MeshCollider>();
        WrappedCoroutine();
    }

    /// <summary>
    /// For the Button to click
    /// </summary>
    public void WrappedCoroutine()
    {
        StartCoroutine(Coroutine());
    }

    /// <summary>
    /// Main method
    /// </summary>
    /// <returns></returns>
    public IEnumerator Coroutine()
    {
        //coroutine
        WaitForSeconds wait = new WaitForSeconds(1f);

        //what to generate
        int randomItemFromListIndex;
        GameObject randomItemFromList;

        int i = 1;

        while(Physics2D.OverlapArea(upperLeft, lowerRight) != null && i< numToGenerate)
        {
   
                //the object
                randomItemFromListIndex = Random.Range(0, listOfObjects.Count);
                randomItemFromList = listOfObjects[randomItemFromListIndex];

                //where
                xRandomPos = Random.Range(borders.bounds.min.x, borders.bounds.max.x);
                yRandomPos = Random.Range(borders.bounds.min.y, borders.bounds.max.y);
                finalRandomPos = new Vector3(xRandomPos, yRandomPos, 0f);
       
                //------------------------------new changes in attempt to avoid overlappping------------------------
                setUpperLower(finalRandomPos);
                //---------------------------------------------------------------------------------------------------

                Instantiate(randomItemFromList, finalRandomPos, Quaternion.identity);
                    yield return wait;

            i++;
        }
    }

 
 
    //------------------------------new changes in attempt to avoid overlappping------------------------
    void setUpperLower(Vector3 pos)
    {
        upperLeft.x = pos.x - halfWidth;
        upperLeft.y = pos.y - halfHeight;
        lowerRight.x = pos.x + halfWidth;
        lowerRight.y = pos.y + halfHeight;
    }
     //-----------------------------------------------------------------------------------------
 
    /// <summary>
    /// Restarts the current scene when the button is clicked
    /// </summary>
    public void Restart()
    {
        SceneManager.LoadScene("Overlap");
    }

}

Unlike the previous attempt, the code below does not produce any card at all.

Any suggestion as to how I can modify the code and where I am understanding this wrong would be much appreciated!

1.- Wow, so much code, that doesn’t encourage anyone to help you.
2.- Plz reference us wheres ur previous question.
3.- What do you want exactly to do. It might explained at your previous post but not at this one. You wanna spawn x cards at random positions without overlapping anyone of them?

You can use poisson disc sampling

1 Like
  1. I’m not sure about ‘anyone’ but If you are not interested in checking the code, feel free to give suggestion on the topic as long as it’s relevant without going through it. It’s not some random code, but code that explains the behavior of the working program and the same modified code twice that shows how non-overlapping behavior has been attempted, as explained in the question. I posted them in case someone wanted to see the reasoning I am attempting and thought that might help others to understand the process.
  2. I don’t have any previous questions on the topic. I meant that it is a common problem that has been asked previously and those were my attempts
  3. yes

You could use a while loop for this however it’s probably not a good approach as you’re opening yourself up to infinite loops.

Basically the syntax would be;

for (int i = 0; i < NumToGenerate; i++)
{
    TrySpawnCard();
 }

void TrySpawnCard(){

    var randPosition = //pick random position

    bool spawned = false;

    while(!spawned){
       
        foreach (var card in cardsAlreadySpawned)
        {
            if(Vector2.Distance(randPosition, card.transform.position) > minimumDistance)
                continue
            else{
                spawned = true;
                SpawnCard(randPosition);
                return;
            }
        }
    }
}

void SpawnCard(position){
    //Do spawning here
    // Add it to list of cardsAlreadySpawned
}

Again I would look for other solutions here this is pretty messy.

For a collision based approach (convert it to C#):

1 Like