본문 바로가기

Unity3D/C#

유니티 Slider onValueChanged 로 값 표시 · 정수 단계 · 저장값 불러오기 (wholeNumbers, SetValueWithoutNotify)

반응형

SLIDER onValueChanged

 

유니티 Slider 는 값이 바뀔 때마다 onValueChanged 가 불리고, 그 값을 TMP 글자에 넣으면 「볼륨 70%」 같은 표시가 됩니다. 정수로만 움직이게 하려면 wholeNumbers 를, 저장해 둔 값을 이벤트 없이 넣으려면 SetValueWithoutNotify 를 씁니다.

 

slider.onValueChanged.AddListener(value => Debug.Log(value));   // 움직일 때마다 불림
slider.SetValueWithoutNotify(0.5f);                             // 값만 바꾸고 이벤트는 안 부름

 

게임 설정 화면의 볼륨, 밝기, 마우스 감도는 대부분 슬라이더로 만듭니다. 슬라이더를 움직이면 숫자가 따라 바뀌고, 게임을 껐다 켜도 그 값이 남아 있어야 합니다. 이 블로그의 예전 글 UI Slider 기능 할당/삭제 in Script 는 예전 Text 로 값을 보여 주고 최솟값 · 최댓값 · 정수 단계를 스크립트로 정하는 데까지였습니다. 이번 글에서는 TMP 글자로 값을 보여 주고, PlayerPrefs 에 저장했다가 다음 실행 때 불러오는 볼륨 슬라이더를 만들겠습니다. 불러오는 순간 onValueChanged 가 불려 저장이 한 번 더 일어나는 문제도 함께 막겠습니다.

 

C# (UNITY 3D)

 

볼륨 슬라이더 웹 데모 — 처음엔 70 에서 이벤트 없이 시작하고, 끌면 정수가 바뀔 때마다 onValueChanged 가 불려 저장되고, 다시 실행하면 저장된 25 에서 시작한다

▲ 아래 VolumeSlider 의 동작을 옮긴 웹 데모입니다(Unity 화면 아님). 불러오기와 저장 흐름은 Unity 에서 확인한 결과와 같습니다.

 

처음 실행하면 저장된 값이 없어 70 으로 시작하고, 이때 onValueChanged 는 한 번도 불리지 않습니다. 손잡이를 끌면 정수가 바뀔 때마다 onValueChanged 가 불려 글자가 바뀌고 값이 저장됩니다. 다시 실행하면 저장된 25 에서 시작합니다.

 

1. 준비 — 슬라이더 하나와 글자 하나

 

Hierarchy 에서 UI (Canvas) > Slider 로 슬라이더를, UI (Canvas) > Text - TextMeshPro 로 값을 보여 줄 글자를 만듭니다(예전 버전 메뉴는 UI > …). VolumeSlider 는 Canvas 나 빈 오브젝트에 붙이고, slider_volume 칸에 슬라이더를, text_volume 칸에 글자를 끌어다 넣습니다. 최솟값 · 최댓값 · Whole Numbers 는 코드가 정하므로 인스펙터 값은 그대로 두어도 됩니다.

 

using UnityEngine;
using UnityEngine.UI;
using TMPro;

 

Slider 를 쓰기 위해 UnityEngine.UI 를, TMP_Text 를 쓰기 위해 TMPro 를 사용합니다.

 

2. 볼륨 슬라이더 코드

 

using UnityEngine;
using UnityEngine.UI;
using TMPro;

//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
public class VolumeSlider : MonoBehaviour
{
    public Slider slider_volume;          // 볼륨 슬라이더
    public TMP_Text text_volume;          // 「볼륨 70%」 를 보여 줄 글자

    const string KEY_VOLUME = "volume";   // PlayerPrefs 에 저장할 이름

    void Start()
    {
        if (slider_volume == null)
            return;

        Init_Slider();

        int volume_saved = PlayerPrefs.GetInt(KEY_VOLUME, 70);    // 저장된 값(처음이면 70)
        slider_volume.SetValueWithoutNotify(volume_saved);         // 불러올 때는 이벤트 없이
        Apply_Volume(slider_volume.value);                         // 글자와 소리는 직접 한 번 맞춤

        slider_volume.onValueChanged.RemoveAllListeners();
        slider_volume.onValueChanged.AddListener(Change_Volume);   // 이제부터 움직일 때마다
    }

    void Init_Slider()
    {
        slider_volume.minValue = 0;
        slider_volume.maxValue = 100;
        slider_volume.wholeNumbers = true;                         // 0, 1, 2 … 100 정수로만
    }

    void Change_Volume(float value)
    {
        Apply_Volume(value);
        PlayerPrefs.SetInt(KEY_VOLUME, (int)value);                // 바뀔 때마다 저장
    }

    void Apply_Volume(float value)
    {
        AudioListener.volume = value / 100f;                       // 0~100 을 0~1 로
        if (text_volume != null)
            text_volume.text = "볼륨 " + (int)value + "%";
    }
}

 

