유니티 심화/복습

[복습] LearnUGUI - Test04[Stage]

다모아 2023. 9. 7. 01:03

이번거는 많이 어렵다고 느낀다..

학교에서 내 생각대로 했었던 코드들은 뭔가 회생 불가능할 것 처럼 보여서

강사님의 코드를 보고 써보고 이해해보기로 하였다.

그래도 뭔가 많이 어렵다..


Test04Stages

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class Test04Stage : MonoBehaviour
{
    public enum eState
    {
        Lock, Open, Complete
    }
    //텍스트
    [SerializeField]
    private TMP_Text[] arrTxtStageNum;
    //Lock, Open, Complete GameObject
    [SerializeField]
    private GameObject[] arrStateGo;
    //상태 저장
    [SerializeField]
    private eState state;
    //밖에서 상태 쓰려고
    public eState State
    {
        get
        {
            return this.state;
        }
    }
    //Stage Image 사용하려고
    private Button btn;
    //대리자 사용해서 상태 가져오려고
    public System.Action<int, eState> onClick;
    //스테이지 숫자
    private int stageNum;
    //스테이지 숫자 가져오려고
    public int StageNum
    {
        get
        {
            return this.stageNum;
        }
    }

    //stageNum과 state(상태)를 받아오는 Init
    public void Init(int stageNum, eState state)
    {
        //스테이지 숫자 받아와서 넣어주고
        this.stageNum = stageNum;
        Debug.Log(stageNum);
        //버튼 할당 받아주고
        this.btn = this.GetComponent<Button>();
        // 텍스트에 스테이지 숫자 넣어준다
        foreach(TMP_Text tmpText in this.arrTxtStageNum)
        {
            tmpText.text = stageNum.ToString();
        }

        //이미지를 클릭하면 대리자 실행
        this.btn.onClick.AddListener(() => {
            this.onClick(this.stageNum, this.state);
        });
        
        this.state = state;
        this.ChangeState(this.state);

        this.gameObject.SetActive(true);
    }

    public void ChangeState(eState state)
    {
        //모두 비활성화
        this.InActiveAll();
        //상태 받아주고
        this.state = state;
        //index에 그 상태 숫자값 넣기
        int index = (int)this.state;
        //그 상태의 숫자값 받은 애만 활성화 시켜주기
        this.arrStateGo[index].SetActive(true);
    }

    //모두 비활성화
    private void InActiveAll()
    {
        //하나씩 다 비활성화
        foreach(GameObject go in this.arrStateGo)
        {
            go.SetActive(false);
        }
    }

    public void Show()
    {
        this.gameObject.SetActive(true);
    }

    public void Hide()
    {
        this.gameObject.SetActive(false);
    }

    //UI에 업데이트하기 
    public void UpdateUI(int stageNum, eState state)
    {
        this.stageNum = stageNum;
        this.state = state;

        foreach(TMP_Text tmpText in this.arrTxtStageNum)
        {
            tmpText.text = stageNum.ToString();
        }

        this.ChangeState(this.state);
        this.gameObject.SetActive(true);
    }
}

 

Test04UIStageController

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class Test04UIStageController : MonoBehaviour
{

    [SerializeField]
    private Test04Stage[] stages;

    private Dictionary<int, Test04Stage.eState> dic = new Dictionary<int, Test04Stage.eState>();

    private int totalStages;

    public event UnityAction onMoveNextEvent;
    private Test04UIPageStage.Page page;

    public void Init(int totalStages, Test04UIPageStage.Page page)
    {
        this.page = page;

        Debug.LogFormat("[Init] {0} - {1}, total : {2}", this.page.start, this.page.end, this.page.Total);

        this.totalStages = totalStages;

        for(int i = 0; i < this.totalStages; i++)
        {
            var stageNum = i + 1;
            if(stageNum == 1)
            {
                dic.Add(stageNum, Test04Stage.eState.Open);
            }
            else
            {
                dic.Add(stageNum, Test04Stage.eState.Lock);
            }
        }
        this.Clear();

        //이벤트 등록
        for (int i = 0; i < this.page.Total; i++)
        {
            var uiStage = this.stages[i];
            int idx = i;
            uiStage.onClick = (stageNum, state) =>
            {
                Debug.LogFormat("stageNum: {0}, state: {1}", stageNum, state);

                if (state != Test04Stage.eState.Open) return;

                this.CompleteStage(uiStage);
                int nextStageNum = stageNum + 1;
                if (nextStageNum <= this.totalStages)
                {
                    Debug.LogFormat("{0} ~ {1}, total : {2}", this.page.start, this.page.end, this.page.Total);

                    Debug.LogFormat("다음 스테이지 오픈 : {0}, page.end: {1}", nextStageNum, this.page.end);
                    //다음 Test04Stage가 현재 페이지에 있는지 확인
                    if (nextStageNum > this.page.end)
                    {
                        Debug.Log("다음 스테이지가 현재 페이지에 없음");
                        //데이터만 변경
                        this.dic[nextStageNum] = Test04Stage.eState.Open;
                        //다음 페이지로 이동 버튼 눌렀을 때 로직 실행
                        this.onMoveNextEvent();
                    }
                    else
                    {
                        Debug.Log("다음 스테이지가 현재 페이지에 있음");
                        Test04Stage nextUIStage = this.stages[idx + 1];
                        Debug.LogFormat("nextUIStage: {0}", nextUIStage);
                        //데이터를 변경하고
                        this.dic[nextStageNum] = Test04Stage.eState.Open;
                        //UI를 업데이트 함
                        nextUIStage.ChangeState(Test04Stage.eState.Open);
                        //모든 스테이지들의 상태를 출력
                        for (int i = 0; i < this.totalStages; i++)
                        {
                            var num = i + 1;
                            Debug.LogFormat("{0} : {1}", num, this.dic[num]);
                        }
                    }
                }
                else
                {
                    Debug.Log("마지막 스테이지");
                }
            };
            int stageNum = page.start + i;
            uiStage.Init(stageNum, this.dic[stageNum]);
        }
    }
    
    private void CompleteStage(Test04Stage uiStage)
    {
        if(uiStage.State == Test04Stage.eState.Open)
        {
            this.dic[uiStage.StageNum] = Test04Stage.eState.Complete;
            //해당 스테이지 찾아 업데이트 하기
            uiStage.ChangeState(Test04Stage.eState.Complete);
        }
    }

    public void UpdateUI(Test04UIPageStage.Page page)
    {
        this.page = page;

        Debug.LogFormat("[UpdateUI] {0} ~ {1}, total : {2}", this.page.start, this.page.end, this.page.Total);

        for (int i = 0; i < this.totalStages; i++)
        {
            var stageNum = i + 1;
            Debug.LogFormat("{0} : {1}", stageNum, this.dic[stageNum]);
        }

        this.Clear();

        for (int i = 0; i < page.Total; i++)
        {
            var uiStage = this.stages[i];
            int stageNum = page.start + i;
            uiStage.UpdateUI(stageNum, this.dic[stageNum]);
        }
    }

    private void Clear()
    {
        foreach (Test04Stage uiStage in this.stages)
        {
            uiStage.Hide();
        }
    }
}

 

