[Unity 강의] 뱀서라이크 강의 - UI

업데이트:

카테고리:

태그: , ,




1. UI

✔ UI배치, 몬스터 처치 시 UI변경, 경험치 Slider 변경, 레벨업 시 Popup표시

UI작업 시 체크

✔ 클릭 문제 시 모든 UI Raycast Target 확인해 보기
✔ UI Sort Order 무언가 가려져있어서 클릭이 안되는지
✔ EventSystem 유 무 확인.
✔ UI 크기 조정 - UI Scale Mode - Scale With Screen Size
   화면크기 바꾸면서 확인해 보기. - 늘어나고 줄어들 때 잘 배치되도록 설계하기
✔ 코드랑 연동했을 때 무엇을 고쳐야 하나?
✔ UI 작업자의 의도 확인, 관찰하기
✔ 다국어 생각
✔ 텍스트 부분 - 코드에서 수정하게
✔ UI Prefab - Script 1:1 대응되도록 하나의 스크립트로 내부 ui 관리
✔ 항상 켜져 있는 UI, Popup UI 구분
✔ POP UP - Stack으로 키고 끄는 관리





2. UI 코드

✔ UI에서 게임 중 변화되는 부분 확인, 체크
✔ 컴포넌트 어떤 부분을 변경해야 바뀌는지 확인
✔ 코드에서 [SerializeField]로 가져오기
✔ 컴포넌트 설정 변경
✔ 버튼 - ONClick() 사용
✔ UI 갱신 - event 콜백으로
🔹UIManager UI_Base - UI 관리
🔹UI_GameScene - 게임 기본 정보들 (Gem, kill)
🔹UI_SkillSelectPopup - 레벨 업 시 팝업(다음강의)
🔹UI_SkillCardItem - 스킬정보

UIManager

✔ UI들을 보여주고, Popup 을 Stack을 이용해 관리
✅ ShowSceneUI - UI_GameScene(유지되는UI 캐릭터 정보) 를 Instantiate.
✅ ShowPopup - Popup UI 보여줌.
✅ ClosePopup - popup 끄기
✅ RefreshTimeScale() - popup 시 시간 멈춤, 다시 진행

UIManager
public class UIManager
{
    UI_Base _sceneUI;

    Stack<UI_Base> _uiStack = new Stack<UI_Base>();

    public T ShowSceneUI<T>() where T : UI_Base 
    {
        if (_sceneUI != null)
            return GetSceneUI<T>();

        string key = typeof(T).Name + ".prefab";
        T ui = Managers.Resource.Instantiate(key, pooling: true).GetOrAddComponent<T>();
        _sceneUI = ui;

        return ui;

    }

    public T GetSceneUI<T>() where T : UI_Base
    {
        return _sceneUI as T;
    }

    public T ShowPopup<T>() where T : UI_Base 
    {
        string key = typeof(T).Name + ".prefab";
        T ui = Managers.Resource.Instantiate(key, pooling: true).GetOrAddComponent<T>();
        _uiStack.Push(ui);
        RefreshTimeScale();

        return ui;
    }

    public void ClosePopup() 
    {
        if (_uiStack.Count == 0)
            return;

        UI_Base ui = _uiStack.Pop();
        Managers.Resource.Destroy(ui.gameObject);
        RefreshTimeScale();
    }

    public void RefreshTimeScale() 
    {
        if (_uiStack.Count > 0)
            Time.timeScale = 0;
        else
            Time.timeScale = 1;
    }
}

UI_GameScene

Image
✔ ui에서 게임 중 변화되는 부분 확인, 체크
✔ 킬 수, GEM Slider 변경
✔ [SerializeField] 사용

UI_GameScene
public class UI_GameScene : UI_Base
{
    [SerializeField]
    TextMeshProUGUI _killCountText;
    [SerializeField]
    Slider _gemSlider;

    public void SetGemCountRatio(float ratio) 
    {
        _gemSlider.value = ratio;
    }

    public void SetKillCount(int killCount) 
    {
        _killCountText.text = $"{killCount}";
    }
}





3. UI 갱신(Update)

✔ PlayerController에서 Gem 얻을 때 데이터 변경
✔ MonsterController에서 Ondead - Kill 수 변경
✔ UI 갱신 - event 콜백으로
🔹GameManager UI에 사용될 데이터 관리, Action, Set 콜백으로 변경될 때 실행 🔹GameScene - 이벤트 구독, 이벤트 발생 시 실행 함수(Handler)

