【Unity2D】モンスターせん滅トリガーを作る

Unity2D

Unity2D ARPG

前々回あたりにモンスターを討伐したら宝箱が出る仕組みを作って小さなカギの扉を開く仕組みを作ろうとしてたのですが、
気づいたら宝箱を開けて鍵をとる仕組みだけ作っててモンスターせん滅フラグを完全に忘れてました。

今回はその補填と南に行くための扉を開くトリガーも作りましょう。

モンスターせん滅トリガーを作る

すでにモンスターせん滅トリガーはできているので、前回のスイッチ同様
モンスターの数をチェックしつつモンスターの数倒したらトリガー発動するようにします。

まずは宝箱を透明にして判定も消してしまいます。

そしてモンスターせん滅トリガーで判定を復活させる感じですね。

それと同時に南の扉オブジェクトを保持しておき同時に扉を開くようにしてみます。

まずはモンスターを3匹ほど配置しましょう。

これでモンスターの数が3になったので、モンスターを倒したときに
床スイッチと同じようにチェックをかけるようにします。

コリジョンはコリジョンで作りましょう。

そしてコリジョン側でモンスターのステータスを変動させたほうがいいですね。

とりあえず再び「_SmallEnemyCollision.cs」を復活させて
床スイッチみたいな仕組みにします。

using System.Collections;
using UnityEngine;

public class _SmallEnemyCollision : MonoBehaviour
{

    // 自身の保持ナンバー
    [SerializeField]
    private uint myNumber = 0;


    // 自身のリジッドボディ格納用
    private Rigidbody2D rb;

    // 親に通知のため
    private State_EnemyManage _EnemyManager;
    private State_EnemyActor _EnemyActor;


    // Start is called before the first frame update
    private void Start()
    {
        // 自身のリジッドボディを取得
        rb = GetComponent<Rigidbody2D>();
        // 親オブジェクトのスクリプトを取得
        _EnemyManager = transform.parent.gameObject.GetComponent<State_EnemyManage>();
        _EnemyActor = transform.gameObject.GetComponent<State_EnemyActor>();
    }

    // 衝突判定
    private void OnTriggerEnter2D(Collider2D other)
    {
        // 衝突判定が武器タグでHPが0より大きければ
        if (other.tag == "Weapon" && _EnemyActor.EnemyStatus[0] > 0)
        {
            // 武器ステートスクリプトを取得して変数forceを利用してノックバックさせる
            rb.AddForce(other.GetComponent<_ObjectStateWeapon>().force);
            // ヒットしたら自身のHPを減らす
            _EnemyActor.EnemyStatus[0]--;
            // モンスターのHPが0になったかチェック
            if (_EnemyActor.checkEnemyState())
            {
                _EnemyManager.countDownMonster();
            }


        }
    }

}

一応順番に倒したいときようにマイナンバーを持たせました。

順不同の場合は特に設定しなくてもいいです。

EnemyActorから処理を一部移しています。

チェック関連はこちらで行い、EnemyActorではステートの調整。

EnemyManagerは全体の管理となっています。

ActorとManager両方載せておきます。

まずはManagerのほう

using System.Collections;
using UnityEngine;

public class State_EnemyManage : MonoBehaviour
{

    // フロア内モンスター数カウント保持
    protected int _enemyCount = 0;
    public int EnemyCount
    {
        get { return _enemyCount; }
        protected set { _enemyCount = value; }
    }

    // エリア保持変数
    [SerializeField]
    private int _area;

    // エリア稼働フラグ
    private bool _activeArea;

    // 対象の扉
    [SerializeField]
    private GameObject _SwitchDoor;


    // プレイヤーステータス確保用変数
    protected State_Player _State;

    // Start is called before the first frame update
    protected virtual void Start()
    {
        // 初回時にカウントしておく
        checkAreaMonsters();
        // プレイヤーステータスを取得
        _State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();

    }

    // Update is called once per frame
    protected virtual void Update()
    {
        isMoveMonsters();
    }

