WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

挫折しないUnity入門④ - Collider編

本日のエンジニアブログを担当する安藤です。
今回はColliderの使い方について触れたいと思います。


はじめに

Colliderは形状が様々あるものの、
大きく非Trigger ColliderとTrigger Colliderの二つに分けられると思います。
(Character Controllerは別物とする)

そこで今回は
非Trigger ColliderとTrigger Colliderの使い分けについて触れていきたいと思います。


Colliderについて

衝突判定に使用するComponent、
Rigidbody Componentと組み合わせて物理的な挙動を簡単に実現出来る。

物理挙動有効化

Colliderをただ付けただけでは衝突処理を行いません。
RigidBodyを付けます。
※ 動かない判定のみのオブジェクトはColliderのみで機能します。
スクリーンショット 2014-11-12 15.07.33


非Trigger Collider

剛体の入った判定処理を行う
主にプレイヤーや障害物に使用
スクリーンショット 2014-11-12 15.31.28

Script例

// 衝突した瞬間に呼ばれる  
void OnCollisionEnter(Collision collision) {  
}  
// 衝突している間呼ばれ続ける  
void OnCollisionStay(Collision collision) {  
}  
// 衝突から離れた瞬間に呼ばれる  
void OnCollisionExit(Collision collision) {  
}  

Trigger Collider

衝突検出のみを行う
爆発処理やイベント検出に使用
スクリーンショット 2014-11-12 15.31.38

Script例

// 衝突した瞬間に呼ばれる  
void OnTriggerEnter(Collider other) {  
}  
// 衝突している間呼ばれ続ける  
void OnTriggerStay(Collider other) {  
}  
// 衝突から離れた瞬間に呼ばれる  
void OnTriggerExit(Collider other) {  
}  

非TriggerとTriggerの組み合わせ例

爆発

スクリーンショット 2014-11-12 17.54.58 スクリーンショット 2014-11-12 17.55.59

GameObject
スクリーンショット 2014-11-12 18.01.38 スクリーンショット 2014-11-12 18.02.12

Script
範囲内の大量のrigidbody付きColliderを飛ばします。

using UnityEngine;  
using System.Collections;  
  
public class Explosion : MonoBehaviour {  
    [SerializeField] GameObject _obj;  
    [SerializeField] int _count = 27;  
    [SerializeField] int _maxX = 3;  
    [SerializeField] int _maxZ = 3;  
    [SerializeField] int _explosionRadius = 3;  
    [SerializeField] int _explosionForce = 10;  
    SphereCollider _collider;  
  
    void Start () {  
        for(int i=0;i<_count;i++){  
            GameObject obj = Instantiate(_obj,Vector3.zero,Quaternion.identity) as GameObject;  
            Vector3 pos = Vector3.zero;  
            float scale = obj.transform.localScale.x;  
            pos.x = (i % _maxX) * scale;  
            pos.z = ((i / _maxX) % _maxZ) * scale;  
            pos.y = (i / (_maxX * _maxZ)) * scale;  
            pos += transform.position;  
            obj.transform.position = pos;  
        }  
    }  
      
  
    Rect buttonRect = new Rect(5,5,100,50);  
    void OnGUI(){  
        if(GUI.Button(buttonRect,"execute")){  
            if (_collider){  
                Destroy(_collider);  
            }  
  
            _collider = this.gameObject.AddComponent<SphereCollider>();  
            _collider.isTrigger = true;  
            _collider.radius = _explosionRadius;  
        }  
    }  
  
  
    void OnTriggerEnter(Collider other){  
        Vector3 normal = (other.transform.position - this.transform.position).normalized;  
        other.rigidbody.AddForce(normal*_explosionForce,ForceMode.VelocityChange);  
  
        if (_collider){  
            Destroy(_collider);  
        }  
    }  
}  

イベントセンサー

スクリーンショット 2014-11-12 17.06.01 スクリーンショット 2014-11-12 17.06.18

GameObject
スクリーンショット 2014-11-12 17.10.36 スクリーンショット 2014-11-12 17.11.30

Script
拡張性に欠けるがrigidbodyを減らす手段の一つとして

using UnityEngine;  
using System.Collections;  
  
public class Character : MonoBehaviour {  
  
    [SerializeField] GameObject _string;  
  
    void Start () {  
        rigidbody.AddForce(Vector3.left,ForceMode.VelocityChange);  
        _string.SetActive(false);  
    }  
  
    void OnTriggerEnter(Collider other){  
        _string.SetActive(true);  
    }  
    void OnTriggerExit(Collider other){  
        _string.SetActive(false);  
    }  
}  

TIPS

非Triggerの複数判定

void OnCollisionEnter(Collision collision) {  
    foreach(ContactPoint point in collision.contacts){  
        // pointから色々取得して処理を行う  
    }  
}  

※ Trigger系は当たった数だけメソッドが呼ばれます。

接触点の検出

スクリーンショット 2014-11-12 16.37.33

非Trigger

void OnCollisionEnter(Collision collision){  
    foreach(ContactPoint point in collision.contacts){  
        Vector3 hitPos = point.point;  
        // hitPosの座標を使ってエフェクトなどを生成  
    }  
}  

Trigger

void OnTriggerEnter(Collider other){  
    // おおまかなの場所を検出  
    Vector3 hitPos = other.ClosestPointOnBounds(this.transform.position);  
    // hitPosの座標を使ってエフェクトなどを生成  
}  

高速化

処理が一番軽いのはSphere Colliderです。
距離で当たり判定をとっていると思われるので、
Scaleを均等に変更しないと実体と判定が乖離します。
スクリーンショット 2014-11-12 15.54.17

判定のレイヤー分け

ProjectSettingsからPhysicsを呼び出し
スクリーンショット 2014-11-12 15.47.57

InspectorでMask行列のチェックを外すと判定処理されなくなる。
スクリーンショット 2014-11-12 15.48.07

見えない壁

スクリーンショット 2014-11-12 15.27.45

空のGameObjectに複数のColliderを追加して実装します。
スクリーンショット 2014-11-12 15.35.40

以上です。