2012年6月6日水曜日

Unity: 指定エリアの中にあるGameObjectを探る方法

以前の記事で、iTweenでモーションパスに沿ってボールを飛ばし、箱に当てるデモを作成しました。

Unity: iTween Visual Editorで楽々モーションパス制作

このデモでは、強引な方法で箱を吹き飛ばしていました。
モーションパスでボールを動かす勢いで、箱を押し出していました。
今回は、もうちょっとスマートに箱を吹き飛ばしてみたいと思います。

iTweenモーションの停止

まず、ボールが何らかの物体に当たったら、iTweenのモーションパス移動を停止させます。物理判定をしているGameObjectの場合、こうしないとボールと箱の挙動がおかしくなってしまう事があるからです。

ボールのスクリプトを以下のように書き換えます。
using UnityEngine;
using System.Collections;

public class ScriptBall : MonoBehaviour {
    bool explosionFlag = false;

    void Start () {
        iTween.MoveTo(gameObject,iTween.Hash("path",iTweenPath.GetPath("MovePath"),"time",3,"easetype",iTween.EaseType.easeOutSine));
    }
    
    void OnCollisionEnter(Collision collision) {
        if (!explosionFlag) {
            explosionFlag = true;
            iTween.Stop(gameObject);
        }
    }
}
OnCollisionEnter()は、ボールのColliderが、別の物体(Collider)にヒットした時に呼ばれます。そこで、iTween.Stop()関数を呼び出し、モーションパス移動を停止させます。explosionFlagは、停止命令は1回だけ送りたいので、それの判定用に使用します。

ただ、これだけだと見栄えが悪いデモになってしまいます。
ボールが箱に当たった瞬間にモーションパスの移動が停止するので、箱を多少揺らしはするものの、吹き飛ばすには至りません。

指定エリア内のGameObjectリストを作る

ボールが箱に当たった瞬間に、近くにある箱を吹き飛ばしたいのですが、その前に、ボールの近くにあるGameObjectのリストを作りたいと思います。

よく見かけるやり方で、シーン上にあるGameObjectを片っ端から調べていく方法があります。以下のようなスクリプトです。
GameObject[] targets = GameObject.FindGameObjectsWithTag("Enemy");
foreach (GameObject obj in targets) {
    // 対象となるGameObjectとの距離を調べ、近くだったら何らかの処理をする
    float dist = Vector3.Distance(obj.transform.position,transform.position);
    if (dist<NEAR_DIST) {
        // ....
    }
}
これはEnemyというTagを付けたGameObjectの配列を作り、ひとつひとつ距離を調べていくやり方のサンプルです。
シーン上にあるEnemyが、比較的少なければ、これも良いと思います。
しかし数百〜数千個とかあったら、どうでしょうか?

通常、こういう時はカリングと呼ばれるテスト行います。
空間の中をある手法で分割し、最終的により狭められた空間の中のオブジェクトだけを調べて高速化する方法です。

Unityの場合、物理エンジンを搭載しているので、物理エンジン内で合理化されたカリングを行なっていると考えられます。ですので、今回は物理エンジンにお願いして、GameObjectのリストを作成してもらいましょう。

Physics.OverlapSphere()で座標と半径を指定して、その球体の中にあるColliderオブジェクトの参照リストを作成します。
Collider[] targets = Physics.OverlapSphere(transform.position,radius);
このスクリプトにより、Collider配列からGameObjectにアクセスする事ができます。

尚、処理速度に関しては、実際に検証を行なっておりません。あくまでひとつの手法として見て頂けたらと思います。

Rigidbody.AddExplosionForce()で吹き飛ばす

箱を吹き飛ばすには、Rigidbody.AddExplosionForce()を使用しました。
名前の通り、爆発力をRigidbodyに与えるのに便利な関数です。

OnCollisionEnter()関数を以下のように書き換えます。
void OnCollisionEnter(Collision collision) {
    if (!explosionFlag) {
        explosionFlag = true;
        iTween.Stop(gameObject);

        // explosionRadiusには、任意の爆発影響半径を入れます
        Collider[] targets = Physics.OverlapSphere(transform.position,explosionRadius);
            
        foreach (Collider obj in targets) {
            if ((obj)&&(obj.tag=="Enemy")) {
                obj.rigidbody.AddExplosionForce(1000,transform.position,explosionRadius);
            }
        }
    }
}
このスクリプトは、箱オブジェクトのTagに、Enemyが設定されている事を前提としています。AddExplosionForce()関数には、爆発力、爆破座標、爆発半径などを指定できます。

動作テスト



関連記事


0 件のコメント:

コメントを投稿

Related Posts Plugin for WordPress, Blogger...