예전에 한번 정리한답시고 글을 썼던 적이 있는데
간만에 다시 작업을 하려니 내 스스로도 헷갈리는 네이밍과 구조로 되어 있어서 싹 갈아 엎었다.
당시엔 이것저것 많이 넣어보려고 먼저 틀을 잡았었는데,
역시 나는 하나씩 해보면서 구조를 짜는게 맞는 것 같다..
클릭 위치로 이동하는 기능을 구현하고 나니, 오브젝트를 클릭했을 때는 어떻게 처리해야 할지 고민이 됐다.
1. 아무것도 하지 않기 (근처까지 이동해야 동작하도록)
2. 해당 위치로 이동하기
3. 이동하기 + 상호작용 실행하기
유저의 의도는 보통 3번이겠지만..
로직이 너무 복잡해질 것 같아서 간단한 2번으로 가기로 했다.
목적지를 설정하는 부분에
if (GetMapPos(endPos).isInteractable) endPos.y += -1;
클릭한 위치가 isInteractable하면 (=상호작용 가능하면) 오브젝트의 한칸 아래 타일로 이동하도록 했다.
오브젝트 구조는 당장 구현에 필요한 두가지로 축약해서 정리했다.
타일맵 위에 올라가는 오브젝트는 모두 BaseObject를 부모로 가지며, 그 중 캐비닛은 인벤토리 UI를 오픈하는 UIObject이다.
private void Interact()
{
// ✏️ 상호작용 키는 '마우스 우클릭'
if (m_inputKey != KeyCode.Mouse1) return;
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Node cellNode = MapManager.Inst.GetMapPos(mousePos);
// ✏️ 클릭한 위치가 상호작용 가능하지 않을 경우 return
if (cellNode.isInteractable == false) return;
// ✏️ 현재 플레이어의 위치가 클릭한 오브젝트 근처가 아닐 경우 (기준 값 1.5f)
if (MapManager.Inst.IsNearBy(new Vector3(cellNode.x, cellNode.y), transform.position) == false) return;
// ✏️ 클릭한 위치 타일 검사 -> 상호작용 함수 호출
Collider2D col = Physics2D.OverlapCircle(mousePos, 0.4f, LayerMask.GetMask(ClientUtil.InteractableLayer));
if (col != null)
{
if (col.TryGetComponent(out BaseObject baseComp) == false) return;
baseComp.I_InteractableFunc();
}
}
기본 UI와 그를 상속받는 인벤토리 UI 로 단순하게 변경했다.
UIObject는 BaseUI 클래스를 변수로 가지고 있고,
상호작용 버튼이 클릭되면 UIManager에 해당 값을 넘긴다.
UIManager.Inst.OpenUI(m_ui);
바로 오픈하지 않고 매니저로 넘긴 이유는 크게 두가지가 있다.
꼭 위와 같은 이유가 아니더라도 UI 관련 기능은 한군데서 관리하는게 좋다.
// *** UI Open ***
// UIObject.cs
public override void I_InteractableFunc()
{
UIManager.Inst.OpenUI(m_ui);
}
// UIManager.cs
public void OpenUI(BaseUI ui)
{
isUIOpened = true;
m_uiOpened.Add(ui);
ui.Open();
}
// *** UI Close ***
// BaseUI.cs
public virtual void OnClickClose()
{
UIManager.Inst.CloseUI(this);
}
// UIManager.cs
public void CloseUI(BaseUI ui)
{
m_uiOpened.Remove(ui);
ui.Close();
if (m_uiOpened.Count == 0)
{
isUIOpened = false;
}
}
다만 2번의 Esc 클릭시 닫는 기능은 키 입력이 씹히는 현상이 있어서 조금 더 확인해봐야 할 것 같다.