본문 바로가기

Unity3D/C#

유니티 앱 내릴 때 자동 저장 (OnApplicationPause)

반응형

PAUSE OnApplicationPause

 

유니티 모바일 게임에서 홈 버튼을 눌러 앱이 뒤로 갈 때 저장하려면 OnApplicationPause(bool paused) 를 씁니다. paused 가 true 로 들어오면 앱이 뒤로 가는 순간이므로 이때 PlayerPrefs.Save() 등으로 저장합니다.

 

void OnApplicationPause(bool paused)
{
    if (paused)
        PlayerPrefs.Save();                  // 앱이 뒤로 갈 때 저장
}

 

게임을 하다가 전화를 받거나 메신저를 보고 돌아왔더니, 앱이 처음부터 다시 시작되면서 모은 코인이 사라져 있는 경우가 있습니다. 휴대폰은 뒤로 간 앱을 메모리가 모자랄 때 말없이 끄기도 하고, 사용자가 최근 앱 목록에서 밀어서 지우기도 합니다. Unity 문서도 모바일에서는 OnApplicationQuit 이 불리지 않을 수 있으니 여기에 저장을 맡기지 말라고 안내합니다. 그래서 앱이 뒤로 가는 순간을 알려 주는 OnApplicationPause 와 OnApplicationFocus 에서 저장합니다. 이번 예시에서는 코인을 모으는 동안에는 저장하지 않다가, 앱이 뒤로 가거나 종료될 때 바뀐 것이 있을 때만 한 번 저장하겠습니다.

 

C# (UNITY 3D)

 

 

+10 을 세 번 눌러 코인이 30 이 되어도 아직 저장하지 않습니다. 홈 버튼으로 앱이 뒤로 가는 순간 30 을 한 번 저장하고, 이어서 들어오는 다른 이벤트는 바뀐 것이 없어 건너뜁니다. 그 뒤 최근 앱 목록에서 앱을 지워도 다시 켜면 코인 30 에서 시작합니다.

 

1. 준비 — 버튼 하나와 글자 하나

 

Hierarchy 에서 UI (Canvas) > Button - TextMeshPro 로 「+10」 버튼을, UI (Canvas) > Text - TextMeshPro 로 코인 글자를 만듭니다. AutoSave 는 씬에 늘 있는 오브젝트에 붙이고 button_coin · text_coin 칸에 끌어다 넣습니다. OnApplicationPause 같은 함수는 씬의 모든 MonoBehaviour 에 불리므로 따로 연결할 것은 없습니다. TMP 기본 글꼴에는 한글이 없어서 「코인」 이 □ 로 보이면 한글 글꼴로 TMP 글꼴 에셋(Font Asset)을 만들어 넣습니다.

 

using UnityEngine;
using UnityEngine.UI;
using TMPro;

 

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

 

2. 자동 저장 코드

 

using UnityEngine;
using UnityEngine.UI;
using TMPro;

//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
public class AutoSave : MonoBehaviour
{
    public Button button_coin;           // 누르면 코인 +10
    public TMP_Text text_coin;           // 지금 코인

    const string KEY_COIN = "save.coin";

    int coin;                            // 지금 코인
    bool is_dirty;                       // 마지막 저장 뒤에 바뀐 게 있나

    void Start()
    {
        coin = PlayerPrefs.GetInt(KEY_COIN, 0);                  // 지난번 값 불러오기
        Show_Coin();

        if (button_coin != null)
        {
            button_coin.onClick.RemoveAllListeners();
            button_coin.onClick.AddListener(Add_Coin);
        }
    }

    void Add_Coin()
    {
        coin += 10;
        is_dirty = true;                                         // 바로 저장하지 않고 표시만
        Show_Coin();
    }

    void OnApplicationPause(bool paused)
    {
        if (paused)
            Save_Game("OnApplicationPause");                     // 홈 버튼 · 다른 앱으로 나갈 때
    }

    void OnApplicationFocus(bool has_focus)
    {
        if (!has_focus)
            Save_Game("OnApplicationFocus");                     // 포커스를 잃을 때(알림창 등)
    }

    void OnApplicationQuit()
    {
        Save_Game("OnApplicationQuit");                          // 정상 종료(모바일은 안 불릴 수 있음)
    }

    void Save_Game(string reason)
    {
        if (!is_dirty)
            return;                                              // 바뀐 게 없으면 건너뜀

        PlayerPrefs.SetInt(KEY_COIN, coin);
        PlayerPrefs.Save();
        is_dirty = false;
        Debug.Log("저장 " + coin + " (" + reason + ")");
    }

    void Show_Coin()
    {
        if (text_coin != null)
            text_coin.text = "코인 " + coin;
    }
}

 

앱이 뒤로 갈 때는 포커스를 잃는 이벤트와 일시정지 이벤트가 함께 들어올 수 있습니다. 세 함수 모두 Save_Game 을 부르지만 is_dirty 가 false 이면 바로 돌아가므로, 바뀐 것이 있을 때 한 번만 저장됩니다. 코인을 누를 때마다 저장하지 않는 것은 Unity 문서가 게임 도중의 PlayerPrefs.Save 가 끊김을 만들 수 있다고 안내하기 때문입니다.

