Menu
CoddyTech

Vendre

Leçon 19 sur 25 du cours Jeu Unity : défense de tours de Coddy.

La vente d’une tour rembourse la moitié de son coût, améliorations comprises : déplacer une défense a donc un coût, mais pas la totalité de celui-ci. La tour conserve un total cumulatif de l’argent dépensé pour elle : son prix à la construction, auquel s’ajoute le prix de chaque amélioration au moment où il est payé. Le remboursement correspond à la moitié de ce total, et une division entière arrondit à l’inférieur. Une location de vélo conserve le même type de total :

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

public int Deposit => paid / 4;

La vente détruit la tour, et le champ tower du socle pointe toujours vers elle. L’opérateur == de Unity considère un objet détruit comme null. Ainsi, dès la frame suivante, pad.tower == null est vrai, et le socle est de nouveau libre sans code supplémentaire. Le panneau, lui, pose un autre problème : il affiche toujours la tour vendue, et un clic sur Upgrade tenterait d’accéder à un objet détruit et déclencherait une MissingReferenceException. La vente ne sélectionne rien, ce qui masque le panneau.

challenge icon

Défi

Moyen

Vends une tour pour la moitié de son coût.

  1. Dans Tower, conserve private int spent; : data.cost dans Start, plus le prix de chaque amélioration dans Upgrade(). Ajoute public int SellValue => spent / 2;.
  2. Dans BuildManager, affiche Sell 25 (la valeur de revente) dans un TMP_Text sérialisé sellLabel lorsque le panneau se remplit.
  3. Écris public void SellSelected() : récupère la valeur de revente, détruis la tour et ne sélectionne rien.

Appuie sur « Jouer », puis construis, améliore et vends une tour. Ensuite, appuie sur « Vérifier ».

Essayez vous-même

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

Toutes les leçons de Jeu Unity : défense de tours

Entraînez-vous par vous-même : Playground Unity