【Unity】Android端末の「Backキー」に対応する

2020/03/21DEVELOP, Unity

Android 端末など「Backキー」が存在する端末に対するアプローチです。基本的な挙動は「画面上に配置されている「戻るボタン」処理と同じ動作をさせる」だと思いますので、その辺りを実現するコードを書いてみます。

目次

サンプルコード

今回は Observer で通知したいなと思いましたので、通知用のコンポーネントを作成します。

using UnityEngine;
using UnityEngine.UI;
 
namespace mira
{
    [RequireComponent(typeof(Button))]
    public class BackKeyButtonObserver : MonoBehaviour
    {
        private Button m_Button;
        public Button button
        {
            get
            {
                if (m_Button == null)
                {
                    m_Button = GetComponent<Button>();
                }
                return m_Button;
            }
        }
 
        public void Exec()
        {
            if (button != null) 
            {
                button.onClick.Invoke();
            }
        }
 
        public bool IsEnable()
        {
            return gameObject.activeInHierarchy && (button == null || button.enabled);
        }
    }
}

次に複数の画面で構成されている場合の戻るボタンのリストを持つコンポーネントを作成します。

using System.Linq;
using UnityEngine;
using UnityEngine.Events;
  
namespace mira
{
    public class BackKeyObserver : MonoBehaviour
    {
        private bool active { get; set; } = true;
 
        [HideInInspector, Header("バックキーボタンリスト(アクティブなボタンが一つだけ呼ばれます)")]
        private BackKeyButtonObserver[] m_Buttons;
        public BackKeyButtonObserver[] buttons
        {
            get
            {
                return m_Buttons;
            }
        }
 
        [SerializeField, Header("バックキーイベント(ボタンが有効な場合は呼ばれません)")]
        private UnityEvent m_OnBackKey = new UnityEvent();
        public UnityEvent onBackKey
        {
            get
            {
                return m_OnBackKey;
            }
        }
 
        private void Awake()
        {
            Debug.Log("[BackKey]Awake");
            BackKeyManager.instance.AddBackKey(this);
        }
 
        private void OnDestroy()
        {
            Debug.Log("[BackKey]OnDestroy");
            BackKeyManager.instance.RemoveBackKey(this);
        }
 
        public void Exec()
        {
            var button = buttons.FirstOrDefault(x => x.IsEnable());
            if (button != null)
            {
                button.Exec();
                return;
            }
 
            onBackKey.Invoke();
        }
 
        public bool IsEnable()
        {
            return active && gameObject.activeInHierarchy;
        }
 
        public void OnChangeActive(bool _active)
        {
            Debug.LogFormat("[BackKey]OnChangeActive - {0}", _active);
            if (active == _active)
            {
                return;
            }
            active = _active;
        }
    }
}

最後に、戻るボタンを管理するマネージャクラスです。複数の戻るボタンイベントが登録されることも多々あると思いますので、その場合は最後に登録された戻る処理を優先的に実行します。

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
 
namespace mira
{
    public class BackKeyManager : MonoBehaviour
    {
        // サンプル用に単体で動作する様に。
        private static BackKeyManager m_instance;
        public static BackKeyManager instance
        {
            get { return m_instance ?? (m_instance = GameObject.Find("BackKeyManager").GetComponent<BackKeyManager>()); }
        }
 
        /// <summary>
        /// バックキーの無効フラグ
        /// </summary>
        /// <value></value>
        public bool invalid { get; set; }
 
        /// <summary>
        /// バックキーリスト
        /// </summary>
        private readonly List<BackKeyObserver> m_BackKeys = new List<BackKeyObserver>();
 
        public void AddBackKey(BackKeyObserver _backKey)
        {
            if (!m_BackKeys.Contains(_backKey))
            {
                m_BackKeys.Add(_backKey);
            }
        }
 
        public void RemoveBackKey(BackKeyObserver _backKey)
        {
            if (m_BackKeys.Contains(_backKey))
            {
                m_BackKeys.Remove(_backKey);
            }
        }
 
        private bool IsEnableBackKey()
        {
            // バックキー監視
            if (invalid)
            {
                return false;
            }
 
            // 入力が効いていない場合
            if (EventSystem.current == null || !EventSystem.current.enabled)
            {
                return false;
            }
 
            // 
            // 他、シーン遷移中など足しても良い。
            // 
            return true;
        }
        public void Update()
        {
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                if (!IsEnableBackKey())
                {
                    return;
                }
 
                if (m_BackKeys.Any())
                {
                    // 最後に追加したバックキーを使用
                    var backKey = m_BackKeys.Last();
                    if (!backKey.IsEnable())
                    {
                        return;
                    }
 
                    // イベント実行
                    backKey.Exec();
                }
            }
        }
    }
}

使い方

シーンに 空の GameObject を配置し BackKeyManager をAddComponent しておきます。

次に、Backキーとして機能させたいボタンに BackKeyObserver を AddComponent します。AddComponent できたら Button コンポーネントの OnClick に割り当てられているであろうイベントと同じものを BackKeyObserver のOnBackKey に割り当てます。

これでBackキー押下時やESCキー押下時(Editor環境)にイベントが実行される様になります。

Posted by kazupon