프로그래밍/유니티

[유니티] Delegate를 이용한 공통 팝업

갓똥 2021. 7. 6. 01:12
728x90
반응형

언제든 사용할 수 있는 공용 팝업을 만들자!

생김새는 위와같이 3종류의 팝업을 만들어 상황에 맞추어 쓸 예정이다.

 

하이라키는 위처럼 만들었다.

MessagePopUp은 MessagePopUpController를 갖고 있으며

BlockPanel은 뒤에 배경 블러처리용으로 사용했다.

 

메인은 PanelPosition 아래있는 Notice, Caution, Warning이다.

자식으로 있는 Text는 위 그림과 같이 상단의 알림, 주의, 경고가 입력되어있다.

Message는 내가 넣고 싶은 텍스트를 입력할 수 있는 곳이고

OKButton과 YesOrNo버튼은 하단에 "확인" 버튼 하나만 띄울 것인지 "예, 아니오" 버튼 두개를 뜨게할지 골라 출력하게 한다.

 

대략 요런 형태가 된다.

 

아래는 코드


// MessagePopUpController.cs

using System;
using UnintyEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class MessagePopUpController : MonoBehaviour
{
    public delegate void Callback();
    private Callback callbackOK = null;
    private Callback callbackYES = null;
    private Callback callbackNO = null;
    
    // Define
    public enum DialogType { NOTICE = 0, WARNING = 1, CAUTION = 2 }
    public enum DialogResponse { OK, YES, NO, ERROR }
    
    public GameObject[] Dialogs;
    public GameObject MessageBox;
    public GameObject OneButton;
    public GameObject YesOrNoButton;
    
    private Button OKButton;
    private Button YesButton;
    private Button NoButton;
    
    private DialogResponse thisResult;
    
    private void Awake()
    {
        // 버튼 배정
        OKButton = OneButton.GetComponent<Button>();
        YesButton = YesOrNoButton.transform.Find("YesButton").gameObject.GetComponent<Button>();
        NoButton = YesOrNoButton.transform.Find("NoButton").gameObject.GetComponent<Button>();
        
        // 기본 리스너 추가
        OKButton.onClick.AddListener(PopUpClose);
        YesButton.onClick.AddListener(PopUpClose);
        NoButton.onClick.AddListener(PopUpClose);
    }

    private void DialogInit() // 초기화
    {
        for (int i = 0; i < Dialogs.Length; i++)
            Dialogs[i].SetActive(false);
    }
    
    public void PopUp(string message, DialogType type)
    {
        SetCallback(null, DialogResponse.OK);

        DialogInit();
        Dialogs[(int)type].SetActive(true);
        MessageBox.GetComponent<Text>().text = message;

        YesOrNoButton.SetActive(false);
        OneButton.SetActive(true);
        OKButton.interactable = true;
        
        GetComponent<Animator>().SetTrigger("PopupOpen");
    }

    public void PopUp(string message, DialogType type, Callback Function)
    {
        SetCallback(Function, DialogResponse.OK);

        DialogInit();
        Dialogs[(int)type].SetActive(true);
        MessageBox.GetComponent<Text>().text = message;

        YesOrNoButton.SetActive(false);
        OneButton.SetActive(true);
        OKButton.interactable = true;

        GetComponent<Animator>().SetTrigger("PopupOpen");
    }

    public void YesOrNoPopUp(string message, DialogType type, Callback yesButtonFuncion, Callback noButtonFuntion)
    {
        SetCallback(yesButtonFuncion, DialogResponse.YES);
        SetCallback(noButtonFuntion, DialogResponse.NO);

        DialogInit();
        Dialogs[(int)type].SetActive(true);
        MessageBox.GetComponent<Text>().text = message;

        OneButton.SetActive(false);
        YesOrNoButton.SetActive(true);
        YesButton.interactable = true;
        NoButton.interactable = true;

        GetComponent<Animator>().SetTrigger("PopupOpen");
    }

    // 팝업이 닫힐 때, 지정해 둔 콜백 실행
    public void PopUpClose()
    {
        switch (EventSystem.current.currentSelectedGameObject.name)
        {
            case "OKButton":
                thisResult = DialogResponse.OK;
                callbackOK?.Invoke();
                callbackOK = null;
                break;
                
            case "YesButton":
                thisResult = DialogResponse.YES;
                callbackYES?.Invoke();
                callbackYES = null;
                break;

            case "NoButton":
                thisResult = DialogResponse.NO;
                callbackNO?.Invoke();
                callbackYES = null;
                break;

            default:
                thisResult = DialogResponse.ERROR;
                break;
        }

        OKButton.interactable = false;
        YesButton.interactable = false;
        NoButton.interactable = false;

        GetComponent<Animator>().SetTrigger("PopupClose");
    }

    // Callback 관련
    public void SetCallback(Callback call, DialogResponse buttontype)
    {
        switch (buttontype)
        {
            case DialogResponse.OK:
                callbackOK = call;
                break;

            case DialogResponse.YES:
                callbackYES = call;
                break;

            case DialogResponse.NO:
                callbackNO = call;
                break;

            default:
                callbackOK = call;
                callbackYES = call;
                callbackNO = call;
                break;
        }
    }

    internal void YesOrNoPopUp(string v1, DialogType nOTICE, object v2, object p)
    {
        throw new NotImplementedException();
    }
}

다른 코드는 전부 보면 이해가고 핵심은 delegate를 이용해 메소드를 넘겨주어 처리한다는 것이다.

사용은 아래와 같이 하면 된다.

// 싱글톤
    MessagePopupController.instance.Popup("Message", DialogType.Notice, MethodName);
    MessagePopupController.instance.Popup("Message", DialogType.Notice, () => HelloWorld());
    
// public으로 등록해놓고 쓰기
    messagePopupController.YesOrNoPopUp("Message", DialogType.Notice, YesMethod, null);
    messagePopupController.YesOrNoPopUp("Message", DialogType.Notice, null, () => NoMethod(param));

Delegate를 간단하게 요약하자면 (이전에 쓴글 복붙)

인자를 전달하려면 람다식을 쓰면 된다.

 

최종결과물

    MessagePopUpController.instance.YesOrNoPopUp("튜토리얼 화면을 종료하고 다음으로 진행하시겠습니까?",
                                                 DialogType.Notice,
                                                 () => CombatManager.instance.StartBattle(player, State.Start),
                                                 null);

 

아,

1. Delegate를 쓸 때 Invoke를 사용하자

2. ?를 통해 null검사가 가능하다. (Method?.Invoke();)

728x90
반응형