Menu
CoddyTech

بيع

الدرس 19 من 25 في دورة لعبة Unity: الدفاع عن الأبراج على Coddy.

يعيد بيع البرج نصف تكلفته، بما في ذلك الترقيات، لذا فإن نقل الدفاع يكلّف شيئًا، ولكن ليس كل شيء. يحتفظ البرج بمجموع جارٍ للأموال المنفقة عليه: سعره عند بنائه، بالإضافة إلى سعر كل ترقية عند دفعه. يكون المبلغ المسترد نصف هذا المجموع، ويقرّب القسمة على عدد صحيح الناتج إلى الأسفل. يحتفظ استئجار الدراجة بالنوع نفسه من المجموع:

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

public int Deposit => paid / 4;

يؤدي البيع إلى تدمير البرج، ويظل الحقل tower في المنصة يشير إليه. يتعامل Unity مع == على أنه يساوي null للكائن المدمّر، لذا ابتداءً من الإطار التالي تكون pad.tower == null صحيحة، وتصبح المنصة متاحة مجددًا من دون أي تعليمات برمجية إضافية. أما اللوحة فمسألة أخرى: فهي لا تزال تعرض البرج المباع، والنقر على الترقية سيصل إلى كائن مدمّر ويطرح MissingReferenceException. لا يؤدي البيع إلى تحديد أي شيء، وهذا يخفي اللوحة.

challenge icon

التحدي

متوسط

بِع برجًا بنصف تكلفته.

  1. في Tower، احتفظ بـ private int spent;: أضف data.cost في Start، بالإضافة إلى سعر كل ترقية في Upgrade(). أضف public int SellValue => spent / 2;.
  2. في BuildManager، اعرض Sell 25 (قيمة البيع) في sellLabel من نوع TMP_Text مُسلسل عند ملء اللوحة.
  3. اكتب public void SellSelected(): استرد قيمة البيع، ودمّر البرج، وأزل تحديده.

اضغط «العب»، ثم ابنِ برجًا وطوّره وبِعه. ثم اضغط «تحقق».

جرّب بنفسك

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

جميع دروس لعبة Unity: الدفاع عن الأبراج

تدرّب بنفسك: Playground لـ Unity