업그레이드
Coddy의 Unity 게임: 타워 디펜스 코스 레슨. 18개 중 12번째.
타워를 클릭하면 레벨과 업그레이드 버튼이 있는 패널이 열립니다. 레벨이 올라갈 때마다 타워의 공격력과 사거리가 증가하고, 업그레이드 비용은 이전보다 더 많이 듭니다. 레벨은 규칙을 따르므로 에셋에는 숫자 목록이 아니라 규칙이 저장됩니다. 즉, 레벨 1의 능력치, 레벨마다 추가되는 수치, 업그레이드 비용이 저장됩니다. ScriptableObject의 메서드가 나머지를 계산하므로, 어떤 스크립트가 요청하든 같은 결과를 얻습니다. 농장 게임의 작물 수확량 예시:
public int YieldAt(int level)
{
return baseYield + yieldPerLevel * (level - 1);
}타워는 자체 level만 유지하고, 패널에는 현재 레벨의 수치 옆에 다음 레벨의 수치가 표시되므로 플레이어는 돈을 쓰면 무엇을 얻는지 알 수 있습니다. 타워를 클릭하면 타워의 자식인 부분, 즉 본체나 머리에 맞습니다. 따라서 hit.collider.GetComponentInParent<Tower>()는 해당 부분이 속한 타워를 찾습니다. 맞은 오브젝트에서 시작해 부모 오브젝트를 차례로 확인합니다.
패널은 3D 뷰 위에 표시되는 UI이며, 업그레이드 버튼을 클릭하는 것도 BuildManager가 감지하는 마우스 누름입니다. 레이는 버튼을 통과해 뒤에 있는 지면에 닿고, BuildManager는 버튼에서 손을 떼기 전에 패널을 닫습니다. 포인터가 UI 위에 있는 동안 EventSystem.current.IsPointerOverGameObject()는 true이므로 BuildManager는 일찍 반환하고 해당 클릭을 버튼에 맡깁니다.
챌린지
중급TowerData에 public intmaxLevel,upgradeCost,damagePerLevel과 public floatrangePerLevel을 추가하세요.int DamageAt(int level)와float RangeAt(int level)을 추가하세요. 레벨 1에서는 에셋 자체의 피해량과 사거리를 사용하고, 그보다 높은 각 레벨에서는 레벨당 증가량을 더합니다. 해당 레벨에서 다음 레벨로 올라가는 비용인int UpgradeCost(int level)도 추가하세요. 비용은upgradeCost에 레벨을 곱한 값입니다.Tower에 1에서 시작하는 private intlevel을 두고,public int Level => level;로 읽을 수 있게 하세요.DamageAt(level)으로 발사하고RangeAt(level)범위 안의 대상을 지정하세요. 레벨을 1 올리는public void Upgrade()를 추가하세요.BuildManager에서 타워를 클릭하면 선택하고, 다른 것을 클릭하면 아무것도 선택되지 않게 하세요(빈 건설 패드를 클릭하면 여전히 그 위에 건설됩니다). 직렬화된towerPanel은 타워를 선택한 동안에만 표시되며,towerNameText에는Arrow level 1,towerStatsText에는 두 줄로Damage 2 > 3과Range 4.5 > 5,upgradeLabel에는Upgrade 40을 표시합니다.maxLevel에서는 현재 값만 표시하고, 레이블에는Max level이라고 표시하며upgradeButton은 상호 작용할 수 없게 하세요.- 업그레이드 버튼이 호출하는
public void UpgradeSelected()를 작성하세요. 최대 레벨보다 낮고 GameManager가 업그레이드 비용을 지불할 수 있으면 타워를 업그레이드하고 패널을 새로 고치세요.Update에서 UI 위를 클릭한 경우는 무시하세요.
Play를 눌러 타워를 건설하고, 타워를 클릭해 업그레이드한 다음 Check를 누르세요.
직접 해보기
using UnityEngine;
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);
private Tower chosen;
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]);
}
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)
{
return;
}
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out RaycastHit hit) && 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);
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.