MainScene UI 개발

작명이 마음에 들지 않아 Global에서 MainScene으로 바꿨다.

메인 씬의 UI구나 하는 느낌이 들 수 있게끔 말이다.

 

 

 

UI 연결하기

 

우선 각 메뉴의 Presenter, UIPresenter, UIView와 캔버스를 만들었다.

지금은 비어있으나 딱히 상관없다.

연결하는 것이 목표기 때문이다.

 

버튼을 누르면 해당 캔버스가 켜지거나 꺼지는 기능을 만들 것이다.

 

파이프라인이 복잡해지게 하고 싶진 않아서 MainSceneUIPresenter 클래스가 UIPresenter를 들고 있지 않게 할 것이다.

대신 MainScenePresenter를 통해 Show, Hide를 담은 콜백을 전달할 것이다.

 

    public class MainSceneUIPresenterAction
    {
        public Action OnEmployeeCanvasButtonClick { get; set; }
        public Action OnArrangementCanvasButtonClick { get; set; }
        public Action OnWorldmapCanvasButtonClick { get; set; }
        public Action OnBrandCanvasButtonClick { get; set; }
        public Action OnOfferAppCanvasButtonClick { get; set; }
    }

    private MainSceneUIPresenterAction action;

 

파라미터가 더러워질 것을 예상하여 Action을 담는 클래스를 만들었다.

 

    #region Public Method
    public void Initialize(MainSceneUIPresenterAction action)
    {
        view = GetComponent<MainSceneUIView>();

        this.action = action;
        AddListners();
    }
    #endregion

    #region Private Method
    private void AddListners()
    {
        view.EmployeeCanvasButton.onClick.AddListener(() => action.OnEmployeeCanvasButtonClick.Invoke());
        view.ArrangementCanvasButton.onClick.AddListener(() => action.OnArrangementCanvasButtonClick.Invoke());
        view.WorldmapCanvasButton.onClick.AddListener(() => action.OnWorldmapCanvasButtonClick.Invoke());
        view.BrandCanvasButton.onClick.AddListener(() => action.OnBrandCanvasButtonClick.Invoke());
        view.OfferAppCanvasButton.onClick.AddListener(() => action.OnOfferAppCanvasButtonClick.Invoke());
    }
    #endregion

 

이렇게 하면 코드가 이전보단 간결해진다.

 

    uIPresenter.Initialize(new MainSceneUIPresenter.MainSceneUIPresenterAction
    {
        OnEmployeeCanvasButtonClick = () =>
        {
            employeePresenter.ShowOrHide();
            arrangementPresenter.Hide();
            brandPresenter.Hide();
            offerAppPresenter.Hide();
        },
        OnArrangementCanvasButtonClick = () => 
        {
            arrangementPresenter.ShowOrHide();
            employeePresenter.Hide();
            brandPresenter.Hide();
            offerAppPresenter.Hide();
        },
        OnWorldmapCanvasButtonClick = () => 
        {
            employeePresenter.Hide();
            arrangementPresenter.Hide();
            brandPresenter.Hide();
            offerAppPresenter.Hide();
        },
        OnBrandCanvasButtonClick = () => 
        {
            brandPresenter.ShowOrHide();
            employeePresenter.Hide();
            arrangementPresenter.Hide();
            offerAppPresenter.Hide();
        },
        OnOfferAppCanvasButtonClick = () =>
        {
            offerAppPresenter.ShowOrHide();
            employeePresenter.Hide();
            arrangementPresenter.Hide();
            brandPresenter.Hide();
        }
    });

 

이건 MainScenePresenter의 uiPresenter 초기화 코드이다.

월드맵은 항상 최하단에 켜져 있을 예정이기 때문에 자신에 대한 부분은 없다.

물론 이 대로는 좀 문제가 있어 보인다.

자신을 제외한 클래스만 Hide 하면 좋을 텐데...

잠시 후 다시 수정하고 일단 테스트부터 해보자.

    public void ShowOrHide()
    {
        if (!uIPresenter.gameObject.activeSelf)
            Show();
        else
            Hide();
    }

    public void Show()
    {
        uIPresenter.Show();
    }

    public void Hide()
    {
        uIPresenter.Hide();
    }

 

이렇게 각 프레젠터 안에서 캔버스의 액티브 상태를 보고 켜거나 끈다.

따로 Show나 Hide로 접근할 수 있도록 메서드를 분리해 놓았다.

 

 

작동은 잘 된다.

그러나 지금은 코드 중복이 많고, 더럽다.

당장 뜯어고쳐야 하는 코드이다.

 

모든 프레젠터에 쇼, 하이드 기능이 들어가고 있고 전부 자신의 ui프레젠터를 켜고 끈다는 공통된 기능을 수행한다.

그래서 PresenterBase와 UIPresenterBase라는 부모 추상 클래스를 만들어 공통된 기능을 상속시킬 것이다.

