ぐーるらいふ

底辺。

【Unity】Rigidbodyの弾道予測線をシュミレートする

ghoul-life.hatenablog.com

前回の続きで、今回は撃つ側。

RigidbodyにAddForceを加えた場合の移動位置をシュミレートして表示する

なんていうのはみんなやっているようで、ググると大量に出てきます。

f:id:ghoul_life:20180322150647p:plain

そのまま使わせて頂くのもOKですが、自分なりにまとめたソースもメモ。

Rigidbodyの移動を予測計算

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

/**
 * Rigidbodyにvecを加えた時の弾道をシュミレート。
 */
public class BallSimulator : MonoBehaviour {

    [SerializeField]
    private GameObject _ballSimPrefab; // 何でもOK。予測位置を表示するオブジェクト

    private const int SIMULATE_COUNT = 20; // いくつ先までシュミレートするか

    private Vector3 _startPosition; // 発射開始位置
    private List<GameObject> _simuratePointList; // シュミレートするゲームオブジェクトリスト

    void Start () {
        Init();
    }
	
    void Update () {
        // デバッグ用に線を出してみる。必要無いなら無くても問題なし。
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            for (int i = 0; i < SIMULATE_COUNT; i++)
            {
                if (i == 0)
                {
                    Debug.DrawLine(_startPosition, _simuratePointList[i].transform.position);
                }else
                if (i < SIMULATE_COUNT)
                {
                    Debug.DrawLine(_simuratePointList[i - 1].transform.position, _simuratePointList[i].transform.position);
                }
            }
        }

    }

    // 初期化
    private void Init()
    {
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            foreach (var go in _simuratePointList)
            {
                Destroy(go.gameObject);
            }
        }

        // 位置を表示するオブジェクトを予め作っておく
        if (_ballSimPrefab != null)
        {
            _simuratePointList = new List<GameObject>();
            for (int i = 0; i < SIMULATE_COUNT; i++)
            {
                var go = Instantiate(_ballSimPrefab);
                go.transform.SetParent(this.transform);
                go.transform.position = Vector3.zero;
                _simuratePointList.Add(go);
            }
        }
    }

    /**
     * 弾道を予測計算する。オブジェクトを再生成せず、位置だけ動かす。
     * targetにはRigidbodyが必須です
     **/
    public void Simulate(GameObject target , Vector3 _vec)
    {
        if (_simuratePointList != null && _simuratePointList.Count > 0)
        {
            // 発射位置を保存する
            _startPosition = target.transform.position;
            var r = target.GetComponent<Rigidbody>();
            if (r != null)
            {
                // ベクトルはmassで割る
                Vector3 force = (_vec / r.mass);

                //弾道予測の位置に点を移動
                for (int i = 0; i < SIMULATE_COUNT; i++)
                {
                    var t = (i * 0.5f); // 0.5秒ごとの位置を予測。
                    var x = t * force.x;
                    var y = (force.y * t) - 0.5f * (-Physics.gravity.y) * Mathf.Pow(t, 2.0f);
                    var z = t * force.z;
                    
                    _simuratePointList[i].transform.position = _startPosition + new Vector3(x, y, z);
                }
            }
        }
    }
}

発射する側は

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

/**
 * 弾を発射する
 */
public class BallShooter : MonoBehaviour {

    [SerializeField]
    private BallSimulator _ballSimurator; // 弾道予測線

    [SerializeField]
    private Slider _powerSlider; // 力を変えるスライダー

    [SerializeField]
    private GameObject _shooterBase; // 土台

    private Vector3 _firstPosition;
    private Rigidbody _rigidbody;
    private Vector3 _touchDownPos;

    private bool _isShot;
	// Use this for initialization
    void Start () {
        Init();
    }

    
    // Update is called once per frame
    void Update () {

        if (!EventSystem.current.IsPointerOverGameObject())
        {
            if (Input.GetMouseButtonDown(0))
            {
                _touchDownPos = Input.mousePosition;
            }
            else if (Input.GetMouseButton(0))
            {
                var tempPos = Input.mousePosition;
                Vector3 value = Vector3.zero;
                value.x = (_touchDownPos.x - tempPos.x);
                value.y = (_touchDownPos.y - tempPos.y);
                value.z = 0;
                _touchDownPos = tempPos;

                var qot1 = Quaternion.AngleAxis(value.x, new Vector3(0, 1, 0));
                var qot2 = Quaternion.AngleAxis(value.y, new Vector3(1, 0, 0));
                _shooterBase.transform.rotation *= qot1;
                this.transform.rotation *= qot2;
            }
        }


        Debug.DrawLine(this.transform.position, this.transform.position + this.transform.forward , Color.blue);
        
        // 発射中はシュミレートを止める
        if (!_isShot)
        {
            var vec = this.transform.forward * _powerSlider.value;
            _ballSimurator.Simulate(this.gameObject, vec);
        }
    }

    public void Init()
    {
        _firstPosition = this.transform.position;
        _rigidbody = this.GetComponent<Rigidbody>();
        _rigidbody.isKinematic = true;
        _powerSlider.value = 10.0f;
        _isShot = false;
        
    }

    //--------------------------------------------------------------------------
    // 発射(Buttonから呼ぶとか)
    public void Shoot()
    {
        _isShot = true;
        var vec = this.transform.forward * _powerSlider.value;
        _ballSimurator.Simulate(this.gameObject , vec);
        _rigidbody.isKinematic = false;
        _rigidbody.AddForce(vec, ForceMode.Impulse);
    }
}

こんな感じでOK。

動かす

f:id:ghoul_life:20180322151038g:plain

  • 発射方向を変えるとそれに合わせて予測線も変わるか
  • 発射する力を変えるとそれに合わせて予測線も変わるか
  • massを変えると予測線も変わるか
  • 予測線に沿って飛ぶか

f:id:ghoul_life:20180322151137g:plain

この辺りに注目。

この次は

砲台を突然作り出したのはProbuIlderの勉強のため

assetstore.unity.com

今回でなんちゃって簡易砲台(作ろうとしているゲームでは砲台ではないんだけど)が出来上がったので、
次回からProBuilderを使用したステージ制作に入っていければいいな