이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어갔고, 배치 모드에서 함수를 직접 불러 보니 바뀐 것 없이 Pause(true) 는 저장 0번, +10 세 번 뒤 Focus(false) → Pause(true) 는 저장 1번(30), +10 뒤 Quit 은 저장 1번(40)이었습니다. 휴대폰에서 홈 버튼을 눌러 본 것은 아니며, 이벤트가 불리는 때는 Unity 문서를 기준으로 적었습니다.

 

변수(variable)
button_coin(Button) : 누르면 코인 +10
text_coin(TMP_Text) : 지금 코인을 보여 줄 글자
KEY_COIN(string) : PlayerPrefs 에 저장할 이름
coin(int) : 지금 코인
is_dirty(bool) : 마지막 저장 뒤에 바뀐 것이 있는지

 

함수(function)
Add_Coin : 코인을 늘리고 바뀐 것이 있다고 표시합니다.
OnApplicationPause : 앱이 일시정지될 때(paused = true) 저장합니다.
OnApplicationFocus : 포커스를 잃을 때(has_focus = false) 저장합니다.
OnApplicationQuit : 정상 종료될 때 저장합니다.
Save_Game : 바뀐 것이 있을 때만 PlayerPrefs 에 쓰고 Save 합니다.
Show_Coin : 코인 글자를 고칩니다.

 

3. 언제 불리나 — Pause · Focus · Quit

 

OnApplicationPause · Focus · Quit 이 불리는 때와 AutoSave 를 직접 불러 본 저장 횟수 — 바뀐 것이 없으면 0번, 포커스와 일시정지가 이어져도 1번

 

OnApplicationPause 는 처음 시작할 때도 Awake 바로 뒤에 false 로 한 번 불립니다. 그래서 if (paused) 없이 저장 코드를 넣으면 시작하자마자 저장이 한 번 돕니다. Unity 문서는 모바일에서 포커스를 잃을 때(OnApplicationFocus) 저장하라고 권하는데, 안드로이드에서 화면 키보드가 떠 있는 채로 홈 버튼을 누르면 Focus 대신 Pause 만 불린다고 적고 있습니다. 그래서 2절은 둘 다 두었습니다. 또 키보드가 뜰 때도 OnApplicationFocus(false) 가 들어오므로, 바뀐 것이 있을 때만 저장하게 막아 둡니다. 두 이벤트가 어떤 순서로 오는지는 문서에 없어서, 어느 쪽이 먼저 와도 한 번만 저장되게 했습니다.

 

4. 돌아왔을 때 할 일

 

void OnApplicationFocus(bool has_focus)
{
    if (has_focus)
        Debug.Log("돌아옴 — 밝기 · 화면 꺼짐 설정을 다시 적용");
}

 

반대로 true 가 들어오면 앱으로 돌아온 순간입니다. 화면 밝기처럼 다른 앱에 다녀오면 풀릴 수 있는 설정은 여기서 다시 적용합니다.

 

5. 저장이 안 되어 있을 때 — 자주 하는 실수

 

OnApplicationQuit 에만 저장했다
PC 에서는 창을 닫으면 OnApplicationQuit 이 불리지만, iOS 앱은 종료되지 않고 일시정지되므로 불리지 않고, 모바일에서 OS 가 앱을 끄면 Unity 가 이 함수를 부르지 못할 수 있습니다. 배치 모드에서 저장 없이 AutoSave 를 새로 만들어 불러오니 마지막 저장 뒤에 모은 20 은 사라지고 저장된 40 에서 시작했습니다. 모바일은 앱이 뒤로 갈 때마다 종료될 수 있다고 보고 Pause · Focus 에서 저장합니다.

 

에디터에서 확인이 안 된다
에디터 위쪽의 일시정지 버튼은 OnApplicationPause 를 부르지 않습니다. 에디터에서는 Player Settings 의 Run In Background 가 꺼져 있을 때 Game 창 밖(다른 프로그램)을 누르면 포커스를 잃으면서 불립니다. 정확한 확인은 휴대폰에서 홈 버튼을 눌러 보는 것입니다.

 

함께 보기: PlayerPrefs 의 저장 · 불러오기와 키 이름 바꾸기는 이 블로그의 PlayerPrefs 글(10월 26일)에 정리했습니다.

 

이벤트가 불리는 때는 Unity 공식 문서 MonoBehaviour.OnApplicationPause · MonoBehaviour.OnApplicationQuit · PlayerPrefs.Save 를, 저장 횟수는 Unity 6000.6.3f1 에디터(Windows)에서 직접 불러 본 결과를 기준으로 적었습니다.

 

앱이 뒤로 갈 때(OnApplicationPause) 마지막 설정을 저장하는 처리는 제가 잰드케이로 낸 전광판 앱 BeamSay 에 실제로 들어 있습니다. 안드로이드가 뒤로 보낸 앱을 말없이 끄는 일이 흔해서 넣은 줄입니다(스토어 개발사 표시는 Jakadela Games).

 

 

반응형

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