概要
前回の続きです。
今回は1Wave目で敵がKillされた場合、2Wave以降沸かなくなるバグを修正します。
以下gifアニメは、改善後の様子です。
敵がkillされても、次のWaveで湧いていることを確認できます。
開発環境
IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10
UnityEditor上の設定
前回と変化なし
実装のポイント
EnemyHealthクラスについて
デザインパターンのObserveパターンを使っています。役割はObservaleです。
HPが0になった時にObserverに通知するようになってます。
Spawnerクラスについて
デザインパターンのObserveパターンを使っています。役割はObserverです。
ObservaleのSpawnerから通知後、次Waveで湧く処理を実行します。
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 コミット分
章まとめ