    // フロア内モンスターカウント
    int checkAreaMonsters()
    {
        // 一応カウント初期化
        EnemyCount = 0;

        foreach (Transform child in transform)
        {
            EnemyCount += 1;
        }

        Debug.Log(EnemyCount);
        return EnemyCount;
    }

    // モンスターカウントを減らす
    public void countDownMonster()
    {
        // モンスターが0より多ければ作動
        if (EnemyCount > 0)
        {
            EnemyCount -= 1;
        }

        // モンスターをせん滅した場合
        if (EnemyCount <= 0)
        {
            _SwitchDoor.transform.localPosition = Vector3.zero;
        }

    }

    // モンスターを動かすか止める
    private void isMoveMonsters()
    {
        // プレイヤーが同じエリアに来たら
        if (_State.NowAreaNo == _area)
        {
            // エリアがアクティブじゃないなら
            if (!_activeArea)
            {
                // 1回だけ発動させたいのでエリアアクティブフラグを立てる
                _activeArea = true;
                // 全子オブジェクトを回す(アクティブフラグにより1度だけforeachをする
                foreach (Transform child in transform)
                {
                    // プレイヤーがエリアにきたら稼働させる
                    child.GetComponent<State_EnemyActor>().MoveFlag = true;
                }
            }
        }
        else
        {
            // アクティブフラグが立ってたら折ってモンスターをストップさせて初期位置に戻す
            if (_activeArea)
            {
                _activeArea = false;
                // 全子オブジェクトを回す(アクティブフラグにより1度だけforeachをする
                foreach (Transform child in transform)
                {
                    // プレイヤーがエリアにきたら稼働させる
                    child.GetComponent<State_EnemyActor>().MoveFlag = false;
                }
            }
        }
    }

}

そしてActorのほうはこんな感じ

using System.Collections;
using UnityEngine;

public class State_EnemyActor : State_EnemyManage
{
    // 自身のリジッドボディ格納用
    private Rigidbody2D rb;

    // モンスターのステータス
    public int[] EnemyStatus = {
        1, // エネミーのHP
        1, // 行動タイプ
        1, // 速度
        0, // 不死フラグ
        1 // 復活フラグ
    };

    // モンスターの初期配置
    [SerializeField]
    private Vector3 _initPos = Vector3.zero;

    // 移動可能かのフラグ
    private bool _moveFlag = false;
    public bool MoveFlag { get; set; }

    protected override void Start()
    {
    }

    protected override void Update()
    {
    }

    // モンスターのステータスをチェック
    public bool checkEnemyState()
    {
        // 自身のHPをチェック
        if (EnemyStatus[0] <= 0)
        {
            // HPが0になったら

            // ムーブフラグを停止
            MoveFlag = false;
            // 衝突判定コンポーネントを無効化
            GetComponent<BoxCollider2D>().enabled = false;
            // 姿を透明にする
            GetComponent<SpriteRenderer>().color = new Color(0, 0, 0, 0);
            // 一応初期配置に戻しておく
            transform.localPosition = _initPos;

            // 倒せたらtrueを返す
            return true;
        }
        else
        {
            return false;
        }
    }

}

コチラも個のステータスとオブジェクトステータスの変更とその通知だけになりました。

ActorでManagerの関数を呼ぶとシリアライズフィールドが自分自身のものを呼ぶようなので、
親に設定したドアのオブジェクトが参照されず少し悩んでいました。

じゃあコリジョン側のほうでManagerの関数を呼べばいいだけでした。

あとは開けたい南の扉を設置してManagerの_SwitchDoor変数にオブジェクト登録しましょう。

これでモンスター全て倒すと扉が開いてくれます。

Unity2D モンスターせん滅トリガー1

これが

Unity2D モンスターせん滅トリガー2

こうなりました。

あとは宝箱の出現ですね。

EnemyManagerで宝箱オブジェクトを保持する変数を作ります。

using System.Collections;
using UnityEngine;

public class State_EnemyManage : MonoBehaviour
{

