游戏开发小结——创建wave系统

目标:学习如何实现Wave系统。

目前,我们的敌人每隔几秒就会无限期地随机生成。现在我们想将其更新为Wave系统,这意味着每一波都会产生一定数量的敌人,每一波都会变得越来越难。
击败一波后,下一波将包含更多敌人、更快的敌人,而且生成速度也更快。
由于我们要在游戏中实现Wave系统,因此最好有一条消息告诉玩家下一波何时开始。
[SerializeField] private TextMeshProUGUI _waveSpawnText;
[SerializeField] private TextMeshProUGUI _waveNumberText;
有了文本的新变量,我们只需在检查器上调整它们即可。
这样我们的 UIManager 就准备好进行编码了。让我们从这里开始吧。
public void UpdateWaveText(int wave)
{
    _waveNumberText.text = "Wave: " + wave.ToString();
}
这个很简单。当我们有一个新的wave时,我们只需调用这个方法来更新显示。
public IEnumerator NextWaveSpawnRoutine(int wave)
{
    _waveSpawnText.gameObject.SetActive(true);
    for (int i = 5; i >= 0; i--)
    {
        if(i > 0)
        {
            _waveSpawnText.text = "WAVE " + wave + " STARTS IN " + i + "!";
            yield return _waveDelay;
        }
        else
        {
            _waveSpawnText.text = "WAVE " + wave + " STARTS NOW !";
            yield return _nowDelay;
        }
    }
    _waveSpawnText.gameObject.SetActive(false);
}
现在我们的协程(Coroutine)。这就是我们在下一波开始时向玩家展示的方式。我们有一个从 5 倒数到零的循环。每秒它都会用当前波形和第二个波形更新显示。
当它达到零时,它只是说波正在开始,短暂的延迟后,我们关闭文本。
现在我们的 SpawnManager。这将包含更多变量和变化。
private int _currentWave = 1;
private int _enemiesPerWave = 5;
private float _spawnInterval = 5.0f;
private float _enemySpeedMultiplier = 1f;
private float _spawnIntervalDecreasePercentage = 10f;
private List<GameObject> _activeEnemies = new List<GameObject>();
所以我们的 SpawnManager 有 6 个新变量。让我们把它们分解一下:
  • _currentWave:保存游戏的当前波次。
  • _enemiesPerWave:保存应该生成的敌人数量。
  • _spawnInterval:保存敌人生成的时间间隔。
  • _enemySpeedMultipler:保存敌人速度的乘数,使我们的敌人更快。
  • _spawnIntervalDecreasePercentage:保存每一波相对于前一波的速度快多少。
  • _activeEnemies:保存游戏中所有活着的敌人的列表。这用于我们检查生成的所有敌人是否都已死亡,并且我们可以开始下一波。
private IEnumerator EnemySpawnRoutine()
{
    yield return UIManager.Instance.NextWaveSpawnRoutine(_currentWave);
    while (!_isPlayerAlive)
    {
        for (int i = 0; i < _enemiesPerWave; i++)
        {
            SpawnEnemy();
            yield return new WaitForSeconds(_spawnInterval);
        }

        while (_activeEnemies.Count > 0)
        {
            yield return null;
        }

        _currentWave++;
        _enemiesPerWave += 2;
        _spawnInterval *= 1.0f - (_spawnIntervalDecreasePercentage / 100.0f);
        _enemySpeedMultiplier += 0.05f;

        UIManager.Instance.UpdateWaveText(_currentWave);
        yield return UIManager.Instance.NextWaveSpawnRoutine(_currentWave);
    }
}
现在我们的协程。首先,我们使用 UIManager 中的协程的 Yield Return,这是我们的 NextWaveSpawnRoutine。我们将当前的波作为参数传递。这告诉 Unity 等待协程完成,然后再执行其余代码。
在我们的循环中,只要我们的玩家活着,它就会运行。我们首先执行一个循环,在 EnemiesPerWave 变量中生成所有敌人。
在我们的循环中,我们Yield Return返回 SpawnInterval,它从 5 秒开始,但随着游戏的进行而变得越来越快。
所有敌人生成后,我们检查是否还有活着的敌人。这是在我们的 While 循环内完成的。当有任何活着的敌人时,我们只是 Yield Return null。
当所有敌人都死后,我们就离开循环。然后我们继续增加用于我们的波浪系统的所有变量。确保我们的波数增加,就会产生更多的敌人,产生的间隔会更短,而我们的敌人会更快。
然后我们更新 UIManager 以匹配正确的 Wave,并yield return返回下一个 Wave 的协程。
现在我们的敌人。由于我们要提高敌人的移动速度,因此当我们的敌人生成时将调用此方法。
private void SpawnEnemy()
{
    float randomPositionX = Random.Range(_minXPosition, _maxXPosition);
    _enemySpawnPosition.Set(randomPositionX, _spawnPositionY, 0f);
    var enemy = Instantiate(_enemyPrefab, _enemySpawnPosition, Quaternion.identity, _enemyContainer.transform);
    _activeEnemies.Add(enemy);
    var enemyBehavior = enemy.GetComponent<EnemyBehavior>();
    if (enemyBehavior != null)
        enemyBehavior.IncreaseSpeed(_enemySpeedMultiplier);
}
仍然在我们的 SpawnManager 中,当实例化我们的敌人时,我们现在正在抓住敌人的手柄。完成此操作后,我们可以将他添加到我们的列表中,并获取他的 EnemyBehavior 脚本的句柄。在空检查之后,我们只需确保调用我们接下来要创建的方法,这会增加敌人的移动速度。
最后对于我们的 SpawnManager,在敌人被摧毁后,我们需要将他从我们的列表中删除。我们将创建一个方法,敌人可以在其中提供其游戏对象,这样我们就可以将他从列表中删除。
public void DestroyEnemy(GameObject enemy)
{
    _activeEnemies.Remove(enemy);
}
现在我们转向 EnemyBehavior 脚本。我们需要我们的方法来提高移动速度,该方法在敌人实例化后调用。
public void IncreaseSpeed(float multiplier)
{
    _enemySpeed *= multiplier;
}
这使我们能够传递一个乘数,这将提高敌人的移动速度。每波都会将我们的移动速度提高 5%,但如果需要的话可以更改。
public void TakeDamage()
{
    _animator.SetTrigger(_onEnemyDeathHash);
    _enemySpeed = 0f;
    _audioSource.Play();
    Destroy(gameObject, 2.5f);
    SpawnManager.Instance.DestroyEnemy(gameObject);
    Destroy(this);
}
现在,在我们的 TakeDamage 方法中,我们只需确保从 SpawnManager 调用 DestroyEnemy,传递我们的游戏对象作为引用,确保从我们的活动敌人中删除正在被摧毁的敌人。
现在我们的 Wave 系统已经完全正常工作了。正如我们所看到的,消灭最后一个敌人后,我们只需进入 NextWaveSpawnRoutine,它们就会继续产生更多敌人,每一波敌人都会变得更强!
01-07 17:24