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.
Défi
Moyen- Dans
Tower, conservez unintprivéspent: lecostde la tour, défini dansStart, plus le prix de chaque amélioration achetée. Ajoutezpublic int SellValue => spent / 2;. - Dans
BuildManager, affichezSell 25, la valeur de revente de la tour sélectionnée, dans un TMP_Text sérialisésellLabelchaque fois que le panneau est rempli. - É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();
}
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.