Menu
CoddyTech

Vendre

Leçon 13 sur 18 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
  1. Dans Tower, conservez un int privé spent : le cost de la tour, défini dans Start, plus le prix de chaque amélioration achetée. Ajoutez public int SellValue => spent / 2;.
  2. Dans BuildManager, affichez Sell 25, la valeur de revente de la tour sélectionnée, dans un TMP_Text sérialisé sellLabel chaque fois que le panneau est rempli.
  3. Écrivez public void SellSelected(), appelée par le bouton Sell : remboursez la valeur de revente via le GameManager, détruisez la tour et ne sélectionnez rien.

Appuyez sur Play, puis construisez, améliorez et vendez quelques tours, puis cliquez sur Check.

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

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

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