見出し画像

Unityゲーム役立ち小ネタスクリプト8:ホーミング弾

現在、3Dキャラモデルアセット「バトルメカガール01」を作っています。
特に深い考えなく銀髪爆乳メカ武装ガールを作りたかっただけなんですが
コンストレイントで可動するサブアームと多数の武器ギミックを持つモデルとなったので、fbxとテクスチャだけ配布しても誰も使えないので、(Blenderで作ってfbxにしたら消えた)コンストレイントをUnity上で作り直して、
専用モーションも作って、エフェクトも自作して、それでも表現が追いつかないのでスクリプトでいろいろ動かして…
ここまで作るならもう独自のキャラクターコントローラまで作ってゲームのテンプレートとして販売するとこまでいくか…ってなってます。

今回はそんな経緯で作り出したホーミングビーム・ミサイルの挙動を実現するスクリプトを紹介します。

SampleHoming.cs

using UnityEngine;

namespace FUNSET
{
    public class SampleHoming : MonoBehaviour
    {
        public Transform target;       // ターゲットのトランスフォーム
        public float speed = 10f;      // ミサイルの速度
        public float maxRotationSpeed = 5f; // 最大回転速度
        public float accelerationTime = 0.5f; // 回転速度が最大になるまでの時間
        public float starttime = 0.5f;//回転速度が加算されるまでの時間
        public float maxLifetime = 10f;  // 最大存続時間
        public float hitDistance = 1f;  // ヒットとみなす距離

        private float startTime;
        private float currentRotationSpeed;

        public string TargetName = "LockOnTarget";

        void Start()
        {
            startTime = Time.time; // 初期時間を保存
            currentRotationSpeed = 0f; // 初期回転速度を0に設定

            //ターゲットをシーン上から名前で取得(1つのみ)
            if (target == null)
            {
                target = GameObject.Find(TargetName).transform;
            }
        }

        void Update()
        {
            if (target == null)
            {
                return; // ターゲットが設定されていない場合は何もしない
            }
            if (starttime > 0) { starttime -= Time.deltaTime; }
            else
            {
                // 回転速度を徐々に増加
                float elapsedTime = Time.time - startTime;
                currentRotationSpeed = Mathf.Lerp(0f, maxRotationSpeed, elapsedTime / accelerationTime);
            }


            // ターゲットの方向を計算
            Vector3 direction = (target.position - transform.position).normalized;

            // ターゲットの方向に向かって回転
            Quaternion lookRotation = Quaternion.LookRotation(direction);
            transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, currentRotationSpeed * Time.deltaTime);

            // 前進
            transform.Translate(Vector3.forward * speed * Time.deltaTime);

            // 一定時間経過後に削除
            if (Time.time - startTime >= maxLifetime)
            {
                Destroy(gameObject);
            }

            // ターゲットに一定距離近づいたら削除
            if (Vector3.Distance(transform.position, target.position) <= hitDistance)
            {
                Destroy(gameObject);
            }
        }


        // メソッドで強制削除
        public void ForceDestroy()
        {
            Destroy(gameObject);
        }
    }
}


これを、飛ばしたいエフェクト=オブジェクトのrootにアタッチしてプレハブにします。
標的の判別は、ホーミング弾のプレハブが生成されると、シーン上のオブジェクトをTargetNameで指定された名前で検索して、一致するオブジェクトがあったらターゲットになります。
発射直後はStarttimeの時間が経過するまで追尾せず直進し、AccelerationTimeで決めた時間がまで旋回速度が上がっていく仕様になってます。
一定時間経過するかターゲットの一定距離に近づいたら消滅するようになってますが、これはうまく命中せずずっとターゲットの周りをぐるぐる飛び続ける不具合の対策で(これらは正直デバッグ用です)、実質的なターゲットとの衝突判定・処理は別のスクリプトでやります。

samplehit.cs

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


namespace FUNSET
{
    public class samplehit : MonoBehaviour
    {
        [SerializeField, Header("trigger"), Tooltip("")]
        Collider hitcollider;

        [SerializeField]
        int hitlayer;

        [SerializeField, Header("コライダーを有効化するまでの時間"), Tooltip("")]
        float hittimer;
        float hitcounter;

        [SerializeField]
        GameObject[] HitObjects;
        [SerializeField, Header("生成と同時にルートオブジェクトを削除する"), Tooltip("")]
        bool destroy;

        private void Start()
        {
            hitcounter = hittimer;
            if (hitcollider == null) { hitcollider = GetComponent<Collider>(); }
            hitcollider.enabled = false;
        }

        private void Update()
        {
            hitcounter -= Time.deltaTime;
            if(hitcounter < 0) { hitcollider.enabled = true; }
        }

        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.layer == hitlayer) 
            {
                for (int i = 0; i < HitObjects.Length; i++)
                {
                    GameObject a = Instantiate(HitObjects[i], transform.position, transform.rotation);
                }

                if (destroy) { Destroy(transform.root.gameObject); }
            }
        }
    }
}


ホーミング弾プレハブのrootにこのスクリプトをアタッチし、リジッドボディとコライダーをアタッチします。
コライダーをトリガーに設定して、Hitcolliderにコライダーを設定、ターゲットオブジェクトにコライダーをアタッチして、ターゲットのレイヤーをHitlayerに設定。
Hittimerには、当たり判定を有効化するまでの時間を設定
HitObjectsに、衝突時に生成するプレハブ(爆発エフェクト、SEなど)を設定。
Destroyで、衝突後に自身=rootオブジェクトを削除するチェックをオンにします。
これでホーミング挙動関係なく、弾がターゲットのコライダーに当たったら爆発します。
爆発エフェクト、SEのプレハブには(なんならホーミング弾にも)時間で自身を削除するスクリプトもアタッチしておきます。
こういうやつ↓

using UnityEngine;

public class DestroyDelay : MonoBehaviour {
    public float DestroyTime;
    float Dcount;
    void Start () 
    {
        if (DestroyTime <= 0) { DestroyTime = 1.0f; }
    }

	void FixedUpdate () 
    {
        Dcount += Time.deltaTime;
        if (Dcount > DestroyTime)
        {
            Dcount = 0;
            Destroy(this.gameObject);
        }
	}
}


これだけだと一つのターゲットにしか弾が飛んでいかないので、ゲームで実際に使うには、ターゲットを複数保存できるようにする>ホーミング弾を生成したら弾のターゲットを上書きする&弾の数をターゲットと併せて調整するスクリプトを作る必要があるでしょう。(それ以前にダメージやHPを管理するあれこれが必要ですが)

ホーミング弾のエフェクト部分の作り方については解説は省略します。パーティクルのサブエミッターやトレイルを組み合わせて作ればいいと思いますが、それ以上の見た目を追求すると、チューブ上のメッシュを生成しながら透明度を可変…、とかになるんでしょうか?

アセットストアに出せるんかな

私の創作活動の進捗、当記事のご意見・ご感想には、Discordをぜひご登録ください。
Discord Kelorin Jo https://discord.gg/wbUJVdJcga

いいなと思ったら応援しよう!