1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unityで2Dの追尾ミサイルを実装する

Posted at

概要

制作しているゲームで2Dでの追尾ミサイルを実装したので、その方法をまとめておきます。
たくさん配置して無限追尾ミサイルで遊ぶと楽しいです。

開発環境
Unity Editor Version:6000.0.22f1

仕様

  • 特定のtransform目掛けてゆっくりと回転する
  • ミサイルの正面向きに移動する
  • 移動速度は徐々に加速させ、最高速度に到達すると加速を止める

完成形

先に完成形を載せておきます。これをミサイルオブジェクトにアタッチすればよいです。

using UnityEngine;

public class Missile : MonoBehaviour
{
        /** 追尾対象 */
        [SerializeField] private Transform target;
        /** 画像の正面向き */
        [SerializeField] private Vector3 fromDirection;
        /** 回転最高速度 */
        [SerializeField] private float maxRotationSpeed;
        /** 移動最高速度 */
        [SerializeField] private float maxSpeed;
        /** 最高速度に到達するまでの秒数 */
        [SerializeField] private float reachMaxSpeedSeconds;

        private float _elapsedSeconds;
        private float _elapsedSecondsRatio;
        
        void Update()
        {
            Accelerate();
            LookAtTarget();
            Move();
        }
        
        private void Accelerate()
        {
            // 加速する
            _elapsedSeconds+=Time.deltaTime;
           _elapsedSecondsRatio = _elapsedSeconds / reachMaxSpeedSeconds;
            float maxRatio = 1.0f;
            if (maxRatio < _elapsedSecondsRatio)
            {
                _elapsedSecondsRatio = 1.0f;
            }
        }
        
        private void LookAtTarget()
        {
            // 追尾対象への向きと距離
            Vector3 heading = target.position - transform.position;
            // 追尾対象への回転量
            Quaternion targetRotation = Quaternion.FromToRotation(fromDirection, heading.normalized);
            // ゆっくりと回転させる
            float rotationSpeed = maxRotationSpeed * _elapsedSecondsRatio;
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
        }
        
        private void Move()
        {
            float moveSpeed = maxSpeed *  _elapsedSecondsRatio;
            
            // ミサイルが向いている方向に移動
            var forward = transform.rotation * fromDirection;
            transform.position += forward.normalized * (moveSpeed * Time.deltaTime);
        } 
}

特定のtransform目掛けてゆっくりと回転する

まずはミサイルと、ターゲットとなるゲームオブジェクト(四角形)を配置しておきます。

missile_first.jpg

本題のコードの話。
ターゲットに向けて回転し続けるにはTransform.LookAtを使えばいい...と思ってたのですが、これは2DだとY軸も回転しておかしくなります。2Dの時はQuaternion.FromToRotation(Vector3 fromDirection, Vector3 toDirection)を使います。第一引数には画像がどの向きを向いているかを指定します。上記の場合、画像は右向きなのでXに1を入れます。仮に上向きならY軸に1,下向きなら-1,左向きならX軸に-1を入れます。第二引数には追尾対象への方向を渡します。

また、ゆっくりと回転させるために補間をかけます。補間はQuaternion.Slerpを使います。

        /** 追尾対象 */
        [SerializeField] private Transform target;
        /** 回転速度 */
        [SerializeField] private float rotationSpeed;
        /** 画像の正面向き */
        [SerializeField] private Vector3 fromDirection;
        
        void Update()
        {
            LookAtTarget();
        }

        private void LookAtTarget()
        {
            // 追尾対象への向きと距離
            Vector3 heading = target.position - transform.position;
            // 追尾対象へどれくらい回転するか
            Quaternion targetRotation = Quaternion.FromToRotation(fromDirection, heading.normalized);
            // ゆっくりと回転させる
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
        }

missile_01.gif

ミサイルの正面向きに移動する

追尾対象に向けてではなく、ミサイルの正面向きに移動させます。正面向きのベクトルは Rotation * 画像の向き(fromDirection) で求めます。

        /** 移動速度 */
        [SerializeField] private float moveSpeed;

        void Update()
        {
            LookAtTarget();
            Move();
        }

        private void Move()
        {
            // ミサイルが向いている方向に移動
            var forward = transform.rotation * fromDirection;
            transform.position += forward.normalized * (moveSpeed * Time.deltaTime);
        }

missile_02.gif

最高速度に到達するまで移動量と回転量を加速させる

「最高速度」最高回転量」「最高速度に到達するまでの秒数」を SerializeField でインスペクターから指定できるようにします。また、Accelerate 関数で加速の計算をしています。

using UnityEngine;

public class Missile : MonoBehaviour
{
        /** 追尾対象 */
        [SerializeField] private Transform target;
        /** 画像の正面向き */
        [SerializeField] private Vector3 fromDirection;
        /** 回転最高速度 */
        [SerializeField] private float maxRotationSpeed;
        /** 移動最高速度 */
        [SerializeField] private float maxSpeed;
        /** 最高速度に到達するまでの秒数 */
        [SerializeField] private float reachMaxSpeedSeconds;

        private float _elapsedSeconds;
        private float _elapsedSecondsRatio;
        
        void Update()
        {
            Accelerate();
            LookAtTarget();
            Move();
        }
        
        private void Accelerate()
        {
            // 加速する
            _elapsedSeconds+=Time.deltaTime;
           _elapsedSecondsRatio = _elapsedSeconds / reachMaxSpeedSeconds;
            float maxRatio = 1.0f;
            if (maxRatio < _elapsedSecondsRatio)
            {
                _elapsedSecondsRatio = 1.0f;
            }
        }
        
        private void LookAtTarget()
        {
            // 追尾対象への向きと距離
            Vector3 heading = target.position - transform.position;
            // 追尾対象への回転量
            Quaternion targetRotation = Quaternion.FromToRotation(fromDirection, heading.normalized);
            // ゆっくりと回転させる
            float rotationSpeed = maxRotationSpeed * _elapsedSecondsRatio;
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
        }
        
        private void Move()
        {
            float moveSpeed = maxSpeed *  _elapsedSecondsRatio;
            
            // ミサイルが向いている方向に移動
            var forward = transform.rotation * fromDirection;
            transform.position += forward.normalized * (moveSpeed * Time.deltaTime);
        } 
}

missile_03.gif

これで完成です。
これで大体ミサイルっぽい動きになったのではないでしょうか。
移動速度に Easing をかけるともっと良い動きになりそうですね。

ちなみに自作しているゲーム内では、一定時間が経過したら回転しないようにしたり、衝突時に爆発エフェクトを再生したりして使用しています。

game.gif

楽しいですね。

参照

1
1
0

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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?