【自作ゲーム開発5】一番近い対象をロックオンする【Unity】


注意
本記事で掲載されている動作の実装方法及びプログラムのソースコードは最適な方法ではない可能性があります。
今後不具合等が判明した場合には修正及び改良をおこなう可能性があります。
また今後自分で同機能を実装する場合の参考にする可能性もあるためソースコードだけではなく説明しつつ記事を進めていきます。

はじめに

Unityによる自作ゲーム開発進捗その5になります!

今回はこのゲームに実装予定のタワーディフェンスシステムの一部を実装していきます。

以下にソースコードを掲載します。

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

public class TowerLockon : MonoBehaviour{
    [SerializeField]
    private Transform m_this_transform;
    private BoxCollider m_collider;

    private List<GameObject> m_object_list = new List<GameObject>();
    private List<Transform> m_position_list = new List<Transform>();

    private string m_target_tag = null;

    private float m_lockon_speed = 0.05f;
    private float m_lockon_range = 30.0f;

    private int m_array_set = 0;

    private bool m_lockon_state = false;

    private void Start(){
        m_collider = this.GetComponent<BoxCollider>();
        LockonStatusSet("Player", 30.0f, 50.0f); // タスク1
    }

    private void FixedUpdate(){
        if(m_lockon_state){
            m_this_transform.rotation = Quaternion.Slerp(m_this_transform.rotation, Quaternion.LookRotation(m_position_list[m_array_set].position - m_this_transform.position), m_lockon_speed);

            if(((m_this_transform.position - m_position_list[m_array_set].position).sqrMagnitude > m_lockon_range) || (m_object_list[m_array_set].activeSelf == false)){
                m_object_list.Clear();
                m_position_list.Clear();
                m_array_set = 0;
                m_lockon_state = false;
                m_collider.enabled = false;
                m_collider.enabled = true;
            }
        }else{
            if(m_object_list.Count > 0){
                float l_distance_check = m_lockon_range;

                for(int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1){
                    if((m_this_transform.position - m_position_list[l_loop].position).sqrMagnitude < l_distance_check){
                        m_array_set = l_loop;
                        l_distance_check = (m_this_transform.position - m_position_list[l_loop].position).sqrMagnitude;
                    }
                }

                m_lockon_state = true;
            }
        }
    }

    private void OnTriggerEnter(Collider a_collider){
        if(m_lockon_state) return;

        if(a_collider.gameObject.tag == m_target_tag){
            m_object_list.Add(a_collider.gameObject);
            m_position_list.Add(a_collider.gameObject.GetComponent<Transform>());
        }
    }

    public void LockonStatusSet(string a_tag, float a_area, float a_range){
        m_target_tag = a_tag;
        m_collider.size = new Vector3(a_area, 10.0f, a_area);
        m_lockon_range = a_range;
    }
}

/*
    [規則]
    p_ 外部アクセス
    m_ メンバー変数
    l_ ローカル変数
    a_ 引数
    e_ 列挙型

    [説明]

    [バージョン]
    2021-01-18 ロックオン機能とロックオンが外れた場合に一番近いターゲットに切り替える機能

    [タスク]
    タスク1 キャラクターのタワー設置時に別スクリプトからセットする
*/

タワー種類の概要

今回からタワーシステムが出てくるため簡単な説明をいたします。

本ゲームの戦闘方法はプレイヤーが自身で戦うファイター型タワー戦闘に特化したビルダー型を分けており、タワーは主にビルダーが使用する戦闘方法です。

どちらかの戦闘方法しか使用出来ない訳ではないので、ファイタースタイルでも補助用のタワーをセットしたりする事も可能にする予定です。

本ゲームに実装される予定のタワーの種類を大きく分けると以下の3タイプです。

  • ロックオン型
  • 固定範囲型
  • 自立型
ロックオン型
  • 範囲内の敵に向きを合わせてから攻撃する2動作が必要なタイプ
  • 固定範囲型に比べると威力は高め
