구상
터렛 업그레이드 기능을 추가하려 한다.
일단 StatusManager에 이벤트를 만들어서 버튼을 눌렀을 때 이벤트가 발생하도록 해놓았다.
처음에는 적을 만들 때와 같이 터렛의 프리팹에 업글 UI를 추가하려했으나,
인게임에 송출되었을 때 두 가지 문제점이 발생하였다.


1. UI가 화질이 깨지고 희미하게 보인다.
2. 터렛이 화면 가운데에 있을 때는 터렛의 바로 위에 UI가 뜨지만, 사이드에 있을 때는 터렛의 바로 위보다 살짝 옆에 있는 것 처럼 보인다.
1과 2의 이유는 동일하다.
프리팹 안에 캔버스가 있다는 것.
그래서 눙물을 머금고 (코드가 복잡해진다는 것을 알기에...) UI를 화면에 동적으로 띄우기로 했다.
하나의 슬라이더를 선택한 오브젝트의 위로 이동시킬지, 아니면 풀링으로 여러 개 만들어놓고 설치된 터렛에 연결시킬지를 굉장히 고민했는데, 챗지피티씨에게 물어보니 메모리 사용량을 줄이고 싶고 동시 업그레이드가 제한적이라면 전자가 더 좋을 것 같다고 했다.
현재 게임의 경우, 플레이어가 직접 특정 터렛에 다가가는 것이 선택의 조건이고, 업그레이드와 제거 버튼이 각각 하나씩 밖에 없어서 일부러 하나밖에 선택하지 못하도록 코드를 짜놓았기 때문에 1번이 좋을 것 같다고 생각했다.
UI를 원하는 월드 위치에 얹는 것은 다른 터렛 셀렉 화면에서 구현해보았기 때문에 크게 신경쓰지 않았다.
문제는 터렛에 저장해놓은 업그레이드 정보를 어떻게 UI에 전달한담...
일단 흐름은
TurretButtons에서 업그레이드 버튼 클릭 -> StatusManager의 이벤트 발생 -> TurretAI에서 레벨 가져감 -> UpgradeSlider에서 레벨에 따라 활성화시킬 슬라이더 정하여 시간에 따라 value 값 늘림 -> value 값이 1이 되었을 때 터렛 쿨타임 변경하고 TurretAI에게 알림(어떻게 알리실 건데요?) -> TurretAI에서 레벨 변경. -> 다시 UpgradeSlider에서 레벨에 따라 활성화시킬 슬라이더 정함. 레벨이 최대레벨보다 크거나 같으면 다 채워진 마지막 슬라이더를 보여주며 최대 레벨이라는 것을 알림.
이 될 것이다.
여러 시도를 해본 결과, 여러 문제가 발생했다.
일단 단순히 한 흐름으로 가는게 아니라 왔다 갔다를 하다보니 구조도 많이 복잡해지고 StatusManager에 너무 많은 이벤트를 추가하는 것 같았다.
그리고 무엇보다도 레벨과 슬라이더의 결합력이 너무 강해서 둘을 나누어 효율적인 코드를 짜려니 머리가 터질 것 같았다.
그래서 결국 슬라이더를 태그로 찾아서 레벨과 쿨타임, 그리고 슬라이더를 한 스크립트에서 관리하기로 했다.
처음부터 이렇게 하면 될 걸 왜 빙빙 돌아서 여기까지 왔는지 모르겠다.
Find 함수들을 너무 혐오해서 생긴 일인 것 같다.
분명 더 효율적인 방법들이 있겠지만, 일단 이 방법으로 구현하기로 했다.
여러 과정을 걸쳐 최종적으로 확정된 구조는,

