Menu
CoddyTech

Сделай по-своему

Урок 18 из 18 курса Игра в Unity: защита башен на Coddy.

В игре должна быть концовка. Игрок проигрывает, когда количество жизней достигает 0, и побеждает, когда отправлены все волны и на поле не осталось врагов. Спавнер знает, когда отправлена последняя волна, а Enemy.All знает, сколько врагов осталось, поэтому спавнер ждёт, пока поле опустеет, а затем вызывает событие, которое обрабатывает GameManager. Корутина ждёт выполнения условия с помощью WaitUntil, который проверяет лямбда-выражение один раз за кадр. Вот пример комнаты-головоломки, которая открывает выход, когда все переключатели включены:

yield return new WaitUntil(() => switchesOn == switches.Length);
exit.SetActive(true);

При любом из вариантов завершения игры отображается EndPanel с сообщением, и игра останавливается с помощью Time.timeScale = 0. Количество жизней не опускается ниже 0: если Танк получает урон в 2 единицы, когда у него осталась 1 жизнь, значение сразу перескочит через 0, поэтому Mathf.Max не даёт ему стать меньше 0.

Кнопка «Играть снова» загружает сцену заново. Time.timeScale не является частью сцены и сохраняет своё значение после загрузки, поэтому остановленная на 0 игра запустилась бы снова замороженной: перед загрузкой верни значение 1.

Теперь настрой игру под себя. Все параметры баланса находятся в Inspector:

  • GameManager игры: startMoney (150) и startLives (20).
  • Массив waves у спавнера: восемь строк с типом врага, количеством и интервалом между появлением врагов, а также timeBetweenWaves (8).
  • Ресурсы EnemyData: скорость, здоровье, награда и количество жизней, которое отнимает каждый тип врага. У босса 450 единиц здоровья, и он отнимает 10 жизней.
  • Ресурсы TowerData: стоимость, урон, дальность, скорострельность, значения улучшений, радиус поражения Cannon и замедление от Frost.

С настройками по умолчанию можно победить, построив смешанную оборону на поворотах пути и улучшая башни по мере поступления денег; несколько башен одного типа не удержат оборону. Меняй по одному параметру за раз и играй, чтобы понять, как он влияет на игру.

challenge icon

Задание

Сложно
  1. В WaveSpawner добавь public event Action AllWavesCleared. После отправки последней волны дождись, пока Enemy.All опустеет, а затем вызови это событие.
  2. В GameManager добавь сериализованные поля endPanel (GameObject) и endText (TMP_Text). Число жизней никогда не опускается ниже 0, а когда оно достигает 0, игра заканчивается сообщением Game Over. Когда все волны пройдены и остались жизни, игра заканчивается сообщением You Win!. При завершении показывается панель с сообщением, а Time.timeScale устанавливается в 0.
  3. Напиши public void PlayAgain(), который вызывается кнопкой панели: верни нормальную скорость времени и заново загрузи активную сцену.

Нажми Play и защищай базу на протяжении всех восьми волн, затем нажми Check. После этого настрой игру в Inspector и играй по-своему.

Попробуйте сами

using UnityEngine;
using TMPro;

public class GameManager : MonoBehaviour
{
    [SerializeField] private int startMoney = 100;
    [SerializeField] private int startLives = 20;
    [SerializeField] private WaveSpawner spawner;
    [SerializeField] private TMP_Text moneyText;
    [SerializeField] private TMP_Text livesText;
    [SerializeField] private TMP_Text waveText;

    private int money;
    private int lives;

    void OnEnable()
    {
        Enemy.ReachedBase += OnReachedBase;
        Enemy.Killed += OnKilled;
        spawner.WaveStarted += ShowWave;
    }

    void OnDisable()
    {
        Enemy.ReachedBase -= OnReachedBase;
        Enemy.Killed -= OnKilled;
        spawner.WaveStarted -= ShowWave;
    }

    void Start()
    {
        money = startMoney;
        lives = startLives;
        ShowMoney();
        ShowLives();
    }

    public bool TrySpend(int amount)
    {
        if (amount > money)
        {
            return false;
        }

        money -= amount;
        ShowMoney();
        return true;
    }

    public void Earn(int amount)
    {
        money += amount;
        ShowMoney();
    }

    void OnKilled(Enemy enemy)
    {
        Earn(enemy.data.reward);
    }

    void OnReachedBase(Enemy enemy)
    {
        lives -= enemy.data.damage;
        ShowLives();
    }

    void ShowMoney()
    {
        moneyText.text = "Money: " + money;
    }

    void ShowLives()
    {
        livesText.text = "Lives: " + lives;
    }

    void ShowWave(int wave, int total)
    {
        waveText.text = "Wave " + wave + "/" + total;
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Игра в Unity: защита башен