Test04UIPageStage

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Test04UIPageStage : MonoBehaviour
{
    public struct Page
    {
        public int start;
        public int end;
        public int Total
        {
            get
            {
                return this.end - this.start + 1;
            }
        }
        public Page(int start, int end)
        {
            this.start = start;
            this.end = end;
        }
    }
    private const int MAX_DISPLAY_STAGES = 18; //한 페이지에 보여줄 수 있는 최대 스테이지 갯수

    [SerializeField]
    private Button btnPrev;
    [SerializeField]
    private Button btnNext;
    [SerializeField]
    private int totalStages = 28;
    [SerializeField]
    private Test04UIStageController stageController;

    private int currentPage = 1;
    private int totalPage = -1;

    public void Init()
    {
        //계산하기
        this.totalPage = Mathf.CeilToInt((float)this.totalStages / MAX_DISPLAY_STAGES);

        Page page = this.CalcStartEnd();

        this.stageController.onMoveNextEvent += OnMoveNextEventHandler;
        this.stageController.Init(totalStages, page);

        this.btnNext.onClick.AddListener(() => {
            this.MoveNext();
        });

        this.btnPrev.onClick.AddListener(() => {
            this.MovePrev();
        });
    }

    private void OnMoveNextEventHandler()
    {
        this.MoveNext();
    }

    private void OnDisable()
    {
        this.stageController.onMoveNextEvent -= OnMoveNextEventHandler;
    }

    private void MovePrev()
    {
        if (this.currentPage == 1)
        {
            Debug.Log("첫 페이지 입니다.");
            return;
        }
        this.currentPage -= 1;
        Debug.LogFormat("currentPage: {0}", this.currentPage);
        Page page = this.CalcStartEnd();
        this.stageController.UpdateUI(page); //초기화
    }

    private void MoveNext()
    {
        if (this.currentPage == this.totalPage)
        {
            Debug.Log("마지막 페이지 입니다.");
            return;
        }

        this.currentPage += 1;
        Debug.LogFormat("currentPage: {0}", this.currentPage);
        Page page = this.CalcStartEnd();

        Debug.LogFormat("<color=lime>{0} ~ {1}, Total: {2}</color>", page.start, page.end, page.Total);

        this.stageController.UpdateUI(page); //초기화
    }

    private Page CalcStartEnd()
    {
        Debug.LogFormat("총 스테이지 : {0}", this.totalStages);
        Debug.LogFormat("마지막 페이지 : {0}", this.totalPage);
        Debug.LogFormat("현재 페이지 : {0}", this.currentPage);
        int end = this.currentPage * MAX_DISPLAY_STAGES;
        int start = end - (MAX_DISPLAY_STAGES - 1);

        //마지막 스테이지가 총 스테이지보다 같거나 클 경우
        if(end >= this.totalStages)
        {
            end = this.totalStages;
        }

        return new Page(start, end);
    }
}

 

Test04UIMain

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test04UIMain : MonoBehaviour
{
    [SerializeField]
    private Test04UIPageStage uiPageStage;

    // Start is called before the first frame update
    void Start()
    {
        this.uiPageStage.Init();    
    }
}