てんちょーの技術日誌

自分がつまづいたこととかメモ

【Unity】character controllerについて整理してみた C#

ユニティちゃんライセンス

このアセットは、『ユニティちゃんライセンス』で提供されています。このアセットをご利用される場合は、『キャラクター利用のガイドライン』も併せてご確認ください。

ようやく少しUnityに触れる機会があったのでcharacter controllerについて調査してみました。

今回はUnityちゃんを対象にして実験していきます。

用意したのは

  • Unityちゃん
  • cube 2つ
  • plane 1つ

です。 f:id:shop_0761:20150621163516p:plain

サンプル用のprojectはこちらの記事から 

shop-0761.hatenablog.com

さらにUnityちゃんを動かすのにはこちらをそのまま参考にさせてもらいました。

qiita.com

今回はcharacter controllerのみについて扱うのでassetのimport等は省略しますね。

で、ここまできて動くことには動くようになりましたが、問題はここからです。 有名なテラシュールブログさんにもあるようにcharacter controllerには罠があるそうです。

tsubakit1.hateblo.jp

でも便利なことは便利なものだと思うわけで、なんとか実用的な使い方はないものかと。

とりあえずcharacter controller について公式で説明を発見しました。

CharacterController は Rigidbdy による処理を持たなくてもコリジョンによって簡単に動きの制限を行うことが可能です。 CharacterController は力には影響されず、Move 関数を呼び出した時のみ移動します。 その時に移動を行いますがコリジョンによって制限されます。

さりげなくRigidbdyになってる... というのはいいとして、rigidbodyなしでcollsionがあるイメージでいいとのことです(テラシュールブログさんより) 確かにものが少ないほうが負荷は減るし助かるんですが、扱いが厄介で...

設定を間違えるとこのように浮いてしまいます。 f:id:shop_0761:20150621162636p:plain

そのため自動生成してもらえるAssetを買うか、自分で調整するしかない気がします。

とりあえず今回は上手くいったのでこのまま進めます。

他のcubeとの衝突について実験します。 左側のcubeにはrigidbodyを追加してあります。

衝突させると... f:id:shop_0761:20150621163624p:plain このように動かせます。

反対側のcubeは f:id:shop_0761:20150621163742p:plain 刺さります。

なるほどなるほど。普通ですね。

では試しに character controller -> capsule collider に差し替えてみます。

f:id:shop_0761:20150621164241p:plain お風呂みたいになってしまいます。(反対も同じ

ならばさらに capsule collider + Rigidbody では f:id:shop_0761:20150621165314p:plain rigidbodyなしのcubeは問題なさそう

では反対のcubeは f:id:shop_0761:20150621165419p:plain お、 f:id:shop_0761:20150621165432p:plain 座標軸が変わってしまったようです。 cubeと一緒にunityちゃんも傾いていったようす。 このまま歩いたり、回れるんですが、重力があるせいで下に落ちてきます。不思議なかんじ。

おとなしくcharacter controllerを使えとのことのようです。

ここでcharacter controllerの衝突判定に使うOnControllerColliderHitを使ってみます。 コードはこんな感じにしました。

unitychanScript

using UnityEngine;
using System.Collections;

public class unitychanScript : MonoBehaviour {
    private Animator animator;
    GameObject plane,cube1,cube2;

    // Use this for initialization
    void Start () {
        animator = GetComponent<Animator>();
    }
    
    // Update is called once per frame
    void Update () {
        if (Input.GetKey("up")) {
            transform.position += transform.forward * 0.01f;
            animator.SetBool("is_running", true);
        } else {
            animator.SetBool("is_running", false);
        }
        if (Input.GetKey("right")) {
            transform.Rotate(0, 10, 0);
        }
        if (Input.GetKey ("left")) {
            transform.Rotate(0, -10, 0);
        }
    }

    void OnControllerColliderHit(ControllerColliderHit hit){
        if (hit.gameObject.CompareTag("cube1")) {
            Debug.Log(hit.gameObject);        
        }
        else if (hit.gameObject.CompareTag("cube2")) {
            Debug.Log(hit.gameObject);
        }
        else if (hit.gameObject.CompareTag("plane")) {
            Debug.Log(hit.gameObject);
        }
        else {
            Debug.Log ("OnControllerColliderHit");

        }
       }
}

検出用にそれぞれのオブジェクトを設定しました。 tagで管理する方法が主流っぽい。

さて実験すると、 f:id:shop_0761:20150622000216p:plain Planeしか出ない。一応if elseで優先度も意識したんだけどな。

上からなら f:id:shop_0761:20150622000336p:plain 正常に判断してくれます。(左も一緒)

うーん。どうしたものか。cubeにscriptつけてみる。 scriptの詳細はこちら shop-0761.hatenablog.com 簡潔に言えばもろもろの衝突を検出する関数が書いてあります。 Logの出力は (衝突元のobject) hit (そのときの関数) with (衝突先のobject) となってます。

結果。 f:id:shop_0761:20150622000907p:plain これならまだ納得いくけど、cube2が反応しない。 (こっちはcube2にrigidbody追加すれば反応しました。)

いっそunitychanに同じようなscript実装すればそっちでこと足りるのでは、と思い実装

このとき、unitychanとcube1にそれぞれscriptを追加。 f:id:shop_0761:20150622004532p:plain

いいかんじじゃん! 行けそう

てかcube1側でのscriptを無効にしてもいいんじゃね f:id:shop_0761:20150622005524p:plain な ぜ お 前 が 生 き て い る ん だ(衝突判定はする)

componentからremoveすれば消えることには消えるんだけど、そうすると今度はunitychan側の 衝突関数が反応しない。

もう訳がわからない。これも使うなってことか。

と、巡り巡ってcharacter controllerに帰ってくるわけか。 ただplaneばっかり検出されて他のobjectが拾えない。

もう少し検証する必要がありそうだ。

なにか間違ってるところやこうしたら上手くいった、などなど教えてほしいです。

monodevelop嫌いになりそう(早く乗り換えたい