衝突判定の再考、壁抜けを止める：「はじめてのUnity」のブロック崩しを改造しながら学ぶ

#この解説は
Unityのサイトに昔からあるチュートリアルに、以下があります。
[「はじめてのUnity」(旧）
http://tutorial.unity3d.jp/archive/my-first-unity/](http://tutorial.unity3d.jp/archive/my-first-unity/)
これを独自にC#化したものをベースとしています。以下を
この解説は以下の段階があります、前の内容を実行した上でのものとして参照して下さい。現時点(20151026)では、[新しい「はじめてのUnity」の玉転がしゲーム](https://github.com/unity3d-jp/FirstTutorial/wiki)が公開されています。本サイトのブロック崩しと併せて学習すると広がりがあり、初期の学習にはとても有効です。


* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(1)ステージ配置](http://qiita.com/JunShimura/items/cbb0db8087a5cc75735e)
* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(2)色を変える](http://qiita.com/JunShimura/items/1b2b4842e983022075de )
* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(3)動くボール](http://qiita.com/JunShimura/items/dfb2bdbbc328869f62ec )
* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(4)ラケットを動かす]
(http://qiita.com/JunShimura/items/5fac20966bba6b223427)
* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(5)消えるブロック](http://qiita.com/JunShimura/items/867179092a2a564bc88c)
* [[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(6)ブロックを手作業で並べる](http://qiita.com/JunShimura/items/1f39e52414171c67bc71)

#衝突判定の再考
現状での動作はほぼ問題ありませんが、内容を考えなくてはならない部分があります。壁にめり込んだり、抜けたりする場合です。
ラケットの速度は、以下の設定で変わります。

* `Racket`の`Rigidbody`コンポーネント内にある`Mass`が小さいと軽くなって速くなる
* `Racket.cs`にある変数`accel`の値が大きいと、加えられる力が大きくて速くなる

これらを修正して、ゲームに合った心地よい操作感を実現していきますが、速くすると、ラケットを左右に振るったときに、壁にはまる、或いは壁の外に出る時があります。
[![Gyazo](https://i.gyazo.com/294cc9785e4be6b395475705faf21347.png)](https://gyazo.com/294cc9785e4be6b395475705faf21347)

現実の堅さのある物体であればこうした現象は発生しませんが、ゲーム内では発生します。この現象は何故起きるのでしょうか。

##衝突判定の仕組み
Unityでは衝突判定を、一定時間ごとに以下で行っています。

__`Collider`コンポーネント同士の接触__

で判定しています。

##Colliderの状態を確認
`LeftWall`と`Racket`はそれぞれ`Box Collider`コンポーネントをそれぞれ、持っています。
この図は、`racket`のものです。
[![Gyazo](https://i.gyazo.com/ab6b818e0ddbaa099d8001163b4f152d.png)](https://gyazo.com/ab6b818e0ddbaa099d8001163b4f152d)

デフォルトの設定では、Unity内で創りだして置いたCube型の`GameObject`は`Collider`の大きさは、見えている外観と同じ物が与えられています。形を確認したい場合は、`Box Collider`コンポーネントの[Edit Collider]ボタンを押してみてください。大きさや位置の変更が可能です。
[![Gyazo](https://i.gyazo.com/fe35b6d9e2e86bcc77affb7f685649ac.png)](https://gyazo.com/fe35b6d9e2e86bcc77affb7f685649ac)

壁にはまっている状態では、`Rigidbody`コンポーネントで物理的な動きをしていますので本来、交差はできないので、どう対応するか分からない状態になっています。

##「一定時間」の定義
Unityでは物理判定の処理を、デフォルトでは0.02秒毎に処理します。この値は、[Edit]>[ProjectSettings]>[Time]で設定されており、変更も可能です。[Inspector]タブの`TimeManager`の`Fixed Timestep`がそれにあたります。
[![Gyazo](https://i.gyazo.com/f95e60e679161dbf729d1dd72337c246.png)](https://gyazo.com/f95e60e679161dbf729d1dd72337c246)

くわしくは以下を参照してください。
[Unity-マニュアル:タイムマネージャ
http://docs.unity3d.com/ja/current/Manual/class-TimeManager.html]
(http://docs.unity3d.com/ja/current/Manual/class-TimeManager.html)

細かければ緻密な処理は可能ですが、演算を多く必要とする可能性が高く、少ないと粗い動きになる可能性があります。

##抜けていく現象の原因
この一定時間の間隔で衝突判定をしていますが、その瞬間に交差しているかが問題になります。現状の抜けている原因は、速度が速いために、物理的に留まるべき状態を瞬間に検知できていないからです。
[![Gyazo](https://i.gyazo.com/117f46d52cd2fe444b746921113e733f.png)](https://gyazo.com/117f46d52cd2fe444b746921113e733f)

##解決の方法を模索する
現状の衝突判定を考えて対策する方法は、以下があります。

0. 衝突判定を瞬間毎ではなく継続性のあるものにする
0. `Fixed Timestep`の値を小さくして、物理処理の実行頻度を上げる
 * 【×】物理演算の回数がゲーム全体で増加するため、処理が鈍重になる可能性がある
 * 【×】他の物理演算に影響する
0. 抜けてはならない物体に対し内部に交差した状態になってしまった場合、強制的にScriptで座標を戻す
 * 【×】衝突判定を2度しているのが無駄になる
 * 【×】`Rigidbody`での物理的な動作が、強制的に変化させると異常になる
 * 【×】対象物によって判定が複雑になる（垂直・水平以外は面倒な数式になる）

この中で、選ぶべき方法は、__衝突判定を瞬間毎ではなく継続性のあるものにする__になります。

##衝突判定の方法を変える
`Racket`を選択し、[Inspector]タブで`Rigidbody`の`Collision Detection`を`Continuos`に変えます。

[![Gyazo](https://i.gyazo.com/4fde8b2bbba38bfc8a09c836ab112264.png)](https://gyazo.com/4fde8b2bbba38bfc8a09c836ab112264)

ここの部分の設定は、物体が動いている場合に抜けてしまう時、有効です。衝突判定したいもので、片方が動いている場合はこれを設定してください。この設定を行うと、移動中にどこかの`Collider`と衝突していれば、検知します。
この部分の設定をくわしく知りたいときは、以下を参照してください。

[Unity-マニュアル:リジッドボディ
http://docs.unity3d.com/ja/current/Manual/class-Rigidbody.html]
(http://docs.unity3d.com/ja/current/Manual/class-Rigidbody.html)

***
#まとめ

* 衝突の判定は
 * 対象を`Collider`同士で行われる
 * タイミングは一定時間で制御している(`Fixed Timestep`)
* 衝突判定は瞬間ごとで抜けているために、求める状態にならない場合がある
* 衝突判定を移動体に対して継続的にしたい場合、対静物では`Collision Detection`を`Continuos`に