본문 바로가기

Unity/로직 설계

[Unity] 마우스 호버링 시 강조 로직 설계

이번엔 무엇을 했는가?

깃허브에 새 레포지토리를 만들었다.

 

 

GitHub - miniron-v/Unity-Snap-On-Scripts: 유니티 게임오브젝트에 부착해 쉽게 사용 가능한 코드들을 모아

유니티 게임오브젝트에 부착해 쉽게 사용 가능한 코드들을 모아둡니다.<br> Gather code snippets that can easily snap onto Unity GameObjects and use right away. - miniron-v/Unity-Snap-On-Scripts

github.com

 

이번 프로젝트의 목적은 바로 사용 가능한 스크립트 설계 및 저장으로, 코드의 재사용성에 초점을 맞췄다보면 좋을 듯하다.

매 프로젝트 개발 시마다 스크립트를 새로 짤 필요 없이, 기존 코드들을 컴포넌트로 부착만 하면 사용 가능한. 그런 구조로 구현하려는 노력의 흔적이다.

 

이번엔 그 첫걸음인 마우스 올림/내림에 따른 강조 효과에 대해 알아보자.


어떻게 쓰는 건데?

상황에 맞는 하이라이터를 갖다 쓰면 된다.

 

현재는 Hover Highlighter 뿐이지만, 다른 하이라이터를 만들 수도 있으니 분리시켜뒀다. UI Highlighter를 통해 사용법을 간단히 살펴보자.

 

단일 객체 사용법

간단히 버튼에 적용해보자

 

버튼을 하나 만들고, FadeUIHighlighter를 드래그한다. 그럼 오른쪽처럼 Handler와 함께 추가된다.

Handler는 오브젝트에 붙은 모든 Highlighter를 통합 관리하는 친구다.

 

Logo를 연결하고, 나머진 기본값으로 두고 한번 실행해보면?

 

마우스가 안 보인 건 아쉽다

 

이렇게 마우스를 올리면 로고가 나타나고, 내리면 사라지는 버튼이 된다.

 

그룹 사용법

라디오버튼처럼, 하나가 강조되면 나머진 원래 상태로 돌아가는 기능도 자주 구현하게 될 것이다. 유니티에선 주로 ToggleGroup으로 부르길래, 비슷하게 만들어봤다.

 

사용법은 간단하다. 먼저 버튼을 여러 개로 늘려보자.

 

일일이 위치 조정하기 귀찮아서 Layout Group을 썼다.
Buttons에 붙여 뒀다

 

그리고 부모 오브젝트에 UIHighlighterToggleGroup을 추가한다.

사실 꼭 부모일 필욘 없다. 그게 이 코드의 강력한 점이기도 하고.

 

 

이전에 자동 추가된 Handler에 Toggle Group 변수가 있었다. 여기에 방금 추가한 Toggle Group 스크립트를 할당해주면 끝이다.

 

 

그럼 이렇게, 하나가 강조되면 나머진 강조 해제되는 그룹이 생성되었다.

그런데, Default가 Unhighlight와 동일하니 티가 잘 안난다. 그러니 값을 조금 조정해보자.

 

 

디폴트 값을 반투명으로 바꿔봤다.

이제 실행해보면?

 

이제 좀 잘 드러난다

 

이렇게, 아무 것에도 마우스를 올리지 않으면 같은 그룹의 버튼들이 Default로 돌아간다.

단순히 오브젝트에 붙이기만 하면 되며, 버튼 외에도 다양한 곳에 사용이 가능하다.

 

Scale, Fade, Active 등의 효과가 구현되어 있으며, 만약 다른 효과가 필요하다면 HoverUIHighlighter를 상속받고 함수 3개만 구현하면 끝이다.

 

복잡한 강조 효과 생성법

이번엔 Fade 말고, 좀 더 복잡한 효과를 만들어보자.

예를 들어, 어떤 버튼을 강조할 때 크기를 즉시 키운 후 천천히 페이드하는 효과를 구현하려면 어떻게 해야 할까?

 

그저 두 개를 쓰면 될 뿐

 

그냥 기존 버튼에 ScaleUIHighlighter를 추가하면 끝이다.

 

 

그럼 이렇게 간단하게 복잡한 강조 효과를 생성할 수 있다.


어떻게 만들었나?

코드는 깃허브에 공개되어 있으니 여기에 적진 않겠다. 간단한 구조라 이해하기 어렵지도 않을 것이다.

그러니 이번엔 늘 하던대로, 제작 과정을 천천히 풀어보겠다.

 

처음 프로젝트를 시작하게 된 건, 이번 학기에 소프트웨어공학 수업을 들으며 코드의 재사용성에 대해 생각하게 됐기 때문이다. 수업 내용을 상기하며 게임을 제작하다보니, 여러 UI 스크립트에 같은 효과를 구현하는 게 번거롭단 걸 느꼈다.

 

또, 유니티 코리아 유튜브의 디자인 패턴 시리즈를 보다보니, 기능별로 클래스를 분리하는 과정이 마음에 들었다. 일종의 컴포넌트 기반 개발처럼, 하나의 Player 오브젝트를 만들어도 Move, Attack 등을 분리하는 게 인상깊었다.

 

여기에 약간의 추상화만 더하면, 현재 유니티가 하고 있듯 컴포넌트를 개발하고, 부착하는 방식으로 개발할 수 있겠단 생각이 들었다.

 

