UnityのRigidbodyとColliderで衝突判定

# 動画とスライド

勉強会の記事がブログにあります。(日本法人閉鎖により消滅した模様)
~~http://www.engineyard.co.jp/blog/2014/shimokita-unity-rigidbody/~~

- **動画での説明**
  - https://livestream.com/accounts/1839740/events/3063002/videos/53225198
- スライド
  - https://speakerdeck.com/yandod/rigidbodytocolliderdechong-tu-pan-ding-shimokita-dot-unity
- イベント
  -  https://www.facebook.com/events/335744853239169/  
- 記事のキャッシュ
  - https://web-beta.archive.org/web/20160509145105/http://www.engineyard.co.jp:80/blog/2014/shimokita-unity-rigidbody/

# サンプルシーン

![Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg](https://qiita-image-store.s3.amazonaws.com/0/10032/381035a1-ef48-0dd3-747b-d34c3311b108.jpeg "Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg")

https://dl.dropboxusercontent.com/u/6626376/Unity/RigidbodyAndCollision.zip

# Rigidbodyでできること

- 重力、加速度を使った処理
- 衝突した物体を動かす、衝突された物体の動きに作用する
- 衝突発生時に任意の処理を実行する

# 自然落下

Sampleシーンを開く。下記のオブジェクトにAdd Componentをクリックして<code>Physics > Rigidbody</code>を設定。

![Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg](https://qiita-image-store.s3.amazonaws.com/0/10032/615addca-e317-b37e-c9c3-860941ce19d1.jpeg "Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg")

## 設定項目の意味

|  項目  |   意味|
|:------|-------|
|Mass|重さ。単位はキログラム|
|Drag|空気抵抗|
|Angular Drag|回転に対する空気抵抗|
|Use Gravity|重力を使う|
|Is Kinematic|物理演算を無効にする|
|Interpolate|フレーム間の補間|
|Collision Detection|衝突検知モード。高速で動く物体には変更|
|Constraits|移動や回転を軸ごとに無効にする|

## 地面が落ちてしまう場合

Rigidbodyは物理演算で動作させるオブジェクトに設定する。壁や床はRigidbodyを付ける必要はない。

## 地面をすり抜けてしまう場合

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/dfa8f792-0796-112b-5058-5b219f9a0408.gif "Untitled.gif")


アセットや変換したモデルがすり抜けてしまう場合はオブジェクトに<code>Collider</code>が設定されていないのが原因。重力で落下した物体が地面に着地するにはそれぞれに下記のコンポーネントが必要になる。
CubeやSphereなどのプリミティブには最初から<code>Collider</code>が付いている。

- 落下する物体
	- 物体の形に応じた<code>Collider</code>
	- 重力が設定された<code>Rigidbody</code>
- 受け止める地面
	- 地面の形に応じた<code>Collider</code>

すり抜けて落下していく物体には<code>Physics > BoxCollider</code>などのコンポーネントを設定する。入れ子になっているオブジェクトの場合はそれぞれの子オブジェクトに指定するか、親オブジェクトに指定する。子オブジェクトにColliderを設定した場合は合成した形で親オブジェクトに適用される。

コライダーはシーンでオブジェクトをクリックするとワイヤフレームで表示される。

![例](http://docs-jp.unity3d.com/Documentation/Images/manual/class-Rigidbody-2.jpg)

球体にあえて四角形の判定をつけたりすることも出来る。
![Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg](https://qiita-image-store.s3.amazonaws.com/0/10032/90ce4bfc-c6ed-865f-c95f-50aa07484b83.jpeg "Step1_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg")

レンダラーを無効にすれば表示されないが、判定はあるということも可能。

# 力を加える

<code>Rigidbody</code>が設定されたオブジェクトにはスクリプトから力(Force)と回転(Torque)を加えることができる。

|メソッド|効果|
|:---|:---|
|AddForce|力を加える|
|AddRelativeForce|相対座標で力を加える|
|AddTorque|回転を加える|
|AddRelativeTorque|回転を加える|


力を加える時には<code>ForceMode</code>指定することでさらに挙動が変わる。

|モード|効果|
|:---|:---|
|Force|質量が反映する、連続的な力を加える|
|Acceleration|質量を考慮しない、連続的な加速を加える|
|Impulse|質量が反映する、瞬間的な力を加える|
|VelocityChange|質量を考慮しない、連続的な速度の変化を加える|


```js:ControlScript.js
#pragma strict

function Update () {

	var speed = 100;

	if (Input.GetKeyDown(KeyCode.LeftArrow)) {
		rigidbody.AddForce(Vector3.left * speed, ForceMode.Force);
	}
	
	if (Input.GetKeyDown(KeyCode.RightArrow)) {
		rigidbody.AddForce(Vector3.right * speed, ForceMode.Force);
	}
	
}
```

Forcemode.Forceの場合は力を加えて揺らすようなイメージ。

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/893be53a-8c78-8e59-6ea3-0ce509efe71c.gif "Untitled.gif")

Forcemode.VelocityChangeは操作に応じてひとまず動きに反映されるイメージ。

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/c053aa46-c31c-f711-f9d3-1eb7ac774fb1.gif "Untitled.gif")


ForceModeやspeedを調整してみるとさまざまな動きをする。力を加えると意図しない回転をしてしまうことも多い。プレイヤーなど操作を反映させる場合は不都合な場合も。不都合がな場合はConstraitsで回転を封じる。

```javascript:上下左右に動く例	
#pragma strict

function Update () {

	var speed = 3;

	if (Input.GetKeyDown(KeyCode.LeftArrow)) {
		rigidbody.AddForce(Vector3.left * speed, ForceMode.VelocityChange);
	}
	
	if (Input.GetKeyDown(KeyCode.RightArrow)) {
		rigidbody.AddForce(Vector3.right * speed, ForceMode.VelocityChange);
	}

	if (Input.GetKeyDown(KeyCode.UpArrow)) {
		rigidbody.AddForce(Vector3.forward * speed, ForceMode.VelocityChange);
	}
	
	if (Input.GetKeyDown(KeyCode.DownArrow)) {
		rigidbody.AddForce(Vector3.back * speed, ForceMode.VelocityChange);
	}
	
}
```


# 衝突時に処理を行う

衝突が発生すると状況に応じて次のコールバックが実行される。それぞれの処理内で衝突の状況に応じて任意の処理を行うことで動きを作る。この際の処理はRigidbodyがメッセージ機能を使い、相手のオブジェクトと自分のオブジェクトの対応する処理を呼び出している。

|コールバック|動作|
|:---|:---|
|OnCollisionEnter|他のColliderやRigidbodyオブジェクトに触れた時に呼び出される|
|OnCollisionExit|他のColliderやRigidbodyオブジェクトに離れた時に呼び出される|
|OnCollisionStay|他のColliderやRigidbodyオブジェクトに触れ続けている時に呼び出される|

```UnityScript:衝突時に自分を消去
#pragma strict

function OnCollisionEnter(other:Collision) {

	Destroy(gameObject);

}
```

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/952e58b9-f17a-84cd-4d2d-0b7ce64a45c0.gif "Untitled.gif")

