2022. 2. 11. 17:54ㆍUnity3D/수업 내용
2월 9일 포스팅에서 이어지는 내용입니다.
이전 포스팅에서 스테이지 버튼을 클릭했을 때 스테이지에 대한 정보가
나오는 팝업창을 구현했다.
오늘은 잠긴 스테이지의 팝업창을 추가하고,
상단에 재화를 표시하고,
스테이지 목록을 다음 페이지로 넘기는 기능을 구현할 것이다.
스테이지가 잠긴 상태의 팝업창 구현하기
이전에 만들어둔 팝업창을 띄우는 메서드를 보면,
'스테이지가 잠겼을 때'는 아무 행동도 하지 않고 함수를 종료하게 만들어 두었다.
이 '스테이지가 잠겼을 때'에 오늘 만들 팝업창을 띄울 코드를 작성해보자.
- 각 버튼은 자신의 스크립트 내에 UI오브젝트(만든 팝업창) 하나를 갖고 있다.
- UI오브젝트 또한 자신의 UI요소를 다룰 스크립트 하나를 갖고 있다.
- '스테이지가 잠겼을 때' 조건문 안에다가 UI오브젝트의 스크립트로 접근해서 팝업창의 스테이지 네임 텍스트와 입장 최소 레벨 텍스트를 수정하고 UI오브젝트를 활성화시킨다.
- 마지막으로 닫는 버튼까지 구현해주면 끝.
실행 결과
+
사용자 레벨에 따라 스테이지 슬롯이 해금되게 만들기
이전에는 이전 스테이지를 완료했을 때 다음 스테이지가 해금되는 방식으로 했지만,
방식을 바꾸어 사용자 레벨에 따라 스테이지가 열리도록 했다.
public void OpenLock()
{
if(this.id == 100)
{
this.isLocked = false;
this.lockIcon.gameObject.SetActive(false);
this.gameObject.GetComponent<Image>().sprite = slotFrame[0];
}
else
{
Dictionary<int, StageData> dict = DataManager.GetInstance().GetDictStageData();
StageData data = dict[this.id];
if (data.requireLevel <= DataManager.GetInstance().GetHeroInfo().level)
{
this.isLocked = false;
this.lockIcon.gameObject.SetActive(false);
this.gameObject.GetComponent<Image>().sprite = slotFrame[0];
}
List<StageInfo> list = DataManager.GetInstance().GetListStageInfo();
foreach (StageInfo info in list)
{
if (info.id == id)
{
this.gameObject.GetComponent<Image>().sprite = slotFrame[1];
}
}
}
}
- 슬롯들을 해금하는 메서드인 OpenLock()의 조건문과 코드를 수정하고 몇 줄 추가한다.
- 레벨업을 시켜줄 버튼을 만들고 시험해본다.
레벨에 따라 해금시킨다.
상단 메뉴에 재화 표시하기
지금까진 장식용으로 상단에 UI를 배치했지만,
이번 단계에서 실제 기능하는 UI로 만들 것이다.
골드와 보석은 GameInfo에서 불러오고,
하트는 지속적으로 시간을 측정해 일정 시간마다 하트를 1 회복시킬 것이다.
상단 우측의 별 개수 또한 StageInfo에서 보유한 모든 별을 합산해 표기해줄 것이다.
- 별 개수를 표기할 텍스트를 스크립트 상에 올려놓고, 호출될 때마다 StageInfo를 참조하여 별의 개수를 합산하고 텍스트를 수정할 것이다.
using UnityEngine.UI; public class UIStageControl : MonoBehaviour { [SerializeField] Text starAmountText; public void UpdateStarAmount() { int a = 0; foreach(StageInfo info in DataManager.GetInstance().GetListStageInfo()) { a += info.star; } starAmountText.text = a + ""; } }
- 골드와 보석 또한 1번과 같은 방법으로 UI 텍스트를 수정할 것이다.
using UnityEngine.UI; public class UIStageControl : MonoBehaviour { [SerializeField] Text starAmountText; [SerializeField] Text goldText; [SerializeField] Text gemText; public void UpdateGold() { int g = DataManager.GetInstance().GetGold(); if(g <= 0) { goldText.text = "0"; return; } goldText.text = string.Format("{0:#,###}", g); // 이 코드는 정규표현식으로 숫자 1000 단위마다 쉼표를 찍어준다. } public void UpdateGem() { int g = DataManager.GetInstance().GetGem(); if (g <= 0) { gemText.text = "0"; return; } gemText.text = string.Format("{0:#,###}", g); } public void UpdateStarAmount() { int a = 0; foreach(StageInfo info in DataManager.GetInstance().GetListStageInfo()) { a += info.star; } starAmountText.text = a + ""; } }
- 하트는 시작 직후 GameInfo에서 개수를 가져오고, 그 이후로는 시간이 지남에 따라 서서히 증가하게 된다.
using UnityEngine.UI; public class UIStageControl : MonoBehaviour { [SerializeField] Text starAmountText; [SerializeField] Text goldText; [SerializeField] Text gemText; [SerializeField] Text heartText; [SerializeField] Text heartGenTimerText; int min = 0; float sec = 5f; void Start() { UpdateGold(); UpdateGem(); UpdateHeart(); } void Update() { sec -= Time.deltaTime; heartGenTimerText.text = string.Format("{0:D2}:{1:D2}", min, (int)sec); //이 코드 또한 정규표현식으로 디지털 시계 같은 표기를 해준다. (29:59) if(sec <= 0f) { if(min <= 0) { min = 5; sec = 0.9f; DataManager.GetInstance().IncreaseHeart(); UpdateHeart(); } else { sec = 59.9f; min--; } } } public void UpdateHeart() { int h = DataManager.GetInstance().GetHeart(); if(h <= 0) { heartText.text = "0"; return; } heartText.text = h + ""; } public void UpdateGold() { int g = DataManager.GetInstance().GetGold(); if(g <= 0) { goldText.text = "0"; return; } goldText.text = string.Format("{0:#,###}", g); } public void UpdateGem() { int g = DataManager.GetInstance().GetGem(); if (g <= 0) { gemText.text = "0"; return; } gemText.text = string.Format("{0:#,###}", g); } public void UpdateStarAmount() { int a = 0; foreach(StageInfo info in DataManager.GetInstance().GetListStageInfo()) { a += info.star; } starAmountText.text = a + ""; } }
중간 결과
다음 페이지로 넘기는 기능 구현하기
일단 실행결과
운이 좋게도 기존 코드의 작동 방식이
다음 페이지로 넘기는 기능의 코드와 호환이 잘 되어서 금방 됐다.
원리는 페이지를 나타내는 int형 변수 하나를 갖고 있고,
좌, 우로 넘기기 버튼이 눌릴 때마다 페이지 변수를 증감하거나 차감한다.
그 페이지에 따라서 반복문의 시작값과 끝나는 값이 달라진다.
(예를 들어 현재 2페이지이면 반복문의 시작 값은 18, 끝 값은 36이고,
1페이지이면 0, 18이다.)
이 반복문의 매개 변수는 슬롯에게 아이디와 인덱스를 부여해준다.
그래서 페이지를 넘길 때마다 그 페이지에 따른 스테이지 넘버를 가진 슬롯이 보여진다.
나머지는 그저 UI작업이라 설명은 생략하겠다.
그리고 한 가지 문제를 만났었는데
페이지를 넘길 때마다 슬롯이 새로 생성되는데
이 슬롯들은 자신이 눌렸을 때 띄울 팝업창의 정보를 수정하기 위해
생성될 때마다 팝업창 게임오브젝트를 참조하는 코드를 실행한다.
그런데 페이지를 넘기고 나서 새로 생성된 슬롯들이
팝업창 오브젝트를 참조하려고보니 첫번째 슬롯들이 팝업창을 참조한 다음 비활성화 시켜버려서
두번째 세대 슬롯부터는 참조할 대상이 없는 것이다.
그래서 난 비활성화 상태인 게임오브젝트를 찾는 법을 검색했고,
답은 이러하였다.
https://artiper.tistory.com/114
[Unity] 비활성화 된 GameObject찾기
문제점 GameObject가 비활성화 상태였는데. Find함수로 찾으려니 안찾아졌다. 해결방법 비활성화 된 객체를 활성화시키려면, 활성화된 부모를 찾아서 자식을 찾는 형식으로 접근해야 합니다. 참고
artiper.tistory.com
밑은 소스 코드.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIStageBoxControl : MonoBehaviour
{
[SerializeField] GameObject grid;
[SerializeField] List<UIStageSlotControl> slots;
[SerializeField] GameObject slotPrefab;
[SerializeField] UIPageControl[] pages;
[SerializeField] Button btnLeftPage;
[SerializeField] Button btnRightPage;
int pg = 0;
void Start()
{
LoadItems();
ChangeYellowPos();
btnLeftPage.onClick.AddListener(() => {
for (int h = 0; h < slots.Count; h++)
{ // instantiate로 슬롯을 만들기 때문에 이렇게 지워주지 않으면 내부적으로 계속 쌓이고 쌓인다.
Destroy(slots[h].gameObject);
slots.Remove(slots[h]);
h--;
}
pg--;
if (pg < 0)
{
pg = 1;
}
LoadItems();
ChangeYellowPos();
});
btnRightPage.onClick.AddListener(() =>
{
for(int h = 0; h < slots.Count; h++)
{
Destroy(slots[h].gameObject);
slots.Remove(slots[h]);
h--;
}
pg++;
if(pg > 1)
{
pg = 0;
}
LoadItems();
ChangeYellowPos();
});
}
void ChangeYellowPos()
{
foreach (UIPageControl page in pages)
{
page.goYellow.SetActive(false);
}
pages[pg].goYellow.SetActive(true);
}
void LoadItems()
{
Dictionary<int, StageData> dict = DataManager.GetInstance().GetDictStageData();
int ed = 18 + (pg * 18); //페이지에 따라서 반복문의 시작값과 끝 값을 결정
if (ed >= dict.Count)
{
ed = dict.Count;
}
for (int i = 0 + (pg * 18); i < ed; i++)
{
GameObject go = Instantiate(slotPrefab);
go.transform.SetParent(grid.transform);
UIStageSlotControl slot = go.GetComponent<UIStageSlotControl>();
slot.InitStageNum(i + 1);
slots.Add(slot);
}
foreach (UIStageSlotControl slot2 in slots)
{
Debug.Log(slot2.id);
}
}
public void ShowSlots()
{
foreach(UIStageSlotControl slot in slots)
{
slot.OpenLock();
slot.MarkStar();
}
}
void OpenOrLock()
{
List<StageInfo> list = DataManager.GetInstance().GetListStageInfo();
if (list == null)
{
slots[0].OpenLock();
return;
}
int index = list[list.Count - 1].id - 100;
slots[index].OpenLock();
}
}
'Unity3D > 수업 내용' 카테고리의 다른 글
22.02.15 업적 창 구현하기 (0) | 2022.02.15 |
---|---|
22.02.14 Shop 메뉴 구현하기 (0) | 2022.02.14 |
22.02.04 JSON 데이터를 UI에 바인딩하기 (0) | 2022.02.05 |
22.02.03 Canvas와 UI 배우기 (0) | 2022.02.03 |
22.01.27 런타임마다 외부의 데이터로 갱신하는 법 (0) | 2022.01.27 |