Unity 내일배움캠프 TIL 0921 | SingletonManager로 코드 리팩토링하기 | Scene 이동 이벤트 | 마우스 포인터 Enter/Exit Event | Canvas Group과 애니메이션

cheeseonrose·2023년 9월 21일
0

Unity 내일배움캠프

목록 보기
41/89
post-thumbnail

야근에 야근에 야근을 더해서

ㅇ<-<
이미 쓰러진 감자입니다

개인 프로젝트

SingletonManager

  • 개쩌는 코드 리팩토링의 시작
    기능 구현하면서 자꾸 코드를 더 깔끔하게 만들고 싶은 욕심이 나서 결국 코드 리팩토링하는데 시간을 한참 썼다.

  • 우선 저번 팀 과제에서 시간 부족으로 시도하지 못했던 UI Manager를 하나만 만들어서 게임 전체 UI를 관리하게 하는 것

    • 이를 위해서 저번 과제에서의 UIManager 코드에 Dictionary에서 UI Component를 제거하는 메서드를 추가했다.
    • 그냥 string으로 받아와도 되긴하는데 뭔가..GetUIComponent 메서드랑 통일성을 주고 싶어서 제너릭으로 받아왔다. 아무래도 깔맞춤 못참으니까 ㅇㅅaㅇ
    • 이 함수는 Scene을 이동할 때 원래 Scene의 UI 컴포넌트들을 Dictionary에서 삭제하는 역할이다.
      삭제하는 이유는 다시 해당 Scene으로 돌아왔을 때 Dictionary에 UI 컴포넌트가 없어야 Instantiate를 하기 때문
      만약 삭제하지 않고 넘어갔다가 다시 돌아온다면 이미 Dictionary 안에는 UI 컴포넌트가 존재하기 때문에 프리팹을 생성하지 않을 것이다.
    public void RemoveUIComponent<T>(T uiComponent) where T : GameUIClass
      {
          if (uiDic.ContainsKey(typeof(T).Name))
          {
              uiDic.Remove(typeof(T).Name);
          }
      }
  • 그리고 UI Manager를 하나로 만든 것에서 나아가 모든 Manager 클래스들을 처음 Scene에서 만들고 DontDestroyOnLoad에 담아서 Scene을 이동하면서 전달하는 것!

    • 이를 위해서 SingletonManager라는 클래스를 새로 만들었다. trigger 같은 역할이라고 생각하면 될듯

    • UI Manager와 비슷하게 Dictionary에 Manager 클래스들을 담아서 관리한다. 이것도 Monobehaviour가 아니라 전체 Manager 클래스가 상속받는 부모 클래스를 value 값으로 들고 있게 바꿀 수 있을 듯..?!

    • 처음에 Manager Instance들이 Dictionary에 있는지 확인하고, 없다면 생성해준다.

      public class SingletonManager : MonoBehaviour
      {
        public static SingletonManager Instance;
      
        Dictionary<string, MonoBehaviour> managerDic;
      
        private void Awake()
        {
            if (Instance == null)
            {
                Instance = this;
            }
            else if (Instance != this)
            {
                Destroy(gameObject);
                return;
            }
      
            DontDestroyOnLoad(gameObject);
      
            // 닉네임 입력부터 하고 싶을 때 PlayerDatabase 지우고 주석 풀기
            //PlayerPrefs.DeleteAll();
      
            managerDic = new Dictionary<string, MonoBehaviour>();
      
            InitManagerInstance<DataManager>();
            InitManagerInstance<UIManager>();
            InitManagerInstance<GameManager>();
            InitManagerInstance<SoundManager>();
        }
      
        public void InitManagerInstance<T>() where T : MonoBehaviour
        {
            string key = typeof(T).Name;
            if (!managerDic.ContainsKey(key))
            {
                var obj = new GameObject();
                obj.name = key;
                managerDic.Add(key, obj.AddComponent<T>());
            }
        }
      }
    • 이렇게 SingletonManager만 StartScene에 미리 생성해놓으면 Hierarchy 창은 아래와 같이 깔꼼해진다.

    • 그리고 게임을 시작하면 이렇게 Manager 오브젝트들이 DontDestroyOnLoad에 담겨서 좌르륵 생성된다.

  • 이렇게 마참내 전체 게임에서 기능별 Manager가 각각 하나씩만 있는 구조 완성~,,!


