出售
Coddy Unity 塔防游戏 课程的第 19 课,共 25 课。
出售防御塔会退还其花费的一半,包括升级费用,因此移动防御塔会有花费,但不会退还全部费用。防御塔会累计记录花在它上面的金额:建造时的价格,加上每次支付的升级费用。退款金额是总额的一半,整数除法会向下取整。自行车租赁也会累计同类金额:
public void ExtendRide(int extraHours)
{
paid += extraHours * pricePerHour;
}
public int Deposit => paid / 4;出售会销毁防御塔,而格子的 tower 字段仍然指向它。Unity 的 == 会把已销毁的对象视为 null,因此从下一帧开始,pad.tower == null 就为 true,格子也会自动恢复为空闲状态,不需要额外代码。面板则不同:它仍然显示已出售的防御塔,点击“升级”会访问一个已销毁的对象,并引发 MissingReferenceException。出售时不会选中任何对象,因此面板会隐藏。
挑战
中等以塔花费的一半价格出售它。
- 在
Tower中,保留private int spent;:在Start中记录data.cost,并在Upgrade()中记录每次升级的价格。添加public int SellValue => spent / 2;。 - 在
BuildManager中,面板填充时,在序列化的 TMP_TextsellLabel中显示Sell 25(出售价格)。 - 编写
public void SellSelected():返还出售价格,销毁这座塔,并取消选择。
按下 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();
}
}
}
Unity 塔防游戏中的所有课
自己练习: Unity 在线编辑器