이때 가장 중요한 건, 이미 개발된 컴포넌트는 아닌가?는 점이다. 얼핏보면 UI 시스템은 이미 잘 구현된 것처럼 보이며, 실제로 Button 컴포넌트엔 Highlight가 포함되어 있다.

 

하지만 Tint, Swap 등 한정된 강조 효과확장의 어려움, 복잡한 효과 구현 불가 등 한계를 느끼고, UI 강조 효과 구현을 스냅온 프로젝트의 첫 대상으로 정했다.

 

맨 처음엔...

처음엔 단순히 Fade, Scale 등의 효과를 따로 분리해내고, 한 오브젝트에 여러 스크립트를 부착해 복잡한 효과를 구현하는 방법을 생각했다.

 

구현의 편의성을 위해 HoverUIHighlighter 클래스를 만들고, 아래와 같이 작성했었다.

 

using UnityEngine;
using UnityEngine.EventSystems;

public abstract class HoverUIHighlighter : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
    public void OnPointerEnter(PointerEventData eventData)
    {
        Highlight();
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        Unhighlight();
    }

    protected abstract void Highlight();
    protected abstract void Unhighlight();
}

 

OnPointerEnter와 Highlight를 연결해두고, 상속받아 Highlight, Unhighlight만 구현하는 단순한 구조.

실제로도 잘 동작했지만, 디자이너 님의 요청 사항에 맞게 구현하다보니 한계가 느껴졌다.

 

토글 그룹의 구현

 

4개의 카드 중 하나가 강조되면, 나머지 3개는 어두워지게 해주세요.

 

 

위의 방법으론 이런 토글 그룹을 구현할 수 없었다.

단순히 위 스크립트에 그룹 변수를 추가할 수도 있었지만, 여러 Highlighter를 붙이는 만큼, 모든 스크립트에 끌어다 할당하는 건 굉장히 귀찮을 일이었다.

(반대로 토글 그룹에, 모든 하이라이터를 끌어다 놓는 것도 마찬가지로 귀찮다.)

 

그렇기에 토글로 할당할 땐 하나에만 끌어다놓자!라는 생각에 UIHighlighterHandler를 만들게 되었다.

Handler는 토글 그룹에게 메세지를 받아, 오브젝트의 모든 효과를 실행해야 했다.

그래서 자연스레 UnityEvent를 사용, 기존 Highlighter가 이벤트에 함수를 등록하는 방식으로 구현했다.

 

Highlighter가 Handler를 찾게 되니, RequireComponent를 사용해 Highlighter만 끌어다놓아도 Handler가 자동 추가되게 했다.

 

그룹에 핸들러 할당 vs 핸들러에 그룹 할당

다음은 그룹에 핸들러를 할당하냐, 핸들러에 그룹을 할당하냐하는 문제가 있었다.

둘은 얼핏보면 비슷하나, 사용 경험에 있어서, 그리고 동작 방식에 있어선 크게 달라지게 된다.

 

그룹에 핸들러를 할당하면, 그룹 내에 배열을 만들고 핸들러를 끌어다 놓게 될 것이다. 반대라면, 핸들러의 토글 그룹 변수에 그룹을 끌어다 놓게 된다.

 

이들의 가장 큰 차이는 그룹을 변경할 때 나타난다. 그룹에 핸들러를 할당하는 방식이라면, 기존 그룹에서 제거 -> 새 그룹에 추가를 거쳐야 하지만, 핸들러에 그룹을 할당하게 되면 새 그룹을 핸들러에 할당만 하면 끝이다.

 

그룹에 할당하는 방식의 장점이라면, 어쨌든 그룹엔 핸들러 배열을 생성해야 한다는 점이다. 즉, 추가 작업 없이도 배열이 생성된다. 실제로 후자의 방법을 택했을 땐, 모든 Handler의 Awake에 그룹에 자기 자신을 추가하는 로직이 필요하다.

 

그럼에도 핸들러에 그룹을 할당하게 구현한 건, 결국 사용하기 편해서이다. 게다가 그룹, 핸들러가 부모 - 자식 관계로 꼭 묶이지 않는 만큼, 그룹 교체 시 오브젝트를 찾는 수고도 덜 수 있다. 따라서 2번째 방법을 선택했고, 편하게 사용 중이다.


이렇게 개발해보니

확실히 재사용이 용이해진 게 느껴진다. 기존엔 역할 별로, 혹은 위치 별로 나눴기에 SkipButton, EventButton, SkillButton 등 이름만 다르고 비슷한 스크립트가 많이 생겼는데, 이젠 강조 효과만 담당하는 스크립트가 따로 있으니 강조 효과를 스크립트별로 반복 구현할 필요도 없고, 강조 효과를 고치기 위해 관련 없는 스크립트에 접근하는 일도 사라졌다. 무엇보다 상속을 이용해 쉬운 확장이 가능하다는 점이 가장 좋다.

 

앞으로도 재사용을 염두에 두며 개발할 예정이며, 나아가 새 프로젝트에도 바로 적용 가능한 스크립트를 위주로 작성할 것이다. 그럼 게임 개발 경력이 늘어날 수록, 개발 속도는 기하급수적으로 상승할 것이며, 게임잼 같은 대회에서도 훨씬 완성도 높은 결과물을 낼 수 있을 것이다.