#はじめに
CharacterControllerは接地判定や、移動が簡単にできるのでとても便利です。
しかし、衝突の判定を行うとき、コールバックが行われないことがあったので調べてみました。
#実験
##準備
CharacterControllerにはOnControllerColliderHitというコールバック関数があります。
- OnControllerHit
- OnControllerColliderHit はキャラクターコントローラーが移動中にコライダーに衝突した際に、呼び出されます。
どうやらCharacterControllerにもOnCollisionEnterが実装されているようで、
衝突したときにはどちらが呼ばれるのでしょうか?
using UnityEngine;
public class PlayerCtrl : MonoBehaviour {
private CharacterController cc;
private float gravity = 100.0f;
void Start()
{
cc = GetComponent<CharacterController>();
}
void FixedUpdate()
{
// 重力を計算
var moveDirection = new Vector3(0f, -gravity * Time.fixedDeltaTime, 0f);
// CharacterControllerに渡す
cc.Move(moveDirection * Time.fixedDeltaTime);
}
private void OnCollisionEnter(Collision collision)
{
// 衝突した対象の名前を取得
var colName = collision.gameObject.name;
if (colName == "Enemy")
{
Debug.Log("OnCollisionEnter(Player)");
}
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
// 衝突した対象の名前を取得
var colName = hit.gameObject.name;
if (colName == "Enemy")
{
Debug.Log("OnControllerHit(Player)");
}
}
}
using UnityEngine;
public class EnemyCtrl : MonoBehaviour {
private void OnCollisionEnter(Collision collision)
{
// 衝突した対象の名前を取得
var colName = collision.gameObject.name;
if(colName == "Player")
{
Debug.Log("OnCollisionEnter(Enemy)");
}
}
}
##Playerをぶつけてみる
赤がPlayer、青がEnemyです。
PlayerにはCharacterControllerとPlayerCtrlのみアタッチされています。
EnemyにはRigidBodyとBoxCollider、EnemyCtrlがアタッチされています。
もちろんIsTriggerはfalseです。
PlayerをCharacterControllerで落下させ、Enemyにぶつけてみると...
上記のようにOnControllerHitが接触しているフレームの間呼び出され、
PlayerとEnemyのOnCollisionEnterは呼び出されていません。
##Enemyをぶつけてみる
次はEnemyをぶつけてみようと思います。
IsGravityをTrueにしてPlayerの上に落下させます。
するとPlayerとEnemyのOnCollisionEnterが呼び出されました。
こちらではPlayerのOnControllerHitは呼び出されず、一度しか呼び出されません。
#推測
##実行順序
ここでUnityのイベント関数の実行順を確認すると、
FixedUpdateの次にInternal physics Updateという処理があり、
ここでRigidBodyの計算がされるようです。
おそらくここでCharacterControllerの処理も計算されているのではないでしょうか?
[Unity-マニュアル:イベント関数の実行順]
(https://docs.unity3d.com/jp/540/Manual/ExecutionOrder.html)
おそらく計算の順番が
CharacterControllerのColliderの計算
↓
OnControllerHitのコールバック
↓
めり込まないよう位置の修正
↓
RigidBodyのColliderの計算
↓
OnCollisionEnterのコールバック
↓
めり込まないよう位置の修正
つまり、Playerをぶつけたときは、
OnControllerHitのコールバック後に位置が修正されているため、
RigidBodyの計算の際、衝突していないことになる(はず)
Enemyをぶつけたときは、
RigidBodyで移動が計算されるので、もちろんOnControllerHitは呼ばれず、
OnCollisionEnterのコールバックが呼ばれる。
##結論
対処法としては、
・そもそもCharacterControllerを使わない
・CharacterControllerと併用してIsTriggerのコライダを別に用意し、OnCollisionTriggerを呼ぶ
・CharacterControllerのキャラが移動した時のCollisionのコールバックしか呼ばないようにする
色々と調べたのですが、CharacterControllerの処理タイミングなどについての記事は見つけられませんでしたので、検証結果として残したいと思います。
間違っている点などがありましたらコメントをお願いいたします。