유니티 백그라운드/포그라운드 음악 재생 토글하기

안녕하세요.

유니티 SDK 나오기 전에 안드로이드에서 실기기 테스트할 때, 백그라운드로 가도 소리가 재생되는 현상을 처리하는 코드를 공유드립니다.

이 토픽의 댓글을 기반으로 몇가지 빠져있는 부분과 좀 더 친절하게 작성한 코드입니다.

전부 유니티 안에서 처리할 수 있고, 두 개의 파일만 있으면 됩니다.

1. 먼저 ObserveVisibilityPlugin.jslib 파일을 만들어유니티 Assets 폴더 아래에 Plugin/WebGL 폴더에 넣습니다.
당연히 WebGL 플랫폼만 Include 해주시면 됩니다.

플러그인 내용은 아래 코드를 붙여넣어주세요.

// ObserveVisibilityPlugin.jslib
mergeInto(LibraryManager.library, {
    StartObserveVisibility: function (goPtr, methodPtr) {
        var goName = UTF8ToString(goPtr);
        var method = UTF8ToString(methodPtr);
        
        // 이전 구독 해제
        if (typeof window.__tossVisUnsub === 'function') {
            try { window.__tossVisUnsub(); } catch (e) { console.log(e); }
            window.__tossVisUnsub = null;
        }
        
        function send(state, evt) {
            var payload = JSON.stringify({
                state: state, // "visible" | "hidden"
                eventType: evt, // "visibilitychange" | "pagehide" | "pageshow" | "blur" | "focus" | "init"
                hidden: state === 'hidden',
                ts: Date.now()
            });
            console.log(`[TOSS] ${goName} ${method} ${payload}`);
            try { SendMessage(goName, method, state === 'hidden' ? 1 : 0); } catch (e) { console.log(e); }
        }
        
        // 캡처 단계로 가장 먼저 잡는다(숨기기 직전에도 최대한 빨리 유니티 호출)
        var opts = { capture: true, passive: true };
        
        function onVisibility() { send(document.hidden ? 'hidden' : 'visible', 'visibilitychange'); }
        function onPageHide()   { send('hidden',  'pagehide'); }
        function onPageShow()   { send('visible', 'pageshow'); }
        function onBlur()       { send(document.hidden ? 'hidden' : 'visible', 'blur'); }
        function onFocus()      { send(document.hidden ? 'hidden' : 'visible', 'focus'); }
        function onFreeze()     { send('hidden', 'freeze'); }
        
        // 이벤트 구독
        document.addEventListener('visibilitychange', onVisibility, opts);
        window.addEventListener('pagehide', onPageHide, opts);
        window.addEventListener('pageshow', onPageShow, opts);
        window.addEventListener('blur', onBlur, opts);
        window.addEventListener('focus', onFocus, opts);
        window.addEventListener('freeze', onFreeze, opts);
        
        // 초기 1회 상태 통지(필요 시)
        send(document.hidden ? 'hidden' : 'visible', 'init');
        
        // 이벤트 구독 해제 함수 정의
        window.__tossVisUnsub = function () {
            document.removeEventListener('visibilitychange', onVisibility, opts);
            window.removeEventListener('pagehide', onPageHide, opts);
            window.removeEventListener('pageshow', onPageShow, opts);
            window.removeEventListener('blur', onBlur, opts);
            window.removeEventListener('focus', onFocus, opts);
            window.removeEventListener('freeze', onFreeze, opts);
        };
    }
});

2. 이제 상태가 변경될 때 소리를 토글할 C#스크립트에 아래 코드를 추가합니다. 새로 생성해도 무방합니다.

public class BGMPlayer : MonoBehaviour
{
    [DllImport("__Internal")]
    private static extern int StartObserveVisibility(string goName, string methodName);
        
    private void Awake()
    {
        StartObserveVisibility(transform.GetHierarchyPath(), nameof(OnVisibleStatusChanged));
    }

    public static string GetHierarchyPath(Transform transform)
    {
        List<string> path = new List<string>();
        Transform current = transform;

        while (current != null)
        {
            path.Add(current.name);
            current = current.parent;
        }

        path.Reverse();
        return string.Join("/", path);
    }
        
    private void OnVisibleStatusChanged(int pauseStatus)
    {
        if (pauseStatus == 1)
        {
            AudioListener.pause = true;
            Time.timeScale = 0f;
            Debug.Log("앱 백그라운드 - 오디오 일시정지");
        }
        else
        {
            AudioListener.pause = false;
            Time.timeScale = 1f;
            Debug.Log("앱 포그라운드 - 오디오 재개");
        }
    }
}

생성한 C# 스크립트를 씬 어딘가에 게임오브젝트를 만들어 붙여넣어주시면 됩니다.
(당연히 이 게임오브젝트는 삭제되면 안 됩니다.)

이제 빌드 후 실행해보시면 잘 처리되는 걸 볼 수 있습니다.

감사합니다.

4개의 좋아요

정말 감사드립니다! 덕분에 구현했습니다.

버전 차이인지는 모르겠지만(6000.2.6f2 사용중입니다), 저의 경우에는 아래처럼 코드를 수정하니 정상작동했습니다.

[DllImport("__Internal")]
private static extern void StartObserveVisibility(string goName, string methodName);
    
private void Start()
{
    StartObserveVisibility(gameObject.name, nameof(OnVisibleStatusChanged));
}
1개의 좋아요

반환값이 없으니 이게 맞겠네요! 코드 수정해놓겠습니다.

좋은 내용 감사드립니다!!