
저번에 이어 계속 작업하지만 살짝 바뀐 부분이있다.

기존 구조에서 컴포저를 추가하고 생성과 애니메이션을 조합해서 명령을 실행시키게 만들었다 또한 컴포저에서 작업순서를 넣은 순서대로 실행시키게 만들었다.
이제 마저 전에작업하던걸 이어서 해보자
버튼을 누르면 명령을 보내야하기때문에 받아줄 클래스가 필요하다. 즉 핸들러가 필요하기때문에 간단하게 만들어주자
사실.. 핸들러는 1분이면 만들수있다 진짜 단순하게 짜면 다음과 같다
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIHandler : MonoBehaviour
{
[SerializeField] string call_popupID;
public void OnShowButtonClicked()
{
//Debug.Log("팝업생성 명령");
UImanager.manager.ShowPanel_popup_info(call_popupID);
}
public void OnHideButtonClicked()
{
//Debug.Log("뒤로가기 명령");
UImanager.manager.HidePanel_popup_info();
}
}
복잡할 일도없다 솔직히 여기서 복잡한작업을 하면 객체화 의미가없겠지..
진짜 간단하다 원리는 매니저 접근자를 통해 매니저에있는 함수를 작동시키는 게 끝이다 ID가 필요하면 인스펙터 변수로 끌고오거나 아니면 DTO를 통해 넣는등 방법은 많다. 결과적으로 그냥 함수를 실행시키면 매니저에 명령을 전달하는역할이다.
물론 연결을 해야 작동을 할수있다 연결방법은 다음과 같다

작업을 시킬 버튼을 선택하고 핸들러를 넣어준다 물론 설계상 다른곳에 넣어도되는데 일단 설명하기쉽게 바로버튼에 하면된다.
인스펙터 변수는 일단 id불러오는용으로 하나 있는상태인데 뭐..사실 딱히맘에안들긴해서 바꾸긴할꺼다.

On Click에 +를 눌러 리스트를 하나 눌러준다 그리고 핸들러가있는 오브젝트를 오브젝트칸에 넣어준다.

이제 함수를 선택해서 만든 핸들러를 선택하고 거기서 자신이만든 핸들러를 할당하면된다, 이러면 버튼을 누를때 선택된 함수를 실행하게된다.
원래는 컨트롤러를 할 차례긴한데.. 구조를 조금 바꿨다... 뭐 나도 배우는입장이라 이런저런 이슈가있긴한상태이고 일단 관리하고 유지보수하기 좋은 코드가 좋은거니깐...
일단 컴포저는 일종의 명령을 순차적으로 실행하고 다음 작업을 대기시키는 방식으로 작업흐름을 조정하는 역할을 한다.
이걸통해서 애미메이션과 컨트롤러를 객체간 분리를 한 상태다 기존 컨트롤러는 패널생성이랑 애니메이션이 겹쳐있어서 사실상 어찌보면 기존방법도 단일책임이긴한데 또 다른입장으로보면 아닌상태라 찝찝해서 구성했다.
일단 만드니깐 선언자체가 정말쉬어진 거도있고 기존에만들었던 애니메이션이나 조작을 쉽게 블럭처럼 추가할수도있다.
서론은 이쯤하고 구조는 다음과 같다
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIComposer : MonoBehaviour
{
private static UIComposer instance;
public static UIComposer Call
{
get => instance ?? (instance = FindAnyObjectByType<UIComposer>());
}
public event Action NextEvent;
#nullable enable
/// <summary>
/// 컨트롤, 또는 애니메이션 ,컨트롤 방식으로 호출 가능, 람다식으로 선언요구
/// </summary>
/// <param name="action1">첫번째 작업</param>
/// <param name="action2">두번째 작업</param>
public void Execute(Action ? action1 = null , Action ? action2 = null)
{
NextEvent = null;
if (action1 is Action) NextEvent += () => action2?.Invoke();
action1?.Invoke();
}
public void Next()
{
NextEvent?.Invoke();
NextEvent = null;
}
}
구조별로 설명하면 다음과 같다.
private static UIComposer instance;
public static UIComposer Call
{
get => instance ?? (instance = FindAnyObjectByType<UIComposer>());
}
생성자, 컴포저에 접근하기 위한 구성이다.
public event Action NextEvent;
받은 작업을 다음 순서에 저장할 변수이다
public void Execute(Action ? action1 = null , Action ? action2 = null)
{
NextEvent = null;
if (action1 is Action) NextEvent += () => action2?.Invoke();
action1?.Invoke();
}
작업을 등록하는 곳이다. 일단 작업을 넣기전 변수를 초기화해준다 작업방식에따라 예기치않게 이전작업이 남아서문제가 생길수있다
여기서 첫번째 작업이 안비어있고 등록되어있을때 두번째 작업을 변수에넣어주는걸 시도한다. 인수뒤에 ? 는 널일수도 값이 있을수도라는거라고 생각하면 편하다.
그리고 첫번째 작업을 실행한다 만약 첫번째 인수도 널인 상태라면 알아서 null라 아무것도 안일어난다.
public void Next()
{
NextEvent?.Invoke();
NextEvent = null;
}
두번째 작업을 실행시키는 인수이다.
예를들어 패널 페이드인일때 패널을 생성하고 애니메이션이나 패널조작을할때 해당 함수에 다음작업을 불러올 구간을 정하고, 거기다 이함수를 넣으면 작동한다.
물론 이 함수를 실행할때 두번째작업이없다고 문제가 생기진않는다. 일종의 다음작업과 연결고리역활을 한다 하면된다.
이 작업을 불러올려면 기본적으로 씬에 존재해야한다 문제는 씬마다 이걸 두기는 참 귀찬다 생길때마다 이런 객체들 한개씩 다 언제추가하나 생각하면 골머리가 아프다.