이러면 프레젠터들을 하나의 속성으로 묶어 편하게 관리할 수도 있다!

 

public abstract class UIPresenterBase : MonoBehaviour
{
    abstract public void Show();

    abstract public void Hide();
}

 

public abstract class PresenterBase : MonoBehaviour
{
    [SerializeField] protected UIPresenterBase uIPresenter;

    public void ShowOrHide()
    {
        if (!uIPresenter.gameObject.activeSelf)
            Show();
        else
            Hide();
    }

    public void Show()
    {
        uIPresenter.Show();
    }

    public void Hide()
    {
        uIPresenter.Hide();
    }
}

 

 

        public void Initialize(List<Employee> employees, EmployeeListUIPresenter employeeListUI)
        {
            this.employees = employees;
            (uIPresenter as EmployeeUIPresenter)
                .Initialize(employeeListUI, (Employee employee) => LevelUp(employee), (Employee employee) => RankUp(employee));
        }

 

UIPresenterBase 타입으로 상속받고 있기 때문에 형변환을 전부 해주었다.

 

    private void Awake()
    {
        worldmapPresenter = GetComponent<WorldmapPresenter>();
        storePresenter = GetComponent<StorePresenter>();
        presenters[(int)Menu.employee] = GetComponent<EmployeePresenter>();
        presenters[(int)Menu.arrangement] = GetComponent<ArrangementPresenter>();
        presenters[(int)Menu.brand] = GetComponent<BrandPresenter>();
        presenters[(int)Menu.offerApp] = GetComponent<OfferAppPresenter>();
    }

 

그리고 MainScenePresenter를 수정했다.

일단 enum값으로 무슨 메뉴인지 분리해 주었으며, 다른 메뉴와 다르게 작동할 Worldmap, store는 따로 작성하였다.

 

    public void Initialize()
    {
        // test
        // 여기서 세이브 데이터를 불러올 예정이기 때문.
        List<Employee> employees = new List<Employee>();
        foreach (Character character in DataManager.instance.CharacterList)
        {
            employees.Add(new Employee(character));
        }
        List<Store> stores = new List<Store>();

        EmployeeListUIPresenter employeeListUIPresenter = Instantiate(employeeListUIPresenterPrefab);
        employeeListUIPresenter.Initialize(employees);
        worldmapPresenter.Initialize();
        storePresenter.Initialize(stores, employeeListUIPresenter);

        (presenters[(int)Menu.employee] as EmployeePresenter).Initialize(employees, employeeListUIPresenter);
        (presenters[(int)Menu.arrangement] as ArrangementPresenter).Initialize();
        (presenters[(int)Menu.brand] as BrandPresenter).Initialize();
        (presenters[(int)Menu.offerApp] as OfferAppPresenter).Initialize();

        uIPresenter.Initialize(new MainSceneUIPresenter.MainSceneUIPresenterAction
        {
            OnEmployeeCanvasButtonClick = () =>
            {
                (presenters[(int)Menu.employee] as EmployeePresenter).ShowOrHide();
                SetAction(Menu.employee);
            },
            OnArrangementCanvasButtonClick = () => 
            {
                (presenters[(int)Menu.arrangement] as ArrangementPresenter).ShowOrHide();
                SetAction(Menu.arrangement);
            },
            OnWorldmapCanvasButtonClick = () => 
            {
                SetAction(Menu.worldmap);
            },
            OnBrandCanvasButtonClick = () => 
            {
                (presenters[(int)Menu.brand] as BrandPresenter).ShowOrHide();
                SetAction(Menu.brand);
            },
            OnOfferAppCanvasButtonClick = () =>
            {
                (presenters[(int)Menu.offerApp] as OfferAppPresenter).ShowOrHide();
                SetAction(Menu.offerApp);
            }
        });

        void SetAction(Menu menu)
        {
            for (int i = 0; i < presenters.Length; i++)
            {
                if (i != (int)menu)
                    presenters[i].Hide();
            }
        }
    }

 

전체 초기화 구문이다.

 

이제 SetAction이라는 로컬 메서드를 통해 자신 이외의 메뉴들을 비활성화시킨다!

물론 월드맵은 자신을 온오프 하는 기능이 없고 말이다.

 

작동하는 것은 아까랑 똑같기 때문에 패스한다.

 

 

 

 

마치며

오늘의 회고라는 말은 맞지 않을 때도 있는 것 같아 제목을 바꿨다.

오늘은 메뉴를 이동하는 기능을 넣었다.

정확히는 구조를 단단히 하는 시간을 가졌지 않나 싶다.

위의 글자(돈, 명예)에 대한 부분은 나중에 돈을 버는 기능을 만들 때 다시 살펴볼 예정.

 

내일부터는 월드맵 파트로 넘어간다.

+ Recent posts