0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

タワーデフェンスゲーム Unity 敵の機能 10/10 敵オブジェクトがKillされた場合でも、次ウェイブで湧くようにする

Last updated at Posted at 2023-04-27

概要

前回の続きです。

今回は1Wave目で敵がKillされた場合、2Wave以降沸かなくなるバグを修正します。
以下gifアニメは、改善後の様子です。
敵がkillされても、次のWaveで湧いていることを確認できます。

NextWave.gif

開発環境

IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10

UnityEditor上の設定

前回と変化なし

実装のポイント

EnemyHealthクラスについて
デザインパターンのObserveパターンを使っています。役割はObservaleです。
HPが0になった時にObserverに通知するようになってます。

Spawnerクラスについて
デザインパターンのObserveパターンを使っています。役割はObserverです。
ObservaleのSpawnerから通知後、次Waveで湧く処理を実行します。

image.png

Enemyクラスについて
メソッド名の変更とHPの初期化処理を追加するぐらい

コード部分

Enemy

Enemy.cs
using System;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public static Action OnEndReached;
    
    [SerializeField] private float moveSpeed = 3f;
    public Waypoint Waypoint { get; set; }
    public Vector3 CurrentPointPosition => Waypoint.GetWaypointPosition(_currentWaypointIndex);
    private int _currentWaypointIndex;
+    private EnemyHealth _enemyHealth;
    
    private void Start()
    {
        _currentWaypointIndex = 0;
+        _enemyHealth = GetComponent<EnemyHealth>();
    }

    private void Update()
    {
        Move();
        if (CurrentPointPositionReached())
        {
            UpdateCurrentPointIndex();
        }
    }

    private void Move()
    {
        Vector3 currentPosition = Waypoint.GetWaypointPosition(_currentWaypointIndex);
        transform.position = Vector3.MoveTowards(transform.position, currentPosition, moveSpeed * Time.deltaTime);
    }
    
    private bool CurrentPointPositionReached()
    {
        return Vector3.Distance(transform.position, CurrentPointPosition) < 0.1f;
    }

    private void UpdateCurrentPointIndex()
    {
        int lastWaypointIndex = Waypoint.Points.Length - 1;
        if (_currentWaypointIndex < lastWaypointIndex)
        {
            _currentWaypointIndex++;
        }
        else
        {
-             ReturnEnemyToPool();
+            EndPointReached();
            
        }
    }

-    private void ReturnEnemyToPool()
+    private void EndPointReached()
    { 
        OnEndReached?.Invoke();
+        _enemyHealth.ResetHealth();
        ObjectPooler.ReturnToPool(gameObject);
    }

    public void ResetEnemy()
    {
        _currentWaypointIndex = 0;
    }
}


EnemyHealth

EnemyHealth.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.UI;

public class EnemyHealth : MonoBehaviour
{
+    public static Action OnEnemyKilled;
    
    // Start is called before the first frame update
    [SerializeField] private GameObject healthBarPrefab;
    [SerializeField] private Transform barPosition;

    [SerializeField] private float initialHealth = 10f;
    [SerializeField] private float maxHealth = 10f;

    public float CurrentHealth { get; set; }
    private Image _healthBar;
    void Start()
    {
        CreateHealthBar();
        CurrentHealth = initialHealth;
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            DealDamage(5f);
        }

        _healthBar.fillAmount = Mathf.Lerp(_healthBar.fillAmount, CurrentHealth / maxHealth, Time.deltaTime * 10f);
    }

    private void CreateHealthBar()
    {
        GameObject newBar = Instantiate(healthBarPrefab,barPosition.position,Quaternion.identity);
        newBar.transform.SetParent(transform);
        
        EnemyHealthContainer container = newBar.GetComponent<EnemyHealthContainer>();
        _healthBar = container.FillAmountImage;
    }

    public void DealDamage(float damageReceived)
    {
        CurrentHealth -= damageReceived;
        if (CurrentHealth <= 0)
        {
            CurrentHealth = 0;
            Die();
        }
    }

+    public void ResetHealth()
+    {
+        CurrentHealth = initialHealth;
+        _healthBar.fillAmount = 1f;
+    }
    private void Die()
    {
+        OnEnemyKilled?.Invoke();
        CurrentHealth = initialHealth;
        _healthBar.fillAmount = 1f;
        ObjectPooler.ReturnToPool(gameObject);
    }
    
    
}



Spawner

Spawner.cs
using System;
using System.Collections;
using UnityEngine;


    public class Spawner : MonoBehaviour
    {
        [SerializeField]  private  int enemyCount = 10;
        // Btw = Between
        [SerializeField] private float delayBtwSpawns;
        [SerializeField] private float delayBtwWaves = 1f;

        private int _enemiesRemaining;
        
        private int _enemiesSpawned;
        private float _spawnTimer;
        private ObjectPooler _objectPooler;
        private Waypoint _waypoint;

        private void Start()
        {
            _objectPooler = GetComponent<ObjectPooler>();
            _waypoint = GetComponent<Waypoint>();
            
            _enemiesRemaining = enemyCount;
        }

        private void Update()
        {
            _spawnTimer -= Time.deltaTime;
            if (_spawnTimer < 0)
            {
                _spawnTimer = delayBtwSpawns;
                if (_enemiesSpawned < enemyCount)
                {
                    SpawnEnemy();
                    _enemiesSpawned++;
                }
            }
        }
        
        // ReSharper disable Unity.PerformanceAnalysis
        private void SpawnEnemy()
        {
            GameObject newInstance = _objectPooler.GetInstanceFromPool();
            Enemy enemy = newInstance.GetComponent<Enemy>();
            enemy.Waypoint = _waypoint;
            enemy.transform.localPosition = transform.position;
            enemy.ResetEnemy();
            newInstance.SetActive(true);
        }

        private IEnumerator NextWave()
        {
            yield return new WaitForSeconds(delayBtwWaves);
            _enemiesRemaining = enemyCount;
            _spawnTimer = 0f;
            _enemiesSpawned = 0;

        }
 
-        private void RecordEnemyEndReached()
+        private void RecordEnemy()
        {
            _enemiesRemaining--;
            if (_enemiesRemaining <= 0)
            {
                StartCoroutine(NextWave());
            }
        }

        private void OnEnable()
        {
-            Enemy.OnEndReached += RecordEnemyEndReached;
+            Enemy.OnEndReached += RecordEnemy;

+            EnemyHealth.OnEnemyKilled += RecordEnemy;
        }


        private void OnDisable()
        {
-             Enemy.OnEndReached -= RecordEnemyEndReached;
+            Enemy.OnEndReached -= RecordEnemy;

+            EnemyHealth.OnEnemyKilled -= RecordEnemy;
        }
    }



参考

Section4 18

github コミット分

章まとめ

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?