본문 바로가기

Unity3D/C#

유니티 안드로이드 뒤로가기 · 앱 종료 (KeyCode.Escape)

반응형

BACK KeyCode.Escape

 

유니티에서 안드로이드 뒤로가기 버튼은 Input.GetKeyDown(KeyCode.Escape) 로 받습니다. 열린 창이 있으면 가장 나중에 연 창부터 닫고, 다 닫혔을 때 「종료할까요?」 창을 띄운 뒤 Application.Quit() 으로 앱을 끝냅니다.

 

if (Input.GetKeyDown(KeyCode.Escape))
    Debug.Log("뒤로가기");

 

안드로이드 사용자는 창을 닫을 때 화면의 X 버튼보다 휴대폰의 뒤로가기를 먼저 누릅니다. 그런데 Unity 로 만든 게임은 따로 처리하지 않으면 뒤로가기를 눌러도 아무 일도 일어나지 않아서, 사용자는 게임이 멈춘 줄 알 수 있습니다. Unity 문서에 따르면 Input.backButtonLeavesApp 의 기본값은 false 라서 뒤로가기 처리는 개발자가 맡아야 하고, 그때는 KeyCode.Escape 를 확인하면 됩니다. 이번 글에서는 설정 창 위에 상점 창을 연 상태에서 뒤로가기를 누르면 상점 → 설정 순서로 닫히고, 다 닫혔을 때는 종료 확인 창이 뜨는 예제를 만들겠습니다. 두 번 눌러 종료하는 방법과 앱을 끄지 않고 뒤로 보내는 방법도 함께 보겠습니다.

 

C# (UNITY 3D)

 

 

설정과 상점을 차례로 열고 뒤로가기를 누르면 상점이 먼저 닫히고, 한 번 더 누르면 설정이 닫힙니다. 열린 창이 없을 때 누르면 종료 확인 창이 뜨고, 그 창도 뒤로가기로 닫힙니다. 종료 창에서 「예」 를 누르면 Application.Quit() 으로 앱이 끝납니다.

 

1. 준비 — 창 셋과 버튼 넷

 

Hierarchy 에서 UI (Canvas) > Panel 로 설정 창 · 상점 창 · 종료 확인 창을 만들고, UI (Canvas) > Button - TextMeshPro 로 설정 열기 · 상점 열기 버튼과 종료 창 안의 「예」 · 「아니요」 버튼을 만듭니다. 나중에 연 창이 위에 보이도록 Hierarchy 에서는 상점 창을 설정 창 아래에, 종료 창을 맨 아래에 둡니다. BackButton 은 Canvas 에 붙이고 각 칸에 끌어다 넣습니다.

 

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

 

열린 창 목록(List)을 쓰기 위해 System.Collections.Generic 을, Button 을 쓰기 위해 UnityEngine.UI 를 사용합니다. 키 입력은 예전 입력 방식(Input Manager)의 Input.GetKeyDown 을 씁니다.

 

2. 뒤로가기 처리 코드

 

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

//CodeFinder 코드파인더
//From https://codefinder.janndk.com/
public class BackButton : MonoBehaviour
{
    public GameObject panel_settings;    // 설정 창
    public GameObject panel_shop;        // 상점 창
    public GameObject popup_quit;        // 「게임을 종료할까요?」 창
    public Button button_settings;       // 설정 열기
    public Button button_shop;           // 상점 열기
    public Button button_quit_yes;       // 종료 창 — 예
    public Button button_quit_no;        // 종료 창 — 아니요

    readonly List<GameObject> list_open = new List<GameObject>();   // 열린 순서대로