```UnityScript:衝突時に相手を削除
#pragma strict

function OnCollisionEnter(other:Collision) {

	Destroy(other.gameObject);

}
```

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/bcbf422f-ba52-0d40-444e-215d374e2a3a.gif "Untitled.gif")

## 衝突対象の判別

衝突対象に無差別に処理を行うのは得策ではないので、衝突対象を判別した上で処理を行う必要がある。そのためにはタグを使った判定を行うのがよい。タグはインスペクタから設定可能。デフォルトのタグの他に、任意のタグを追加可能。

![Step3_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone_と_Inbox_-_yandod_gmail_com_-_Gmail.jpg](https://qiita-image-store.s3.amazonaws.com/0/10032/94a2d138-ff76-41b0-6638-d97aba9475a5.jpeg "Step3_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone_と_Inbox_-_yandod_gmail_com_-_Gmail.jpg")

とりあえずプレイヤーと思われるプレゼントに<code>Player</code>を設定、衝突されるボールや箱側にスクリプトを付け、 **プレイヤーがぶつかってきた時** に処理を行うようにする。

```UnityScript:ボールなどに設定するスクリプト
#pragma strict

function OnCollisionEnter(other:Collision) {

	if (other.gameObject.CompareTag('Player')) {
		//move to this object to above.
		gameObject.transform.position = Vector3.up * 5;
	}
}
```

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/4093f6f7-eb88-66fd-4098-7341d2936ccc.gif "Untitled.gif")


# 衝突はさせずに処理を行う

ゴールエリアや風が吹くエリア、など一定の範囲にいると処理を行うという場合にはTriggerを使う。具体的には<code>Collider</code>の<code>IsTrigger</code>をOnにすると衝突はしないが専用のコールバックが呼び出される。

![Step4_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg](https://qiita-image-store.s3.amazonaws.com/0/10032/aee91c4d-24eb-caac-9931-7049f0eb4f10.jpeg "Step4_unity_-_RigidbodyAndCollision_-_PC__Mac___Linux_Standalone.jpg")


|コールバック|動作|
|:---|:---|
|OnTriggerEnter|他のColliderやRigidbodyオブジェクトに触れた時に呼び出される|
|OnTriggerExit|他のColliderやRigidbodyオブジェクトに離れた時に呼び出される|
|OnTriggerStay|他のColliderやRigidbodyオブジェクトに触れ続けている時に呼び出される|

```UnityScript:触れてきたオブジェクトを右へ飛ばす
#pragma strict

function OnTriggerEnter(collider:Collider) {
	if (collider.gameObject.CompareTag('Player')) {
		return;
	}
	collider.gameObject.rigidbody.AddForce(
		Vector3.right * 30,
		ForceMode.Impulse
	);
	
}
```

![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/10032/6d649db1-7f0d-8917-a640-29b47ee45db2.gif "Untitled.gif")


# 参考資料

- http://docs-jp.unity3d.com/Documentation/ScriptReference/Rigidbody.html
- http://docs-jp.unity3d.com/Documentation/Components/class-Rigidbody.html
- [ゲームは初心者にやさしく: 必見！Unity初心者が学ぶ「Rigidbodyの正しい移動方法」](http://gamesonytablet.blogspot.jp/2012/12/unityrigidbody.html)