Menu
CoddyTech

升级

Coddy Unity 塔防游戏 课程的第 18 课,共 25 课。

面板的“升级”按钮会调用 UpgradeSelected。它通过 GameManager 的 TrySpend 支付,所以钱不够时不会有任何变化。达到最高等级后,就没有可购买的内容了:属性只显示当前值,而且按钮无法交互。

面板是覆盖在 3D 视图上的 UI,点击面板上的按钮也会被视为一次鼠标按下,BuildManager 也会检测到这次操作。射线会穿过按钮,指向它后面的地面,而 BuildManager 会在按钮松开前关闭面板。当指针悬停在 UI 上时,EventSystem.current.IsPointerOverGameObject() 的值为 true,因此 BuildManager 会提前返回,把这些点击留给按钮处理。

challenge icon

挑战

中等

从面板升级选中的防御塔。

  1. 编写 public void UpgradeSelected():当等级低于 maxLevel 时,如果 game.TrySpend(data.UpgradeCost(level)) 扣款成功,就升级防御塔并刷新面板。
  2. 达到 maxLevel 时,面板分两行显示 Damage 4 和 Range 5.5,并显示 Max level;序列化的 upgradeButton 不可交互。
  3. 在 Update 中,当 EventSystem.current.IsPointerOverGameObject() 返回 true 时直接返回。

按下“开始”,建造一座防御塔,点击它并升级两次:面板会显示 Max level。然后点击“检查”。

亲手试试

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);
    [SerializeField] private GameObject towerPanel;
    [SerializeField] private TMP_Text towerNameText;
    [SerializeField] private TMP_Text towerStatsText;
    [SerializeField] private TMP_Text upgradeLabel;

    private Tower chosen;
    private Tower selected;

    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]);
        Select(null);
    }

    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))
        {
            Select(null);
            return;
        }

        Tower tower = hit.collider.GetComponentInParent<Tower>();
        if (tower != null)
        {
            Select(tower);
            return;
        }

        Select(null);
        if (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);
    }

    void Select(Tower tower)
    {
        selected = tower;
        towerPanel.SetActive(tower != null);
        if (tower != null)
        {
            ShowPanel();
        }
    }

    void ShowPanel()
    {
        TowerData data = selected.data;
        int level = selected.Level;
        towerNameText.text = data.towerName + " level " + level;
        towerStatsText.text = "Damage " + data.DamageAt(level) + " > " + data.DamageAt(level + 1)
            + "\nRange " + data.RangeAt(level) + " > " + data.RangeAt(level + 1);
        upgradeLabel.text = "Upgrade " + data.UpgradeCost(level);
    }
}

Unity 塔防游戏中的所有课

自己练习: Unity 在线编辑器