    // フロア内モンスター数カウント保持
    protected int _enemyCount = 0;
    public int EnemyCount
    {
        get { return _enemyCount; }
        protected set { _enemyCount = value; }
    }

    // エリア保持変数
    [SerializeField]
    private int _area;

    // エリア稼働フラグ
    private bool _activeArea;

    // 対象の扉
    [SerializeField]
    private GameObject _SwitchDoor;

    // 対象の出現オブジェクト
    [SerializeField]
    private GameObject _TreasureChest;



    // プレイヤーステータス確保用変数
    protected State_Player _State;

    // Start is called before the first frame update
    protected virtual void Start()
    {
        // 初回時にカウントしておく
        checkAreaMonsters();
        // プレイヤーステータスを取得
        _State = GameObject.FindGameObjectsWithTag("State_Manager")[0].GetComponent<State_Player>();

        // トリガー宝箱が登録されてたら非表示にする
        if (_TreasureChest)
        {
            _TreasureChest.transform.GetComponent<SpriteRenderer>().color = new Color(255, 255, 255, 0);
            _TreasureChest.transform.GetComponent<BoxCollider2D>().enabled = false;
        }

    }

    // Update is called once per frame
    protected virtual void Update()
    {
        isMoveMonsters();
    }

    // フロア内モンスターカウント
    int checkAreaMonsters()
    {
        // 一応カウント初期化
        EnemyCount = 0;

        foreach (Transform child in transform)
        {
            EnemyCount += 1;
        }
        return EnemyCount;
    }

    // モンスターカウントを減らす
    public void countDownMonster()
    {
        // モンスターが0より多ければ作動
        if (EnemyCount > 0)
        {
            EnemyCount -= 1;
        }

        // モンスターをせん滅した場合
        if (EnemyCount <= 0)
        {
            // 倒したのでモンスターを動かす
            if (_SwitchDoor)
            {
                _SwitchDoor.transform.localPosition = Vector3.zero;
            }
            // 倒したので宝箱を出現させる
            if (_TreasureChest)
            {
                _TreasureChest.transform.GetComponent<SpriteRenderer>().color = new Color(255, 255, 255, 255);
                _TreasureChest.transform.GetComponent<BoxCollider2D>().enabled = true;
            }

        }

    }

    // モンスターを動かすか止める
    private void isMoveMonsters()
    {
        // プレイヤーが同じエリアに来たら
        if (_State.NowAreaNo == _area)
        {
            // エリアがアクティブじゃないなら
            if (!_activeArea)
            {
                // 1回だけ発動させたいのでエリアアクティブフラグを立てる
                _activeArea = true;
                // 全子オブジェクトを回す(アクティブフラグにより1度だけforeachをする
                foreach (Transform child in transform)
                {
                    // プレイヤーがエリアにきたら稼働させる
                    child.GetComponent<State_EnemyActor>().MoveFlag = true;
                }
            }
        }
        else
        {
            // アクティブフラグが立ってたら折ってモンスターをストップさせて初期位置に戻す
            if (_activeArea)
            {
                _activeArea = false;
                // 全子オブジェクトを回す(アクティブフラグにより1度だけforeachをする
                foreach (Transform child in transform)
                {
                    // プレイヤーがエリアにきたら稼働させる
                    child.GetComponent<State_EnemyActor>().MoveFlag = false;
                }
            }
        }
    }
}

これを書いてて別エリアのManagerスクリプトがドアも宝箱もオブジェクトが登録されてないとなったので、
モンスターせん滅によるトリガーにしていない場合は変数にオブジェクトがないと怒られます。

その場合、ifでtrueの場合だけ処理するようにしました。

こうすればトリガーの有無を実現できました。

あとは宝箱オブジェクトをマネージャーの変数に登録すれば完了です。

仕掛けとしてはこれで終わりましょう。

あとは組み合わせで部屋の謎解きを作っていきます。

余裕があれば爆弾と矢の実装をしたいですね。

新たな仕掛けづくりになったら記事にします。

それでは!