ボールの軌道を表示する-trajectory-【Unityメモ】

今回はUnity2Dでボールをはじく時のボールの軌道を表示してみます。

はじめに

Unityのバージョンは2021.3.14f1です。

下記のようにボールが飛ぶ場所の軌道を表示してみます。

下記ではボールをはじくのを実装しています。今回はそれに軌道を加えたイメージです。

実装開始

Hierarchyで右クリック、「2DObject」→「Sprites」→「Square」と「Circle」を追加します。

SquareはGroundに名前を変更、スケール、色を変更してBoxCollider2Dをアタッチします。

CircleはBallに名前を変更、スケールを変更し、「CircleCollider2D」と「Rigidbody2D」をアタッチします。

ゲーム画面を下記のような感じにします。

スクリプトの作成

下記スクリプトを作成してBallにアタッチします。今回はボールを掴むのではなく、画面上でマウスドラッグすると動かすようにしたいのでUpdate内で実装。

using UnityEngine;

public class BallShoot2 : MonoBehaviour
{
    private Rigidbody2D _rb;

    private float power = 2.0f;
    private float maxPower = 5.0f;

    private Vector2 StartPos;
    private Vector2 EndPos;

    private void Start()
    {
        _rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
            StartPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);

        if (Input.GetMouseButton(0))
            EndPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);

        if (Input.GetMouseButtonUp(0))
        {
            Vector2 force = Vector2.ClampMagnitude((StartPos - EndPos), maxPower);
            _rb.AddForce(force * power, ForceMode2D.Impulse);
        }
    }
}

実行してみると下記のようになります。

軌道表示を追加

軌道の表示を追加するのでBallにLineRendererを追加し、Widthを変更しておきます。

スクリプトは下記に変更。

using UnityEngine;

public class BallShoot2 : MonoBehaviour
{

    private Rigidbody2D _rb;
    private LineRenderer _lr;

    private float power = 2.0f;
    private float maxPower = 5.0f;

    private Vector2 StartPos;
    private Vector2 EndPos;
    private Vector2 force;

    private void Start()
    {
        _lr = GetComponent<LineRenderer>();
        _rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _rb.velocity = Vector3.zero;
            _rb.isKinematic = true;
            StartPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
        }

        if (Input.GetMouseButton(0))
        {
            EndPos = (Vector2)Camera.main.ScreenToWorldPoint(Input.mousePosition);
            force = Vector2.ClampMagnitude((StartPos - EndPos), maxPower) * power;

            Vector2[] trajectory = Plot(_rb, (Vector2)transform.position, force, 400);
            _lr.positionCount = trajectory.Length;

            Vector3[] positions = new Vector3[trajectory.Length];
            for (int i = 0; i < trajectory.Length; i++)
            {
                positions[i] = trajectory[i];
            }
            _lr.SetPositions(positions);
        }

        if (Input.GetMouseButtonUp(0))
        {
             _rb.isKinematic = false;
            _rb.AddForce(force, ForceMode2D.Impulse);
        }
    }
    
    private Vector2[] Plot(Rigidbody2D rigidbody, Vector2 pos, Vector2 force, int steps)
    {
        Vector2[] results = new Vector2[steps];

        float timestep = Time.fixedDeltaTime / Physics2D.velocityIterations;
        Vector2 gravityAccel = Physics2D.gravity * rigidbody.gravityScale * timestep * timestep;

        float drag = 1f - timestep * rigidbody.drag;
        Vector2 moveStep = force * timestep;

        for (int i = 0; i < steps; i++)
        {
            moveStep += gravityAccel;
            moveStep *= drag;
            pos += moveStep;
            results[i] = pos;
        }
        return results;
    }
}

プロット部分は下記の記事を参考にしてます。(というかほぼそのまま)

サイトにも書いてあるけど、400個ポイントしているので一つ実装するのは問題ないけど、多くの実装には向かない感じですね。

タイトルとURLをコピーしました