【Unity】物理エンジンと衝突判定を使って重なった正方形をキレイに並べる

物理エンジンを有効にした正方形を同じ座標に複数生成したあと、衝突判定で吹き飛ばして重なりなく並べてみました

■ 完成品
![test3.gif](https://qiita-image-store.s3.amazonaws.com/0/65312/2a077acf-fd40-fca9-1fe6-a89df37e1842.gif)



# 普通に物理エンジンを使ったら爆発バグが起きた
```csharp:物理エンジンを有効なcubeを大量配置するコード
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class main : MonoBehaviour {
	List<GameObject> cubes = new List<GameObject>();
	const string CubeTag = "Player";

	// Use this for initialization
	void Start () {
		for (int y = 0; y < 5; y++) {
			for (int x = 0; x < 5; x++) {
				GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
				cube.tag = CubeTag;
				cube.AddComponent<Cube>();
				cube.AddComponent<Rigidbody>();

				// rigidbody
				Rigidbody r = cube.GetComponent<Rigidbody>();
				r.useGravity = false;

				// change color
				cube.renderer.material.color = getRandomColor();

				// rotate
				cube.transform.Rotate(90, 180, 0);
				// scale
				cube.transform.localScale = new Vector3(100, 100, 100);
				cubes.Add(cube);
			}
		}		
	}
	
	// Update is called once per frame
	void Update () {
	}

	Color getRandomColor(){
		return new Color( Random.value, Random.value, Random.value, 1.0f );
	}
}


```

■ 実行結果
爆発した。違うこうじゃない(＇σ＇)
Z軸移動、回転、慣性移動が発生してしまったので削る必要があります。
![test2.gif](https://qiita-image-store.s3.amazonaws.com/0/65312/0f9de74f-0064-86d1-6939-4a94d6f7a850.gif)

# 衝突判定を自分で書いていく
標準の物理エンジンだとやりたいことと違う現象が発生してしまったので、仕方なく`BoxCollider.isTrigger = true;`と設定してUnity標準の物理エンジンをoffにして地道に衝突判定を書くことにしました。

```csharp:衝突判定を自分で実装したコード,main.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class main : MonoBehaviour {
	List<GameObject> cubes = new List<GameObject>();
	const string CubeTag = "Player";

	// Use this for initialization
	void Start () {
		for (int y = 0; y < 5; y++) {
			for (int x = 0; x < 5; x++) {
				GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
				cube.tag = CubeTag;
				cube.AddComponent<Cube>();
				cube.AddComponent<Rigidbody>();

				// collider
				BoxCollider b = cube.GetComponent<Collider>() as BoxCollider;
				b.isTrigger = true;
				b.size = new Vector3(99, 99, 99);

				// rigidbody
				Rigidbody r = cube.GetComponent<Rigidbody>();
				r.useGravity = false;

				// change color
				cube.renderer.material.color = getRandomColor();

				// rotate
				cube.transform.Rotate(90, 180, 0);
				// scale
				cube.transform.localScale = new Vector3(100, 100, 100);
				cubes.Add(cube);
			}
		}		
	}
	
	// Update is called once per frame
	void Update () {
	}
	
	Color getRandomColor(){
		return new Color( Random.value, Random.value, Random.value, 1.0f );
	}
}



```

```csharp:衝突判定を自分で実装したコード,Cube.cs
using UnityEngine;
using System.Collections;

public class Cube : MonoBehaviour {
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

	void OnTriggerEnter(Collider collider){
		Debug.LogWarning("OnTriggerEnter");
	}

	void OnTriggerStay(Collider collider){
		if (Random.Range (0, 100) != 0) {
			return;
		}

		Debug.LogWarning("OnTriggerStay");
		var pos = setRandomPosition(collider.gameObject.transform.localPosition); 

		// update position
		collider.gameObject.transform.localPosition = pos;
		// update collider position
		BoxCollider b = collider.gameObject.GetComponent<Collider>() as BoxCollider;
		b.center = pos;

		// update rigidbody positon
		Rigidbody r = collider.gameObject.GetComponent<Rigidbody>();
		r.transform.localPosition = pos;
	}

	void OnTriggerExit(Collider collider){
		Debug.LogWarning("OnTriggerExit");
	}

	Vector3 setRandomPosition(Vector3 pos){
		int Point = 100;

		// 0,0,0座標のときはランダム移動
		if (pos.x == 0 && pos.y == 0) {
			if (Random.Range (0, 2) == 0) {
					if (Random.Range (0, 2) == 0) {
							pos.x += Point;
					} else {
							pos.x -= Point;
					}
			} else {
					if (Random.Range (0, 2) == 0) {
							pos.y += Point;
					} else {
							pos.y -= Point;
					}
			}
			return pos;
		}

		// 移動方向が0,0,0座標からずれていたら、移動ベクトルをその方向に固定する。
		if (Random.Range (0, 2) == 0) {
			if (pos.x > 0) {
					pos.x += Point;
			} else {
					pos.x -= Point;
			}
		} else {
			if (pos.y > 0) {
					pos.y += Point;
			} else {
					pos.y -= Point;
			}
		}
		return pos;
	}

}

```

# まとめ
簡単に衝突判定が書けるUnityの強力さにビックリしました。
1. 衝突判定が有効なイベント`OnTriggerStay`を発生させるにはGameObjectに`Collider`と`Rigidbody`が必要
2. Unity標準の物理エンジンをOffにするには`BoxCollider.isTrigger = true;`と設定する
3. 俺々衝突判定をappendするには`cube.AddComponent<Cube>();`と定義する