    void Start()
    {
        Close_All();
        Add_Listener(button_settings, () => Open_Panel(panel_settings));
        Add_Listener(button_shop, () => Open_Panel(panel_shop));
        Add_Listener(button_quit_yes, Quit_Game);
        Add_Listener(button_quit_no, On_Back);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))                       // 안드로이드 뒤로가기 = Escape
            On_Back();
    }

    public void Open_Panel(GameObject panel)
    {
        if (panel == null || list_open.Contains(panel))
            return;
        panel.SetActive(true);
        list_open.Add(panel);
    }

    public void On_Back()
    {
        if (list_open.Count > 0)
        {
            GameObject top = list_open[list_open.Count - 1];        // 가장 나중에 연 창부터
            top.SetActive(false);
            list_open.RemoveAt(list_open.Count - 1);
            return;
        }
        Open_Panel(popup_quit);                                      // 다 닫혔으면 종료를 물어봄
    }

    void Close_All()
    {
        foreach (GameObject panel in new[] { panel_settings, panel_shop, popup_quit })
            if (panel != null)
                panel.SetActive(false);
        list_open.Clear();
    }

    void Quit_Game()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;             // 에디터에서는 Application.Quit 이 무시됨
#else
        Application.Quit();
#endif
    }

    void Add_Listener(Button button, UnityEngine.Events.UnityAction action)
    {
        if (button == null)
            return;
        button.onClick.RemoveAllListeners();
        button.onClick.AddListener(action);
    }
}

 

창을 열 때마다 list_open 끝에 넣고, 뒤로가기를 누르면 끝에서 하나를 꺼내 닫습니다. 종료 창도 Open_Panel 로 열어 같은 목록에 들어가므로, 종료 창이 떠 있을 때 뒤로가기를 누르면 앱이 꺼지지 않고 종료 창만 닫힙니다. 「아니요」 버튼도 On_Back 을 불러 같은 일을 합니다. 에디터에서는 Application.Quit 이 무시되므로 #if UNITY_EDITOR 일 때 Play 모드를 끄게 했습니다.

이 스크립트는 Unity 6000.6.3f1 에서 컴파일 오류 없이 들어갔고, 배치 모드에서 창과 버튼을 만들어 붙인 뒤 On_Back 을 직접 불러 보니 설정 · 상점을 연 상태에서 상점 → 설정 → 종료 창 열림 → 종료 창 닫힘 순서로 바뀌었습니다. 이미 열린 창을 다시 열면 목록에 두 번 들어가지 않았습니다. 휴대폰에서 뒤로가기를 눌러 본 것은 아닙니다.

 

변수(variable)
panel_settings · panel_shop(GameObject) : 설정 창과 상점 창
popup_quit(GameObject) : 종료 확인 창
button_settings · button_shop(Button) : 창을 여는 버튼
button_quit_yes · button_quit_no(Button) : 종료 창의 「예」 · 「아니요」
list_open(List<GameObject>) : 열린 창을 연 순서대로 담는 목록

 

함수(function)
Update : 뒤로가기(Escape)가 눌리면 On_Back 을 부릅니다.
Open_Panel : 창을 켜고 목록 끝에 넣습니다.
On_Back : 가장 나중에 연 창을 닫고, 열린 창이 없으면 종료 창을 엽니다.
Close_All : 시작할 때 모든 창을 닫습니다.
Quit_Game : 앱을 끝냅니다(에디터에서는 Play 모드 끄기).
Add_Listener : 버튼에 함수를 연결합니다.

 

3. 창이 닫히는 순서

 

뒤로가기를 눌러 본 순서 — 나중에 연 창부터 닫히고 열린 창이 없으면 종료 창이 뜨며, Application.Quit 은 에디터에서 무시된다

 

닫히는 순서는 Hierarchy 순서가 아니라 연 순서입니다. 상점을 먼저 열고 설정을 나중에 열었다면 뒤로가기는 설정부터 닫습니다. 그래서 보이는 순서와 닫히는 순서를 맞추려면, 나중에 연 창이 화면에서도 위에 오도록 창들이 같은 부모 아래에 있다면 Open_Panel 의 panel.SetActive(true); 다음 줄에 panel.transform.SetAsLastSibling(); 을 넣어도 됩니다. 같은 부모 아래에서는 나중 순서의 오브젝트가 위에 그려집니다.

 

