유니티

[유니티] #2 수박 게임 만들기 (UI)

디버그러 2025. 2. 26. 22:00

수박 게임을 만들면서 NPC와 UI를 어떻게 처리해야 할지 고민했다.

일단 UI를 2개의 Canvas로 분리하여 글로벌 정보(일시 정지 버튼, 골드량)와 게임 내 상호작용(터치 패드, NPC 요청 패널)을 만들었다.

 

글로벌 캔버스

 

 

1. GlobalCanvas (Screen Space - Overlay) 

  • 이 캔버스는 카메라와 관계없이 항상 화면 위에 렌더링 된다.
  • 항상 화면에 보여야 하는 정보를 위한 UI이므로, 다른 게임 오브젝트에 영향 받지 않고 안정적으로 노출된다.
  • 예시: 일시 정지 버튼, 현재 골드량 UI

 

2. Canvas (World Space)

  • 이 캔버스는 게임 월드의 일부처럼 동작하며, 게임 플레이와 직접 연관된 UI를 담는다.
  • 게임 내 상호작용이 필요한 UI이므로, 카메라에 따른 변화가 중요하다.
  • 예시: 터치 패드, NPC의 요청 패널

 

UI 요소를 역할별로 분리하면, 나중에 수정하거나 기능을 추가할 때 어느 부분에 손을 봐야 하는지 명확해진다.

두 캔버스에 개별 Graphic Raycaster가 있어, 서로 다른 입력 우선순위를 부여할 수 있다.

GlobalCanvas에서 불필요한 요소(배경 Panel, Top 등)의 Image 컴포넌트는 Raycast Target 체크를 해제해두어야 터치 패드나 요청 패널 같은 UI 요소에 이벤트가 전달된다.

 

부모 오브젝트의 Raycast Target을 끄더라도, 자식 오브젝트에 켜져 있다면 자식 요소는 정상적으로 동작한다.

단, GlobalCanvas 자체의 Raycast Target을 끄면 캔버스 전체의 입력 처리가 중지되므로 주의해야 한다.

 

 

 

 

 

일시 정지 버튼

 

public void ClickPause()
{
    isPaused = !isPaused;

    _audioSource.ignoreListenerPause = true;
    _audioSource.clip = buttonClip;
    _audioSource.Play();

    if (isPaused)
    {
        // 일시 정지
        Time.timeScale = 0f;
        AudioListener.pause = true;
    }
    else
    {
        // 게임 재개
        Time.timeScale = 1f;
        AudioListener.pause = false;
    }
}

 

Time.timeScale을 0으로 설정하면 물리(Rigidbody, Collision), 애니메이션 등 대부분의 게임 요소가 멈춘다.

반대로 0.5f 같은 값으로 설정하면 슬로우 모션 효과를 줄 수도 있다.

 

AudioListener.pause를 true로 하여 BGM도 일시 정지 시켰다.

하지만 버튼 클릭음조차 멈추면 안 되므로 ignoreListenerPause 속성을 true로 설정하여 일시 정지 상태에서도 재생되게 했다.

또한, 효과음과 BGM은 별도의 AudioSource로 분리해 관리하고 있다.

이렇게 하면 일시 정지 시 BGM은 멈추더라도 버튼 클릭음 같은 효과음은 정상적으로 재생할 수 있다.

 

 

 

 

 

버튼 클릭 효과

 

public void clickButton()
{
    if (HasEnoughFruits(out var removeList))
    {
        RemoveRequestFruits(removeList);
        
        // 조건을 충족했을 때 효과음 재생
        NPCManager.Instance._audioSource.clip = NPCManager.Instance.requestClickClip;
        NPCManager.Instance._audioSource.Play();
    }
    else
    {
    	// 조건 미충족 시 버튼이 일시적으로 빨간색 변경
        Color originalColor = _image.color;
        _image.DOColor(Color.red, 0.2f).OnComplete(() => { _image.color = originalColor; });
		
        // 버튼이 약간 요동침
        transform.DOComplete(true);
        _rectTransform.DOPunchAnchorPos(new Vector2(-5f, 0), 0.5f).SetRelative(true);
    }
}

 

NPC 요청 버튼을 눌렀을 때 조건이 맞으면 효과음이 재생되고, 그렇지 않으면 버튼이 잠깐 빨간색으로 변하고 약간 요동치는 효과를 주었다.

 

 

사용한 DOTween 함수

 

1. DoColor(Color.red, 0.2f)

- 버튼의 Image 컴포넌트 색상을 0.2초 동안 빨간색으로 변경한다.

 

2. OnComplete(()=> {_image.color = originalColor})

- 애니메이션이 완료되면 원래 색상으로 바꾼다. (람다 함수)

 

3. DOComplete(true)

- 버튼을 연속해서 클릭할 경우, 이전 애니메이션이 남아있는 상태에서 새로운 애니메이션이 시작되면 위치 오프셋이 누적되는 현상이 발생할 수 있다.

- 현재 진행 중인 모든 Tween을 즉시 완료시켜, 버튼의 위치가 원래 위치로 복귀하도록 한다.

 

4. DOPunchAnchorPos(new Vector2(-5f, 0), 0.5f)

- RectTransform 전용 애니메이션 함수다.

- 버튼이 오른쪽으로 약간 요동치도록 한다.

 

5. SetRelative(true)

- 지정한 벡터(-5f, 0)이 현재 위치에 상대적으로 적용되게 한다.

 

Canvas 안에 있는 UI 요소는 일반 Transform 대신 RectTransform을 사용하는 것이 좋다.

 

 

 

 

 

UI 스케일 조정 & 재시작 버튼

 

Unity 에디터에서는 GlobalCanvas가 정상적으로 보이지만, Simulator 모드에서는 UI 크기가 작아져 보이는 문제가 있었다.

초기 UI 제작 시 설정했어야 했는데, 이제라도 수정했다.

 

 

GlobalCanvas의 UI Scale Mode를 Scale With Screen Size로 설정했다.

이 모드는 참조 해상도(Reference Resolution)를 기준으로 UI 요소들의 크기와 위치를 자동으로 조정해준다.

 

참조 해상도를 1920x1080으로 설정하면, 다양한 해상도에서도 UI가 균일하게 보이게 된다.

또한 세로 기준 모바일 게임이면 Match를 Height 1로 하면 된다.

 

 

 

사이즈 조정하는 김에 UI도 모두 바꾸었다.

삼성 갤럭시 S10 기준으로는 버튼이 약간 가려지는 문제가 있지만, 전반적으로는 잘 보인다.

 

 

	
    // 계속하기 버튼 클릭 시 호출
    public void ClickResume()
    {
        isPaused = false;
        AudioListener.pause = false;
        pausePanel.SetActive(false);
        Time.timeScale = 1f;
        ButtonSound();
    }

    // 리플레이 버튼 클릭 시 리플레이 패널 활성화
    public void ClickReplay()
    {
        replayPanel.SetActive(true);
        ButtonSound();
    }

    // 리플레이 예 버튼 클릭 시 현재 씬을 재시작
    public void ReplayYes()
    {
        Time.timeScale = 1f;
        AudioListener.pause = false;
        ButtonSound();
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

    // 리플레이 아니오 버튼 클릭 시 리플레이 패널 비활성화
    public void ReplayNo()
    {
        replayPanel.SetActive(false);
    }

 

다시하기 버튼을 누르면 replayPanel이 활성화되고 Yes를 누르면 씬이 재시작된다.

재시작 전에 Time.timeScale을 1f로 돌려주고 AudioListener.pause도 false로 설정했다.