Unity – C# – Overlapping Rectangles not separating

Unity – C# – Overlapping Rectangles not separating

I am Instantiating a lot of Rectangles of different sizes (ranging from 3 to 18 units) and they all have attached this script, which should cycle through all the GameObject List which contains them. 

It should automatically avoid any overlap, but unfortunately sometimes it gets stuck with some rectangles that remain moving and all the others stop. When this occurs, my program can't continue and blocks in an infinite loop.

Here's the script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Cell : MonoBehaviour {

    private float squareDistance;

    float xShift = 0;
    float yShift = 0;

    //float globalStrength = 0;

    public bool hasStopped = false;

    private Vector2 oldPos = new Vector2();
    //private Vector2 oldPos2 = new Vector2();

    public Vector3 direction;

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

        if(Placer.work) {
            float strength;

            List cells = Placer.cells;

            for (int i=0; i < cells.Count; i++) {
                if(cells[i] != this.gameObject) {
                    if(isOverlapping(cells[i])) {

                        direction = transform.position - cells[i].transform.position;

                        strength = 1; //+ globalStrength;

                        /*if(globalStrength > 0) {
                            globalStrength = 0;
                        }*/

                        direction.Normalize();

                        xShift += strength * direction.x;
                        yShift += strength * direction.y;

                    }
                }
            }

            transform.position = new Vector3(Mathf.Round(transform.position.x + xShift), Mathf.Round(transform.position.y + yShift), transform.position.z);

            xShift = 0;
            yShift = 0;

            if (transform.position.x == oldPos.x && transform.position.y == oldPos.y) {
                hasStopped = true;
            } else {
                hasStopped = false;
            }
            //oldPos2 = oldPos;

            /*if(oldPos2.x == transform.position.x && oldPos2.y == transform.position.y) {
                globalStrength = 2;
                Debug.Log ("si2");
            }*/

            oldPos = new Vector2 (transform.position.x, transform.position.y);

            /*if(oldPos2.x == oldPos.x && oldPos2.y == oldPos.y) {
                globalStrength = 3;
                Debug.Log ("Si");
            }*/
        }
    }

    private bool isOverlapping(GameObject obj) {
        Vector2 size = new Vector2 (GetComponent ().bounds.size.x, GetComponent ().bounds.size.y);

        if (pointInside (obj, new Vector2 (transform.position.x + 1 - size.x / 2, transform.position.y + 1 - size.y / 2))) {
            return true;
        }

        if (pointInside (obj, new Vector2 (transform.position.x - 1 + size.x / 2, transform.position.y + 1 - size.y / 2))) {
            return true;
        }

        if (pointInside (obj, new Vector2 (transform.position.x + 1 - size.x / 2, transform.position.y - 1 + size.y / 2))) {
            return true;
        }

        if (pointInside (obj, new Vector2 (transform.position.x - 1 + size.x / 2, transform.position.y - 1 + size.y / 2))) {
            return true;
        }

        return false;

    }

    /**
     * With this method we verify if a Vector2 is inside a GameObject.
    */
    private bool pointInside(GameObject obj, Vector2 point) {

        Vector2 objSize = new Vector2 (obj.GetComponent ().bounds.size.x, obj.GetComponent ().bounds.size.y);
        if ((point.x >= (obj.transform.position.x - objSize.x / 2)) &&
            (point.x <= (obj.transform.position.x + objSize.x / 2)) &&
            (point.y >= (obj.transform.position.y - objSize.y / 2)) &&
            (point.y <= (obj.transform.position.y + objSize.y / 2))) {

            return true;
        }

        return false;
    }

    public bool getHasStopped() {
        return hasStopped;
    }

}

I have tried with some workarounds, which you can see commented in the code; none of them fixes completely the problem.
What can I do to fix it?

Solutions/Answers:

Answer 1:

You seem to have rectangles that are sandwiched:

  1. You have rectangles A, B and C
  2. A directly above and is overlapping B, which is above and overlapping C.
  3. The update for A is done, pushes it a bit up.
  4. The update for B is done, pushes it a bit up because of C, and a bit down because of A.
  5. B hasStopped because it did not move because your [x|y]Shift is cumulative for all of the neighbours.
  6. Deadlock because this is the situation of a chain in your rectangles.

You might want to try and make sure you 'de-overlap' them one by one right when you create them.

Or you could try and push them only further away from the centre, never closer to it.

References