Menu
CoddyTech
flag Ar iconالعربيةdown icon

البيع

الدرس 13 من 18 في دورة لعبة 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، احتفظ بمتغير int خاص spent: تكلفة البرج cost، التي تُحدَّد في Start، بالإضافة إلى تكلفة كل ترقية عند شرائها. أضف public int SellValue => spent / 2;.
  2. في BuildManager، اعرض Sell 25، قيمة بيع البرج المحدد، في عنصر TMP_Text متسلسل sellLabel كلما تم ملء اللوحة.
  3. اكتب public void SellSelected()، التي يستدعيها زر البيع: أعد قيمة البيع عبر GameManager، ودمّر البرج، ولا تحدد أي شيء.

اضغط Play، ثم ابنِ بعض الأبراج وطوّرها وبِعها، ثم اضغط Check.

جرّب بنفسك

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();
        }
    }
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

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