Menu
CoddyTech

Bán

Bài học 19 trên 25 trong khóa học Game Unity: thủ thành của Coddy.

Bán tháp sẽ hoàn lại một nửa chi phí, bao gồm cả chi phí nâng cấp, vì vậy di chuyển một tháp phòng thủ sẽ tốn tiền, nhưng không phải toàn bộ số tiền. Tháp lưu tổng số tiền đã chi cho nó: giá lúc xây dựng, cộng với giá của mỗi lần nâng cấp khi thanh toán. Tiền hoàn lại bằng một nửa tổng số đó, phép chia số nguyên sẽ làm tròn xuống. Dịch vụ cho thuê xe đạp cũng lưu tổng số tiền theo cách tương tự:

public void ExtendRide(int extraHours)
{
    paid += extraHours * pricePerHour;
}

public int Deposit => paid / 4;

Bán tháp sẽ phá hủy nó, nhưng trường tower của bệ vẫn trỏ đến tháp đó. Toán tử == của Unity xem một đối tượng đã bị phá hủy là null, nên từ khung hình tiếp theo, pad.tower == null là đúng và bệ lại trống mà không cần thêm code. Bảng điều khiển thì khác: nó vẫn hiển thị tháp đã bán, và khi nhấp vào Upgrade, chương trình sẽ truy cập một đối tượng đã bị phá hủy và gây ra MissingReferenceException. Bán tháp sẽ bỏ chọn mọi thứ, khiến bảng điều khiển ẩn đi.

challenge icon

Thử thách

Trung bình

Bán một tháp với giá bằng một nửa chi phí ban đầu.

  1. Trong Tower, giữ private int spent;: data.cost trong Start, cộng thêm giá của mỗi lần nâng cấp trong Upgrade(). Thêm public int SellValue => spent / 2;.
  2. Trong BuildManager, hiển thị Sell 25 (giá bán) trong TMP_Text được tuần tự hóa sellLabel khi bảng được điền thông tin.
  3. Viết public void SellSelected(): hoàn lại giá bán, phá hủy tháp và không chọn gì.

Nhấn Chơi, sau đó xây, nâng cấp và bán một tháp. Rồi nhấn Kiểm tra.

Tự làm thử

using UnityEngine;
using UnityEngine.EventSystems;
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 Button upgradeButton;
    [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 || EventSystem.current.IsPointerOverGameObject())
        {
            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;
        if (level < data.maxLevel)
        {
            towerStatsText.text = "Damage " + data.DamageAt(level) + " > " + data.DamageAt(level + 1)
                + "\nRange " + data.RangeAt(level) + " > " + data.RangeAt(level + 1);
            upgradeLabel.text = "Upgrade " + data.UpgradeCost(level);
            upgradeButton.interactable = true;
        }
        else
        {
            towerStatsText.text = "Damage " + data.DamageAt(level) + "\nRange " + data.RangeAt(level);
            upgradeLabel.text = "Max level";
            upgradeButton.interactable = false;
        }
    }

    public void UpgradeSelected()
    {
        if (selected.Level >= selected.data.maxLevel)
        {
            return;
        }

        if (game.TrySpend(selected.data.UpgradeCost(selected.Level)))
        {
            selected.Upgrade();
            ShowPanel();
        }
    }
}

Tất cả bài học trong Game Unity: thủ thành

Tự luyện tập: Playground Unity