LoginSignup
52
35

More than 3 years have passed since last update.

UnityでAndroidの戻るキー対応をなるべく簡単にする提案

Last updated at Posted at 2020-02-18

意外と面倒くさいAndroid戻るキー対応

ね。面倒くさいですよね。(iOSには無いわけですし・・・)
さしあたり一番簡単なのは「画面上の『ネガティブ的』なボタンと同等の機能をつける」事です。

ダイアログを開いたのであれば、戻るキーが「閉じるボタン」や「戻るボタン」相当になればよいですし。
image.png

画面遷移したのであれば、戻るキーが「Backボタン」(前の画面に戻る)相当になればよいわけです。
image.png

そう書くと簡単のように聞こえますが、
画面遷移した次の画面で、ダイアログを表示した場合は?
image.png

戻るキーを押したら、ダイアログも閉じてしまう+画面も前の画面に戻ってしまう

では困るわけです。

これをまともに対応しようとすると、

  • 優先順位スタックマネージャ的なクラスを作成
  • 画面遷移したら、「戻るボタン」の処理(Actionとか?)を(上記)スタックマネージャにPush
  • ダイアログを開いたら「ダイアログ閉じるボタン」の処理(Actionとか?)をスタックマネージャにPush
  • 戻るキーを押したら、スタックマネージャにスタックされている処理の一番上(Peek)を処理
  • ダイアログを閉じたら「ダイアログ閉じるボタン」の処理をスタックマネージャからRemove
  • もう一度戻るキーを押したら・・・・

といった、管理が必要になります。 はい面倒臭いですね!

もっとシンプルに考える

そもそも、上記例の「画面遷移した次の画面で、ダイアログを表示した場合」って、普通はダイアログがモーダル的に表示されていて、後ろの「戻るボタン」は押せないようにしているのがほとんどのはず。(わざわざ後ろのボタンのintaractiveをfalseにしているのか、「タッチガード」的な全画面Panelを一枚噛ませてタッチイベントを遮断しているかのどちらかがほとんどでしょう)

問題なのは、

単純に戻るキーとボタンが押されたときの処理を関連付けてしまうと、uguiのイベントとは関係無しに処理が呼ばれてしまう

ことです。

なので、徹底的にuguiのイベントを倣い、戻るキーの押下を指定ボタンへのマウスクリックへとすり替えてあげれば解決です。

作ってみた

KeyBind.cs
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[RequireComponent(typeof(Button))]
public class KeyBind : MonoBehaviour
{
    [SerializeField]
    private Button _targetButton;

    public KeyCode _bindKey;

    private void Reset()
    {
        _targetButton = GetComponent<Button>();
    }

    private static List<RaycastResult> raycastResultList = new List<RaycastResult>();
    private PointerEventData _pointerEventData;

    private void Update()
    {
        //指定したキーの押下
        if (Input.GetKeyDown(_bindKey))
        {
            _pointerEventData = new PointerEventData(EventSystem.current)
            {
                button = PointerEventData.InputButton.Left,
                position = _targetButton.transform.position //指定したボタンの位置にマウスがある体
            };
            EventSystem.current.RaycastAll(_pointerEventData , raycastResultList);
            var validGameObject = raycastResultList.Select(result => result.gameObject).FirstOrDefault(gameObject => gameObject != null);//一番最初にぶつかっている有効なGameObject取得
            raycastResultList.Clear();
            if (validGameObject == null)
            {
                return;
            }
            var currentPointerDownHandlerObject = ExecuteEvents.GetEventHandler<IPointerDownHandler>(validGameObject); //ボタン位置にあるGameObjectからIPointerDownHandlerを保持しているGameObjectを取得
            if (currentPointerDownHandlerObject != _targetButton.gameObject){
                return;    //ボタン位置から得られたGameObjectとボタンのGameObjectが異なる=別のもので遮られている ので処理しない
            }

            _pointerEventData.pointerPress = currentPointerDownHandlerObject;
            ExecuteEvents.Execute(currentPointerDownHandlerObject, _pointerEventData, ExecuteEvents.pointerDownHandler);
        }

        //指定したキーの押上
        if (_pointerEventData != null && _pointerEventData.pointerPress != null && Input.GetKeyUp(_bindKey))
        {
            ExecuteEvents.Execute(_pointerEventData.pointerPress, _pointerEventData, ExecuteEvents.pointerUpHandler);
            ExecuteEvents.Execute(_pointerEventData.pointerPress, _pointerEventData, ExecuteEvents.pointerClickHandler);
            _pointerEventData = null;
        }
    }
}

(よくわからんなりに調べて作ったので、大分力業ですが・・・)

使い方

このScriptをButtonコンポーネントが乗っているGameObjectに追加します。
image.png

Target Button は勝手に同GameObjectButtonがセットされます。
そして
Bind Key には割り当てたいハードキー を指定します(KeyCode の一覧が候補で出ます)
Androidの戻るキーは KeyCode.Escape で割り当たります。
image.png

なんと、これだけで、ボタンのタップとAndroidの戻るキーが同等になります! シンプル!!

注意

ボタンを疑似的にクリックした相当なので、(利点でも欠点でもあるんですが)ボタンのTransitionがそのまま効きます。
↑の動画をよく見ると分かるんですが、戻るキーを押した時でもボタンの色が変化しています(戻るキーを押しっぱなしにすると、ボタンも押されっぱなしになる)
それが嫌! という場合には使えないです。 悪しからず・・・。

補足

今回、 Androidの戻るキー対応 と銘打っては居ますが、既に書いた通りボタンには KeyCodeで割り当てるキーを指定することができます。
もう一つの使い道として、入力処理の一元化があります。

こちらの動画のゲームで今回のKeyBind.csが実際に使われており、前半はマウスでボタンをクリックして操作ですが、後半はそれぞれのボタンに割り当てられたキーボードで操作しています。

このように「複数方法の入力処理を制御」するには

  • ベタでボタンがクリックされた場合の処理とキーボード入力の処理の場合を分けて書いてしまうスタイル
  • IInput のような入力処理を抽象化したインタフェースを切り、IInput を実装した KeyboardInputButtonInput のようなクラスをそれぞれ実装するスタイル
  • 神Inputクラスに想定される全インプット処理分の条件分岐をぶち込んでいくGODスタイル

などなど。方法はありますがそれなりに面倒で。

対して、↑の動画のゲームでは入力制御は「ボタン処理」のみ対象に記述しています。
そして、KeyBind.csはあくまでもボタンの疑似クリック処理なので、キーボード操作を増やしても入力制御処理は何も手を入れずに済んでいます。 つまり、ボタンによる入力処理で「処理の一元化」がされている状態です。

もちろん、これは画面上にバーチャルパッド的なものをuguiで置いているから出来るだけなので適用範囲はそう広くは無いですが、使える人も少なくないのではないでしょうか。

52
35
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
35