이렇다. 전에 비해 아주 간단하쥬?
개발
1. 하이어라키의 캔버스에 업그레이드 슬라이더를 레벨마다 하나씩 만들어준다.
2. StatusManager에 이벤트를 추가해준다.
public event Action<TurretSelected> OnTurretUpgraded;
public void UpgradeSelectedTurret()
{
if (_selectedTurret)
{
OnTurretUpgraded?.Invoke(_selectedTurret);
}
}
}
참고로 _selectedTurret은 선택된 터렛 오브젝트 변수이다.
3. Turret Buttons에서 이 함수를 원하는 조건에 호출한다.
void UpgradeTurret()
{
StatusManager.Instance.UpgradeSelectedTurret();
transform.DOLocalMoveY(_originPos.y, 0.1f);
}
4. TurretAI에 하이어라키의 업그레이드 슬라이더를 찾는 코드를 작성한다.
private GameObject[] _lvObjects = new GameObject[3]; // 위치 이동때문에 Transform
private Slider[] _lvSliders = new Slider[3];
private Slider _lv1;
private Slider _lv2;
private Slider _lv3;
private Slider _currentSlider;
void Start()
{
_lvObjects = GameObject.FindGameObjectsWithTag("Level");
_lv1 = _lvObjects[0].GetComponent<Slider>();
_lv2 = _lvObjects[1].GetComponent<Slider>();
_lv3 = _lvObjects[2].GetComponent<Slider>();
_lvSliders[0] = _lv1;
_lvSliders[1] = _lv2;
_lvSliders[2] = _lv3;
}
5. TurretAI에 업그레이드 함수를 만든다.
public void ActivateSlider()
{
if (_currentLv >= 3)
{
// 레벨이 최대라는 것을 표시
}
_currentSlider = _lvSliders[_currentLv];
for (int i = 0; i < _lvSliders.Length; i++)
{
if (_lvSliders[i] == _currentSlider)
{
// 터렛 위로 오겠끔 위치
// 슬라이더 채우기 시작함
}
else
{
// 비활성화
}
}
}
6. 업그레이드 함수를 이벤트에 구독한다.
일 줄 알았는데, 생각해보니 StatusManager의 _selectedTurret은 TurretSelected 타입이었다. 즉 TurretSelected에 이벤트를 구독하고 업그레이드 함수를 호출시켜야 한다는 것이다.
6. 업그레이드 함수를 이벤트에 구독한다.
6. TurretSelected에 TurretAI의 컴포넌트를 가져와 업그레이드 함수를 호출하는 함수를 만들고, StatusManager의 이벤트를 구독한다.
private TurretAI _turretAI;
private void Start()
{
_turretAI = GetComponent<TurretAI>();
StatusManager.Instance.OnTurretDeleted += DeleteMe;
StatusManager.Instance.OnTurretUpgraded += Upgrade;
}
private void OnDisable()
{
StatusManager.Instance.OnTurretDeleted -= DeleteMe;
StatusManager.Instance.OnTurretUpgraded -= Upgrade;
}
...
void Upgrade(TurretSelected turret)
{
if (turret == this)
{
_turretAI.ActivateSlider();
}
}
끄읕
이렇게 보니 간단해보인다.
과정은 험난했는데ㅜㅜ. 그래도 얻어가는게 많아서 좋다!
결국 구현해냈다!!!
FindGameObjectsWithTag는 늦게 생성된 순으로 오브젝트를 찾는다.
'유니티' 카테고리의 다른 글
[유니티] Behaviour Tree 개념 정리 (0) | 2024.12.13 |
---|---|
[멋쟁이사자처럼 유니티 TIL] UI 슬라이더, 체력바 구현 (+ 델리게이트(delegate), 이벤트) (1) | 2024.06.11 |
[유니티 이론] 중요한 기능!! Deltatime 정의 및 사용법 (0) | 2024.01.23 |
[유니티 기초] 버튼 입력 방식으로 물체 이동하기 - GetButton, Transform (1) | 2024.01.23 |
[유니티 기초] Input 으로 키보드, 마우스 입력 기능 넣기 (1) | 2024.01.21 |