固定範囲型
  • 自分を中心にした範囲や前方直線など一定間隔で同じ範囲に攻撃するタイプ
  • 向きを合わせる動作がないため威力は控えめ
自立型
  • 自動で動き回り戦闘するビルダー型の切り札的なタイプ
  • 一定範囲内の敵を攻撃したり味方の補助をしたりと強力だがスキル枠を多く使用したり呼び出しコストが高い等の制限が存在する

ロックオン型でも着弾地点で爆発する範囲攻撃だったり固定範囲型でも射程が短く高威力なども分かれるため上記の内容に必ずしも該当している訳ではありません

また現在実装予定のタワーが上記の3タイプに分かれていますが今後ふとした閃きで追加となる可能性もあります


ロックオンシステム

感知

範囲内に侵入した一番近い敵をロックオンします

追尾

ロックオンしたターゲットの方向を向き続ける

解除

ロックオンしたターゲットが範囲外に出た場合等に感知へ戻る

public void LockonStatusSet(string a_tag, float a_area, float a_range){
    m_target_tag = a_tag;
    m_collider.size = new Vector3(a_area, 10.0f, a_area);
    m_lockon_range = a_range;
}

まだキャラクターの実装が済んでいませんがタワーの設置時に上記の関数でキャラクターステータスをタワーに反映させるために呼び出して範囲や射程等を決定する。


private void OnTriggerEnter(Collider a_collider){
    if(m_lockon_state) return;

    if(a_collider.gameObject.tag == m_target_tag){
        m_object_list.Add(a_collider.gameObject);
        m_position_list.Add(a_collider.gameObject.GetComponent<Transform>());
    }
}

コライダーに初期化した時に設定したタグ(おそらくPlayerとEnemyの2つ)が侵入するとオブジェクトと位置情報を各別リストに追加します。


if(m_object_list.Count > 0){
    float l_distance_check = m_lockon_range;

    for(int l_loop = 0; l_loop < m_object_list.Count; l_loop += 1){
        if((m_this_transform.position - m_position_list[l_loop].position).sqrMagnitude < l_distance_check){
            m_array_set = l_loop;
            l_distance_check = (m_this_transform.position - m_position_list[l_loop].position).sqrMagnitude;
        }
    }

    m_lockon_state = true;
}

FixedUpdate内はロックオンの真偽により分岐するようになっており、上記はロックオンしていない状態の実行内容です。

コライダー侵入によってリスト追加がされていれば追加された全てのオブジェクトとの距離を調べて一番近いオブジェクトの配列番号をm_array_setに代入してロックオン状態の変数を真としています。


m_this_transform.rotation = Quaternion.Slerp(m_this_transform.rotation, Quaternion.LookRotation(m_position_list[m_array_set].position - m_this_transform.position), m_lockon_speed);

if(((m_this_transform.position - m_position_list[m_array_set].position).sqrMagnitude > m_lockon_range) || (m_object_list[m_array_set].activeSelf == false)){
    m_object_list.Clear();
    m_position_list.Clear();
    m_array_set = 0;
    m_lockon_state = false;
    m_collider.enabled = false;
    m_collider.enabled = true;
}

上記はFixedUpdate内のロックオン状態が真の場合の実行内容です。

ロックオン状態が偽の実行時に求めた近いオブジェクトに向かって回転をおこない、設定した距離以上離れたりオブジェクトが倒されて休止になった場合はリストの初期化やロックオン状態の変更及びコライダーの切り替えをおこないます。


m_collider.enabled = false;
m_collider.enabled = true;

次のターゲットへ移る場合に現在コライダー内に居るオブジェクトの侵入判定が取れないためコライダーの切り替えをおこないコライダー内のオブジェクト侵入判定を取れるようにしています。

次のFixedUpdateまでにOnTriggerEnter(Update関数と同じく端末による回数の変動?)で全部のオブジェクトが取り切れるかが端末依存になるかもしれないため今後の様子を見つつ改良を加える予定です。


今回使用するアセット


動画


記事のリンク

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です