SuiTechLog

Unity,Arduino,RaspberryPiなど、モノづくり系を気ままに書き残すブログ。

Unity  放物線をえがく高速弾のすり抜け事故対策


前回の記事では、直線弾のすり抜け対策を実装しました。

 

今度は放物線をえがく高速弾ですり抜けが発生する!という話をきいて

解決策を考えてみました。

 

 

コライダーを使う作戦

 

 コライダーを使った物理演算だけでやろうと思ったら、以下のConteniusを設定すればいけるのかと思うのですが、勉強不足のためか、どうも正しく動作しなかったため、やはりレイを使っていこうかと思います。

qiita.com

 

レイを使う作戦

 

 以下、弾丸に埋め込むプログラムです。

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class parabola_Ray : MonoBehaviour {

    public GameObject test; 

    //判定処理をしたくないレイヤーを無視するためのレイヤーマスク
    public LayerMask mask = -1;

    //リジッドボディ
    private Rigidbody rbd;

    //1F前のポジション
    private Vector3 lastpos;

	// Use this for initializatjmion
	void Start () {
        rbd = GetComponent< rigidbody >();

        lastpos = rbd.position;
    }

    //毎フレームよばれる
    void Update()
    {

    }

    //物理演算の更新フレームごとによばれる
    void FixedUpdate () {

        //現在のポジション(rigidbodyから取らないとダメです)
        var currentpos = rbd.position;

        //1F前のポジションから現在のポジションへの方向
        var dir = (currentpos - lastpos).normalized;

        //1F前のポジションから現在のポジションの距離
        var distance = Vector3.Distance(currentpos , lastpos);

        //新しいレイを作成する(発射位置と方向をあらわす情報)
        Ray ray = new Ray(lastpos, dir);

        //あたったものの情報が入るRaycastHitを準備
        RaycastHit hit;

        //物理コリジョン判定をさせるためにレイをレイキャストしてあたったかどうか返す
        bool isHit = Physics.Raycast(ray, out hit, distance, mask);

        //あたっているなら
        if (isHit)
        {
            //あたったゲームオブジェクトを取得
            GameObject hitobject = hit.collider.gameObject;

            //例:あたったオブジェクトのマテリアルカラー変更
            //hitobject.GetComponent().material.color = Color.green;

            //例:あたったオブジェクト削除
            //Destroy(hitobject);

            //レイがあたった座標
            Vector3 hitposition = hit.point;

            //レイがあたった当たり判定オブジェクトの面の法線
            Vector3 normal = hit.normal;

            //例:着弾エフェクトを発生など
            Debug.DrawLine(hitposition, hitposition + Vector3.forward, Color.red, 0);
            //例:自分自身を削除
            Destroy(this.gameObject);

            ////////////// デバッグ用 ////////////////

            Debug.Log("HIT "+ hitobject.name + hitposition);

        }

        //現在の座標を過去の位置へ(次のフレームで使うよう)
        lastpos = currentpos;
    }

 以下簡単な解説です。

 

やっていることはとても単純で、

「一個前のフレームで弾丸がいた座標」から、「今現在のフレームで、弾丸がいる座標」へレイを飛ばして、レイがコリジョンにあたれば、接触したという判定にします。

これは毎fixed update毎に実行されます。(fixed updateの実行タイミングについては前回の記事をご参照ください。)

 

この方法であれば、どんな軌道であろうと、フレーム間でどんなに距離が離れても、間にコリジョンがあれば判定してくれるので確実にすり抜けがなくなります。

 

問題点

 私の環境(win 2017.3.1f1)では

Update と Fixed Updateでは、transform.positionとrigidbody.positionから取れる座標値に1fのズレ?があるようで、

厳密には2f前の位置を取得してしまっています。コレに関しては原因がつかめなかったので、Unityに詳しい方だれかヘルプお願いいたします。

 

 厳密にはダメですが、一応、接触位置なども正確にとれていますし、放物線をえがく高速弾なので今回はそれほど問題にならないと思い、載せています。

 

参考と関連項目

UpdateとFixed Updateの実行タイミング

docs.unity3d.com

 

Rigid Body

docs.unity3d.com