그럴땐 꼼수를쓰자 우린 매니저를 싱글턴으로 등록한적이있다. 즉 매니저는 씬어디로가든 사라지지않고 계속 존재한다.
즉 여기다 두는게 제일 편하다. 물론, 다른방식도 많다 하지만 편한건 이게최고인거같다.
이제 본격적으로 패널을 생성 및 제거해보자.
뭐 이건 설명할거도없다 위에설명대로만 하면된다.
코드는 다음과 같다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UImanager_PopupUI : MonoBehaviour
{
private static UImanager_PopupUI instance;
public static UImanager_PopupUI PopupUI
{
get => instance ?? (instance = FindObjectOfType<UImanager_PopupUI>());
}
public void show(string uiName)
{
var manager = UImanager.manager;
GameObject panelInstance = Instantiate(manager.popupUIDictionary[uiName]);
panelInstance.transform.SetParent(manager.canvas.transform, false);
if(manager.currentPopupUI.Count > 0)
{
Vector2 newPos = manager.currentPopupUI.Peek().transform.position;
panelInstance.transform.position = new Vector2(newPos.x + 20, newPos.y - 20);
}
manager.currentPopupUI.Push(panelInstance);
UIComposer.Call.Next();
}
public void hide()
{
var manager = UImanager.manager;
GameObject panelInstance = manager.currentPopupUI.Pop();
Destroy(panelInstance);
}
}
생성자 부분은 건너뛰자 어짜피 계속 나올꺼고 이미 한참나와서 반복적으로 설명할 이유가없다.
뭐 잘모르겠으면 첫번째 번호부터 다시 읽는것을 추천한다. 그럴일은 없겠지만...
public void show(string uiName)
{
var manager = UImanager.manager;
GameObject panelInstance = Instantiate(manager.popupUIDictionary[uiName]);
panelInstance.transform.SetParent(manager.canvas.transform, false);
if(manager.currentPopupUI.Count > 0)
{
Vector2 newPos = manager.currentPopupUI.Peek().transform.position;
panelInstance.transform.position = new Vector2(newPos.x + 20, newPos.y - 20);
}
manager.currentPopupUI.Push(panelInstance);
UIComposer.Call.Next();
}
핵심은 이거다 여기서 우리는 필요한것들을 꺼낼꺼다
var manager = UImanager.manager;
매니저를 변수로 저장해주자 이거 그대로쓰기엔 코드가 너무길어진다.
GameObject panelInstance = Instantiate(manager.popupUIDictionary[uiName]);
panelInstance.transform.SetParent(manager.canvas.transform, false);
오브젝트를 복제해주자 Instantiate()함수는 인스턴스생성을 하는 역할을 한다 작동 구조를 순서로 보자면 다음과 같다.
1.매니저에서 저장된 작업에 접근한다. 우린 public Dictionary<string, GameObject> popupUIDictionary = new(); 라는 변수를 매니저에서 생성하고 업데이터로 팝업 프리팹들을 저장해둔 상태다
2.manager.popupUIDictionary[uiName] 를 통해 딕셔너리에서 함수에서 받아온 이름을 가진 패널오브젝트를 값으로 복사한다
3.GameObject panelInstance 변수에 저장한다
4.panelInstance.transform.SetParent(manager.canvas.transform, false); 를 통해 해당오브젝트를 캔버스 아래다 이동시킨다.
if(manager.currentPopupUI.Count > 0)
{
Vector2 newPos = manager.currentPopupUI.Peek().transform.position;
panelInstance.transform.position = new Vector2(newPos.x + 20, newPos.y - 20);
}
manager.currentPopupUI.Push(panelInstance);
이를 통해 팝업생성을 설명했다.
팝업삭제는 어...
public void hide()
{
var manager = UImanager.manager;
GameObject panelInstance = manager.currentPopupUI.Pop();
Destroy(panelInstance);
}
전에 팝업을 스택에 저장했듯이 pop()으로 꺼내서 Destroy();로 없애주면된다. 후속작업이 없는건 Destroy(panelInstance);를 쓴 이상 애니메이션을 작동할 패널이 없기때문이다.
컴포저랑 같다 솔직히 이제 왠만하면 이방식 그대로쓴다. 싱글턴에 박아두자.
기존 매니저에 이걸 추가하자
public void ShowPanel_popup_info(string uiName)
{
if (!popupUIDictionary.ContainsKey(uiName))
{
return;
}
UIComposer.Call.Execute(() => UImanager_PopupUI.PopupUI.show(uiName));
}
구조는 단순하게 말하자면 함수의 매개변수를 가져와서 일단 딕셔너리에있는지 검사한다. 딕셔너리는 게임을 시작할때 패널을 가져와 저장하는 역할을 한다, 만약 없는 패널이면 리턴시킨다.
다음은 컴포저를 통해 호출한다 UIComposer.Call.Execute(); 를 사용하고 작업을 등록하는것은 람다식으로한다.
람다식으로 하는이유는 간단하다 컴포저는 이벤트 델리게이터 방식을쓴다 여기서 매개변수를 쉽게 넘기기위해선 람다식이 최적이기 때문이다. 매개변수를 옴기자고 이것저것 만지는거보다 원형 그대로보내는게 제일 깔끔하다 판단했다.
즉 이방식으로 핸들러 > 매니저 > 컴포저 > 컨트롤러순으로 기능을 구별하고 작동시켰다.
나머지는 다음에 계속 할 예정이다 아마 옵저버랑 애니메이션, 그리고 자잘한 몇개 남은거같다.