Play 하면 「볼륨 70%」 가 보이고, 슬라이더를 움직이면 글자와 AudioListener.volume 이 함께 바뀝니다. 바뀐 값은 바로 PlayerPrefs 에 저장되어 다음 실행 때 그 값에서 시작합니다.

이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어갔고, 배치 모드에서 슬라이더와 글자를 만들어 붙여 보니 처음 Start 뒤 「볼륨 70%」 · 저장 없음, value 에 35.4 를 넣으면 35 로 맞춰져 「볼륨 35%」 와 저장값 35, 새로 만든 슬라이더는 저장된 25 에서 시작했습니다. Game 창에서 손으로 끌어 본 것은 아닙니다.

 

변수(variable)
slider_volume(Slider) : 볼륨을 고를 슬라이더
text_volume(TMP_Text) : 지금 볼륨을 보여 줄 글자
KEY_VOLUME(string) : PlayerPrefs 에 저장할 이름

 

함수(function)
Init_Slider : 범위를 0~100 으로, 값을 정수 단계로 정합니다.
Change_Volume : 슬라이더가 움직일 때 불려, 값을 적용하고 저장합니다.
Apply_Volume : 소리 크기와 글자를 값에 맞춥니다.

 

3. 소수 값을 보여 줄 때 — ToString 과 normalizedValue

 

slider_speed.minValue = 0.5f;
slider_speed.maxValue = 2f;
slider_speed.onValueChanged.AddListener(value =>
{
    text_speed.text = "x" + value.ToString("0.0");                          // x1.3
    text_percent.text = (slider_speed.normalizedValue * 100f).ToString("0") + "%";   // 범위 안에서 몇 %
});

 

게임 속도처럼 소수로 움직이는 슬라이더는 값을 그대로 붙이면 1.3333 같은 긴 숫자가 나옵니다. ToString("0.0") 으로 자릿수를 정하면 「x1.3」 처럼 보입니다. normalizedValue 는 지금 값이 범위 안에서 어디쯤인지를 0~1 로 돌려줍니다. 범위가 0.5~2 일 때 값 1 은 0.333 이라 「33%」 가 됩니다.

 

4. wholeNumbers — 정수 단계로 움직이기

 

int level = Mathf.RoundToInt(slider.value);                     // int 로 쓸 때는 바꿔서

 

Slider 에 값을 직접 넣어 본 결과 — 같은 값과 SetValueWithoutNotify 는 이벤트 0번, wholeNumbers 에서 3.5 와 4.5 는 모두 4, minValue 를 올리면 이벤트가 불린다

 

wholeNumbers 를 켜면 값이 정수로 반올림되지만 value 의 타입은 그대로 float 입니다. int 변수에 넣을 때는 (int) 나 Mathf.RoundToInt 로 바꿔야 합니다. 이 반올림은 Mathf.Round 를 써서 .5 는 짝수 쪽으로 갑니다. 직접 넣어 보니 3.5 는 4 가 되고 4.5 도 4 가 되었습니다. 또 SetValueWithoutNotify 로 넣은 값도 같은 규칙으로 맞춰져서, 3.7 을 넣으면 4 가 됩니다.

 

5. 이벤트가 엉뚱하게 불릴 때 — 자주 하는 실수

 

불러오기만 했는데 저장 · 효과음이 돈다
value 에 값을 넣으면 손으로 움직인 것과 똑같이 onValueChanged 가 불립니다. 저장값을 slider.value = 25 로 넣었을 때 리스너가 한 번 불리는 것을 확인했습니다. 리스너에 저장이나 효과음이 들어 있다면 불러올 때는 SetValueWithoutNotify 를 쓰고, 글자처럼 화면에 보이는 것은 2절의 Apply_Volume 처럼 직접 한 번 맞춥니다. 같은 값을 다시 넣을 때는 이벤트가 불리지 않습니다.

 

slider.minValue = 50;                                           // 값이 30 이었다면 50 으로 바뀌고 onValueChanged 도 불림

 

범위를 바꿨더니 이벤트가 불린다
minValue · maxValue · wholeNumbers 를 바꾸면 슬라이더가 지금 값을 새 범위에 다시 맞추고, 값이 달라졌다면 onValueChanged 를 부릅니다. 그래서 2절 코드는 범위를 먼저 정하고 리스너를 나중에 연결합니다. 인스펙터의 On Value Changed 칸에 직접 넣은 함수는 RemoveAllListeners 로 지워지지 않으니 코드로 연결할 때는 그 칸을 비워 둡니다.

 

함께 보기: 슬라이더와 버튼에 함수를 붙이고 떼는 기본 예제는 예전 글 UI Slider 기능 할당/삭제 in Script 에 있습니다.

 

Slider 동작은 Unity 공식 문서 Slider (uGUI) · UnityEventBase.RemoveAllListeners 와 Unity 6000.6.3f1 의 ugui 패키지 소스를 기준으로 적었습니다.

 

 

반응형

Google Play 및 Google Play 로고는 Google LLC의 상표입니다.