Menu
CoddyTech

Улучшение

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

При нажатии на башню открывается панель с её уровнем и кнопкой улучшения. Каждый уровень увеличивает урон и дальность башни, а каждое улучшение стоит дороже предыдущего. Уровни подчиняются правилу, поэтому ресурс содержит само правило, а не список чисел: характеристики на уровне 1, прибавка за каждый уровень и цена улучшения. Методы в ScriptableObject вычисляют всё остальное, поэтому каждый скрипт, который запрашивает данные, получает одинаковый ответ. Урожай в игре про фермерство:

public int YieldAt(int level)
{
    return baseYield + yieldPerLevel * (level - 1);
}

Башня хранит только свой level, а панель показывает числа для следующего уровня рядом с текущими, чтобы игрок видел, на что тратятся деньги. При нажатии на башню щелчок попадает в одну из её частей — основание или верхушку, которые являются дочерними объектами башни, — поэтому hit.collider.GetComponentInParent<Tower>() находит башню, которой они принадлежат: сначала проверяет объект, в который попал щелчок, затем его родительские объекты.

Панель — это UI поверх 3D-вида, и нажатие кнопки улучшения также является нажатием мыши, которое видит BuildManager. Луч проходит сквозь кнопку к земле за ней, а BuildManager закрывает панель до того, как кнопка будет отпущена. EventSystem.current.IsPointerOverGameObject() возвращает true, пока указатель находится над UI, поэтому BuildManager сразу завершает выполнение и оставляет эти нажатия кнопкам.

challenge icon

Задание

Средне
  1. В TowerData добавь публичные целые числа maxLevel, upgradeCost и damagePerLevel, а также публичное число с плавающей точкой rangePerLevel. Добавь int DamageAt(int level) и float RangeAt(int level): на уровне 1 используются собственные значения урона и дальности актива, а каждый следующий уровень прибавляет значение за уровень. Добавь int UpgradeCost(int level) — цену перехода с этого уровня на следующий: upgradeCost, умноженное на уровень.
  2. В Tower храни приватное целое число level, начинающееся с 1, и предоставь к нему доступ для чтения через public int Level => level;. Стреляй с уроном DamageAt(level) и выбирай цель в пределах RangeAt(level). Добавь public void Upgrade(), увеличивающий уровень на 1.
  3. В BuildManager щелчок по башне выбирает её, а щелчок по любому другому объекту ничего не выбирает (щелчок по свободной площадке всё ещё строит на ней башню). Сериализованная панель towerPanel отображается только при выбранной башне; в towerNameText показывается Arrow level 1, в towerStatsText в двух строках — Damage 2 > 3 и Range 4.5 > 5, а в upgradeLabel — Upgrade 40. При достижении maxLevel отображаются только текущие значения характеристик, подпись гласит Max level, а кнопка upgradeButton недоступна для нажатия.
  4. Напиши public void UpgradeSelected(), которую вызывает кнопка Upgrade: если башня не достигла максимального уровня и GameManager может оплатить улучшение, улучши башню и обнови панель. В Update игнорируй щелчки по элементам интерфейса.

Нажми Play, построй башню, щёлкни по ней и улучши её, затем нажми Check.

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

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using TMPro;

public class BuildManager : MonoBehaviour
{
    [SerializeField] private GameManager game;
    [SerializeField] private Tower[] shop;
    [SerializeField] private Button[] shopButtons;
    [SerializeField] private Color chosenColor = new Color(1f, 0.85f, 0.3f);

    private Tower chosen;

    void Start()
    {
        for (int i = 0; i < shop.Length; i++)
        {
            Tower prefab = shop[i];
            shopButtons[i].GetComponentInChildren<TMP_Text>().text = prefab.data.towerName + "\n" + prefab.data.cost;
            shopButtons[i].onClick.AddListener(() => Choose(prefab));
        }
        Choose(shop[0]);
    }

    void Choose(Tower prefab)
    {
        chosen = prefab;
        for (int i = 0; i < shop.Length; i++)
        {
            shopButtons[i].image.color = shop[i] == chosen ? chosenColor : Color.white;
        }
    }

    void Update()
    {
        if (!Mouse.current.leftButton.wasPressedThisFrame)
        {
            return;
        }

        Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
        if (Physics.Raycast(ray, out RaycastHit hit) && hit.collider.TryGetComponent(out BuildPad pad))
        {
            Build(pad);
        }
    }

    void Build(BuildPad pad)
    {
        if (pad.tower != null)
        {
            return;
        }

        if (!game.TrySpend(chosen.data.cost))
        {
            Debug.Log("Not enough money");
            return;
        }

        pad.tower = Instantiate(chosen, pad.transform.position, Quaternion.identity);
    }
}
quiz iconПроверьте себя

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

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