4. 두 번 눌러 종료하기

 

public TMP_Text text_toast;          // 「한 번 더 누르면 종료됩니다」 를 보여 줄 글자
float time_back = -10f;              // 마지막으로 뒤로가기를 누른 때

public void Back_Twice()
{
    if (Time.unscaledTime - time_back < 2f)
    {
        Quit_Game();                         // 2초 안에 또 누르면 종료(2절의 함수)
        return;
    }
    time_back = Time.unscaledTime;
    if (text_toast != null)
        text_toast.text = "한 번 더 누르면 종료됩니다";
}

 

종료 창 대신 「한 번 더 누르면 종료됩니다」 안내를 띄우는 방식입니다. 위 코드를 2절 BackButton 클래스 안에 넣고, 파일 맨 위에 using TMPro; 를 더한 뒤, On_Back 의 Open_Panel(popup_quit) 줄을 Back_Twice() 로 바꿉니다. text_toast 칸에는 Text - TextMeshPro 글자를 넣고, 안내는 몇 초 뒤 지우는 것이 좋습니다. 이 방식에서는 종료 창과 예 · 아니요 버튼을 쓰지 않습니다. 종료는 2절의 Quit_Game 을 부르므로 에디터에서는 Play 모드가 꺼집니다. 배치 모드에서 첫 번째 누름만 불러 보니 안내 글자가 바뀌고 누른 시각이 기록되었습니다.

 

5. 뒤로가기가 안 먹을 때 — 자주 하는 실수

 

새 Input System 만 켠 프로젝트
Input.GetKeyDown 은 예전 입력 방식(Input Manager)의 함수입니다. 이 글은 Project Settings > Player 의 Active Input Handling 이 Input Manager (Old) 인 프로젝트에서 확인했습니다. 새 Input System 패키지만 쓰는 프로젝트라면 그 패키지의 키 입력 방식으로 Escape 를 확인해야 합니다.

 

앱을 직접 끄는 것이 맞나
Unity 문서는 안드로이드와 iOS 에 앱을 숨기고 닫는 기본 방법이 따로 있으니 Application.Quit 으로 직접 끄는 방식은 권하지 않고, 안드로이드에서 꼭 끝내야 한다면 앱을 끄는 대신 뒤로(백그라운드로) 보내라고 안내합니다. iOS 는 종료를 사용자에게 맡기며, Application.Quit 을 부르면 앱이 튕긴 것처럼 보일 수 있습니다.

 

Input.backButtonLeavesApp = true;        // 뒤로가기를 누르면 앱이 최소화됨(안드로이드)

 

창을 닫을 일이 없는 간단한 게임이라면 2절의 BackButton 대신 이 한 줄을 쓰는 방법도 있습니다. Unity 문서에 따르면 이 값을 true 로 두면 안드로이드에서 뒤로가기를 눌렀을 때 앱이 최소화됩니다. 2절처럼 창을 하나씩 닫는 처리와 함께 쓰는 것은 확인하지 않았습니다.

 

함께 보기: 버튼에 함수를 코드로 연결하는 방법은 유니티 Button onClick 코드로 연결 · interactable 로 잠금 · 연타 막기 에 있습니다.

 

뒤로가기와 종료 동작은 Unity 공식 문서 Input.backButtonLeavesApp · Application.Quit 과 Unity 6000.6.3f1 에디터(Windows)에서 직접 불러 본 결과를 기준으로 적었습니다.

 

뒤로가기를 누르면 열린 창부터 닫고 마지막에 종료를 묻는 순서는 제가 잰드케이로 낸 랜덤 결정 앱 RandomGO 에서 실제로 쓰고 있습니다(스토어 개발사 표시는 Jakadela Games).

 

 

반응형

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