Menu
CoddyTech

売却

Coddyの「Unityゲーム:タワーディフェンス」コースのレッスン 13/18。

タワーを売却すると、アップグレード分も含めて購入費用の半額が戻ってくるため、防衛施設を移動するにはコストがかかりますが、全額ではありません。タワーは、建設時の価格に、支払いごとの各アップグレード価格を加えた、これまでに使った金額の合計を保持します。払い戻し額はその合計の半分で、整数除算によって端数は切り捨てられます。自転車のレンタルでも、同じような合計額を保持します。

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

public int Deposit => paid / 4;

売却するとタワーは破棄されますが、パッドのtowerフィールドは引き続きそのタワーを指しています。Unityの==は破棄されたオブジェクトをnullとして扱うため、次のフレームからpad.tower == nullはtrueとなり、追加のコードなしでパッドが再び空きます。パネルは別の問題です。売却したタワーを表示したままで、アップグレードをクリックすると破棄されたオブジェクトにアクセスし、MissingReferenceExceptionが発生します。売却すると何も選択されないため、パネルは非表示になります。

challenge icon

チャレンジ

中級
  1. Towerで、private int spentを保持します。これは、Startで設定するタワーのcostに、購入したアップグレードの価格を加えたものです。public int SellValue => spent / 2;を追加します。
  2. BuildManagerで、シリアライズされたTMP_Text sellLabelに、選択中のタワーの売却価格であるSell 25を、パネルの内容を設定するたびに表示します。
  3. Sellボタンから呼び出される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ゲーム:タワーディフェンスのすべてのレッスン