GameManager

✔ gem, killCount관리, 변경 시 이벤트를 발생
✔ 프로퍼티 set 에서 이벤트 호출

GameManager
public class GameManager
{
    int _gem = 0;

    public event Action<int> OnGemCountChanged;
    public int Gem
    {
        get { return _gem; }
        set
        {
            _gem = value;
            OnGemCountChanged?.Invoke(value);
        }
    }
    #endregion

    #region 전투
    int _killCount;
    public event Action<int> OnkillCountChanged;

    public int KillCount
    {
        get { return _killCount; }
        set 
        {
            _killCount = value; OnkillCountChanged?.Invoke(value); 
        }
    }
    #endregion
}

GameScene

✔ UI_GameScenet 생성
✔ 킬 수, 젬 이벤트 구독
✔ 이벤트 발생 시 실행 함수(Handler)

GameScene
public class GameScene : MonoBehaviour
{

    void StartLoaded()
    {
        Managers.UI.ShowSceneUI<UI_GameScene>();

        Managers.Game.OnkillCountChanged -= HandleOnkillCountChanged;
        Managers.Game.OnkillCountChanged += HandleOnkillCountChanged;
        Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
        Managers.Game.OnGemCountChanged += HandleOnGemCountChanged;
    }

    int _collectedGemCount = 0;
    int _remainingTotalGemCount = 10;
    public void HandleOnGemCountChanged(int gemCount) 
    {
        _collectedGemCount++;

        if (_collectedGemCount == _remainingTotalGemCount)
        {
            Managers.UI.ShowPopup<UI_SkillSelectPopup>();
            _collectedGemCount = 0;
            _remainingTotalGemCount *= 2; 
        }

        Managers.UI.GetSceneUI<UI_GameScene>().SetGemCountRatio((float)_collectedGemCount / _remainingTotalGemCount);
    }

    public void HandleOnkillCountChanged(int killCount)
    {
        Managers.UI.GetSceneUI<UI_GameScene>().SetKillCount(killCount);

        if (killCount==5)
        {
            //보스? 컨텐츠
        }
    }
    private void OnDestroy()
    {
        if (Managers.Game != null)
        {
            Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
            Managers.Game.OnGemCountChanged -= HandleOnGemCountChanged;
        }
            
    }
}

Player, Monster

✔ 데이터 변경 시점
✅ PlayerController - CollectEnv() - Managers.Game.Gem += 1;
✅ MonsterController - OnDead() - Managers.Game.KillCount++;

P,M Controller
public class PlayerController : CreatureController
{
    void CollectEnv() 
    {
        float sqrCollectDis = EnvCollectDist * EnvCollectDist;

        var findGems = GameObject.Find("@Grid").GetComponent<GridController>().GatherObjects(transform.position, EnvCollectDist + 0.4f);

        foreach (var go in findGems)
        {
            GemController gem = go.GetComponent<GemController>();

            Vector3 dir = gem.transform.position - transform.position;
            if (dir.sqrMagnitude <= EnvCollectDist)
            {
                //✅ 
                Managers.Game.Gem += 1;
                Managers.Object.Despawn(gem);
            }   
        }
    }
}


public class MonsterController : CreatureController
{
    protected override void OnDead()
    {
        base.OnDead();

        //✅
        Managers.Game.KillCount++;

        if (_coDotDamage != null)
            StopCoroutine(_coDotDamage);
        _coDotDamage = null;

        //죽을 때 보석 스폰
        GemController gc = Managers.Object.Spawn<GemController>(transform.position);

        Managers.Object.Despawn(this);
    }
}





이것저것 메모

UI - Data

UI 코드에서 데이터를 넣지 말자. 데이터랑 UI 분리하기.
지금 프로젝트에선 GameManager에서 데이터를 관리하지만
UI에서 쓰는 데이터를 따로 클래스를 만들어서 관리할 수도 있다. UI-Data 1:1

popup시 시간정지

RefreshTimeScale()

UI갱신 흐름 정리

1. Player,MonsterController에서 GameManager의 데이터 변경 시도.
2. GameManager - 데이터의 set 부분의 구독된 이벤트들 호출
3. GameScene에서 미리 구독된 Handler의 실행
4. 데이터에 맞게 UI 변경





잡담, 일기?

큰 규모의 UI - 다른 방법, 실수를 줄이는 방법 알아보기.
다음 시간 - 레벨 업 팝업 수정




📔

댓글남기기