LoginSignup
3
3

More than 5 years have passed since last update.

距離を見るヒット処理を試してみる

Last updated at Posted at 2017-11-08

はじめに

純粋に計算式からヒット判定を求めてみたら
RaycastとかColliderとの負荷の違いはどうなんだろう。
ということでやってみました。

Unity2017.2.0f3で動作確認しています

参考

点と線の距離を求める
※ソースもそのまま流用できそうです。

説明はサイトを確認したほうが良いと思いますが、一応そのまま解説

直線とそれぞれのObjectの距離を求める場合。
直線の始点>終点と、直線の始点>Objectの二つの線と捉えると、平行四辺形の形になります。
ここで、平行四辺形の高さが、今回求めたい距離そのものとみなせます。

平行四辺形の高さは面積がわかれば以下の式で求まります。

高さh=面積/底辺の長さ

ということで直線距離A~Bと、その近くにあるPがあるとして
ベクトルABとベクトルAPの外積で面積Dが出てきます。
直線A~Bの長さが底辺の長さL
ということで直線とPとの距離Hは

H=D/L

あとはそのままScriptに落とし込んでみました。

実装

下記Scriptを準備します。
機能は、二点、マウスの左クリックは計算で求め、右クリックはRaycastAllで計算
それぞれカウンターでHitが1000回あつまるまでの時間を計測します。
※Scriptそのものは一番下参照

※Enemy(名前は何でも良いけど)Objectを作りEnemyControllerSphereColliderをAddしておきます。
 EnemyはPrefabとし、PlayerScriptEnemyObjにアタッチしておいてください。

player.png

enemy.png

結果

1000個のObjectを計算するのにどれくらいの時間がかかるのか求めました。
editor.png

動かします。
直線状にEnemyObjectが並んでいます。 さあ計算してみましょう!!
kekka.png
計算にかかった時間が表示されました!!
んー同じ?・・・
時間の計測方法に問題がありそうですけど、一旦置いておきます。

Profile.png
Profileを見てみると変化が出ていました!

RaycastAllはPhysicsが動いています。
Scriptだけだと計算式の方が処理負荷が重そうですが、Physicsも含めると計算式の方が軽そうです。

単純にこれだけでどうこうという事はありませんが、
まるっとヒット判定する場合は効果があるかもしれません。

Scriptサンプル

距離の算出計算
DistancePtoL.cs

public class DistancePtoL  {
    public float Distance(Vector3 Point,Vector3 Start,Vector3 End)
    {
        Vector3 StartToPoint, StartToEnd;
        StartToPoint = Point - Start;// スタート地点からPointまでのベクトル
        StartToEnd = End - Start;//スタート地点からEndまでのベクトル

        //StartToEndとStartToPointを外積して求められたベクトルの長さが、平行四辺形の面積になる
        float Area = Vector3.Magnitude( Vector3.Cross(StartToEnd, StartToPoint));

        //StartToEndの距離
        float Line = Vector3.Distance(Start, End);

        return Area / Line;//距離の算出
    }
}

本体はこんな感じ
PlayerScript.cs

[System.Serializable]
public class EnemyCore
{
    public GameObject EnemyBody;
    public EnemyController Controller;
}

//GameController
public class PlayerScript : MonoBehaviour {

    [SerializeField]
    private GameObject EnemyObj;//敵の基本Prefab

    [SerializeField]
    private List<EnemyCore> EnemyList = new List<EnemyCore>();//管理する敵リスト

    [SerializeField]
    private int EnemyCount = 1000;//作る仮想敵の数

    [SerializeField]
    private Vector3 _EnemyRange = new Vector3(5,5,1000);


    void Start()
    {
        //敵量産
        for (var i=0;i< EnemyCount; i++)
        {
            EnemyCore _Enemy = new EnemyCore();
            var EnemyPosition = new Vector3(0f,0f,UnityEngine.Random.Range(1f, _EnemyRange.z));
            _Enemy.EnemyBody = Object.Instantiate(EnemyObj, EnemyPosition, Quaternion.identity) as GameObject;
            _Enemy.EnemyBody.name = "Enemy[" + i + "]";
            _Enemy.Controller = _Enemy.EnemyBody.GetComponent<EnemyController>();
            _Enemy.Controller.PL = this.transform.GetComponent<PlayerScript>();
            EnemyList.Add(_Enemy);
        }
    }

    void Update () {
        if (Input.GetMouseButtonUp(0))
        {
            Debug.Log("PointToLine Time");
            //マウスクリック
            _HitCount = 0;
            _Timer = 0;
            StartCoroutine(TimerCount());
            HitTestUpdate(2);
        }

        if (Input.GetMouseButtonUp(1))
        {
            Debug.Log("RayCast Time");
            //マウスクリック
            _HitCount = 0;
            _Timer = 0;
            StartCoroutine(TimerCount());
            HitTestUpdate(1);
        }



    }

    private int _HitCount = 0;
    private float _Timer = 0;
    public void HitCount()
    {
        _HitCount++;
    }

    IEnumerator TimerCount()
    {
        while (true)
        {
            _Timer += Time.deltaTime;

            if (_HitCount >= 999)
            {
                Debug.Log("All Hit Timer :" + _Timer);
                yield break;
            }

            yield return null;
        }
    }


    void HitTestUpdate(int mode)
    {
        Vector3 Start = this.transform.position;
        Vector3 End = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z + 1000);
        RaycastHit[] hits;

        switch (mode) {
            case 1:
                hits = Physics.RaycastAll(Start, Vector3.forward, 1000);
                for(var i=0;i < hits.Length; i++)
                {
                    RaycastHit hit = hits[i];
                    HitCount();
                }


                break;
            case 2:
                for (var i = 0; i < EnemyList.Count; i++)
                {
                    EnemyList[i].Controller.HitUpdate(Start, End);
                }
                break;
        }
    }

}

最後に画面に並べられるスクリプト側
EnemyController.cs

public class EnemyController : MonoBehaviour {

    [SerializeField]
    public DistancePtoL HitCheck;
    public PlayerScript PL;

    void Start()
    {
        HitCheck = new DistancePtoL();
    }

    public void HitUpdate(Vector3 Start,Vector3 End)
    {
        var Dist = HitCheck.Distance(this.transform.position,Start,End);
        if (Dist < 10f)
        {
            PL.HitCount();
        }
    }
}

追記

コメントで教えていただいたのでProfileを正しく計測してみました。
・参考
Unityでスクリプトの一部分の処理をProfilerに表示してもらう

修正箇所はこちら PlayerScript.csの中になります。
ちなみに'Unity2017.2.0f3'では、Profiler.BeginSampleUnityEngine.Profilingに移動したようです。

    switch (mode) {
        case 1:
            UnityEngine.Profiling.Profiler.BeginSample("RaycastAllProcess");
            hits = Physics.RaycastAll(Start, Vector3.forward, 1000);
            for(var i=0;i < hits.Length; i++)
            {
                //RaycastHit hit = hits[i];
                HitCount();
            }
            UnityEngine.Profiling.Profiler.EndSample();

            break;
        case 2:
            UnityEngine.Profiling.Profiler.BeginSample("PointToLine Process");
            for (var i = 0; i < EnemyList.Count; i++)
            {
                EnemyList[i].Controller.HitUpdate(Start, End);
            }
            UnityEngine.Profiling.Profiler.EndSample();
            break;
    }

結果はこんな感じになりました。
profilekekka.png
Timeの数値が結果になります。
結構違いますね。

3
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3