SceneManager.activeSceneChanged

  • SingletonManager를 만든 뒤에 생긴 고민은 GameManager에서 어떻게 Scene 변경을 감지할 것이냐!의 문제
    원래는 Scene마다 각 SceneManager가 있었는데, 이번에는 GameManager 하나로 통합해버렸기 때문에 Scene 이동을 감지할 방법이 필요했다.
  • 처음에 시도했던 것은 그냥 LoadScene 이후에 새 Scene에서 필요한 UI 컴포넌트들을 가져오는 것이었는데, Scene 이동보다 UI 컴포넌트를 가져오는 동작이 먼저 완료되어서 이동한 Scene에는 아무것도 나타나지 않게 되었다.
  • 진짜 우연하게 SceneManager. 쳐놓고 뒤적거리다가 activeSceneChanged 를 발견하게 되었다. 이름에서부터 이거다 싶은 냄새가 솔솔
    구글링해보니 역시 Scene 변경과 관련된 이벤트였다!
  • 아래와 같이 새로운 Scene에 진입했을 때 UI 컴포넌트를 다시 불러오도록 함수를 만들어주고, 이벤트에 연결해줬다.
private void Awake()
{
	...
    
	SceneManager.activeSceneChanged += OnActiveSceneChanged;
}

public void OnActiveSceneChanged(Scene scene1, Scene scene2)
{
	InitUIComponent();
}
  • InitUIComponent에서는 현재 Scene의 State에 따라 다른 UI 컴포넌트를 생성해준다.
    • Scene의 state 값은 GameManager의 private 변수로 관리되며, Scene을 이동하기 전에 새로운 Scene의 state로 갱신해준다.
private void InitUIComponent()
{
	switch (_curState)
    {
    	case SceneStateType.START:
        	InitStartSceneComponent();
            break;
        case SceneStateType.MAIN:
        	InitMainSceneComponent();
            break;
    }        
}

Event Trigger - Pointer Enter/Exit

  • 인벤토리 아이템 위에 마우스 커서를 올리면 해당 아이템의 이름과 효과가 미리 보이는 창을 띄우게 하고 싶었다.
  • 그래서 찾아본 것이 오브젝트에 컴포넌트로 Event Trigger를 추가하고, Eneter/Exit 함수를 만들어 연결해줬다.
public void OnPointerEnter()
{
	_itemNameText.text = _item.Name;
    
    string effectStr = "";
    switch (_item.Type)
    {
    	case ItemType.HEALTH:
        	effectStr = "피로도 -";
            break;
        case ItemType.ATTACK:
        	effectStr = "공격력 +";
            break;
        case ItemType.SHIELD:
        	effectStr = "방어력 +";
        	break;
    }
    _itemEffectText.text = effectStr + _item.Value;
    _itemDescriptionImage.SetActive(true);
    _animator.SetBool("IsOpen", true);
}

public void OnPointerExit()
{
	_animator.SetBool("IsOpen", false);
    _itemDescriptionImage.SetActive(false);
}
  • 그 그런데 문제가 생김
    아이템 컨테이너 안에 UI를 추가했더니 스크롤뷰 때문에 가장자리에서는 설명창이 잘려서 나오게 되는 것
    일단 이건 스크롤뷰 크기와 content 위치를 조정해서 해결했으나 사실 완전히 해결된 것 같지는 않다.
    그..근데 문제가 또 있는데

    ?
    ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
    아아아아악!!!!!

Canvas Group

  • 어제 야근 동료가 알려준 유용한 사실! Canvas Group을 컴포넌트로 추가하면 UI 오브젝트의 하위 오브젝트들까지 한 번에 컨트롤해서 애니메이션을 만들 수 있다는 사실
  • 한 번에 컨트롤 가능한 기능이 제한적인 것 같지만 꽤나 유용하게 써먹었다.
    바로 위의 저 설명창에서.......썼는데......
    아무래도 설명창 위치를 옮겨야겠다...



사실 UI 꾸미기 욕심만 없었으면 진작 끝냈겠지만 UI 꾸미는거 어떻게 참음?
여기 와서 유니티보다 포토샵을 더 만지는 것 같지만 기분 탓이겠지 응

열심히 만든 UI는 다음 TIL에서 공개...
커 밍 쑨 !

야근이 복사가 되는 이벤트
이번주 내내 야근을 누려보세요!

히히...밤새.....죽어보자고..........

끗~

0개의 댓글