UnityでAndroidの戻るボタンに対応する
Androidで実行時にナビゲーションバーを表示する
Unityはデフォルトではフルスクリーンになっているため、そのままでは戻るボタンが表示できない。
そこで最初に起動するシーンのAwakeあたりでプラットフォームがAndroidの時に、Screen.fullScreen = false;
を実行してフルスクリーンを解除する。
public class SampleScene : MonoBehaviour
{
private void Awake()
{
// Androidの場合フルスクリーンモードを解除してナビゲーションバーを常時表示
if (Application.platform == RuntimePlatform.Android)
{
Screen.fullScreen = false;
}
}
}
戻るボタンが押された事を検出する方法
KeyCode.Escapeが押されているかどうかをUpdateの中で判定すればいい。
UnityEditor上の実行でもプラットフォームをAndroidにしていれば、UNITY_ANDROIDに条件がマッチする。
private void Update()
{
#if UNITY_ANDROID
if (Input.GetKeyDown(KeyCode.Escape))
{
// 戻るボタンが押された時の処理をここに書く
}
#endif
}
デバイスシミュレーターではEscを押しても反応してくれない
デバイスシミュレーターを実行時はEscキーを押してもInput.GetKeyDown(KeyCode.Escape)
が常にfalseとなってしまう。
これでは非常に不便なのでAndroidのナビゲーションバーの戻るボタンを押した事にするデバイスシミュレーターのプラグインを書く。
UniRxを使ってグローバルにイベントを発行してやる必要がある。
とりあえず、このスクリプトを任意の場所に置く。
ファイル名はUniRxPublisherSubscriber.csとしているが別の名前でも問題ない。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using System;
/// <summary>
/// UniRxのMessageBrokerの拡張
/// 引用元:
/// https://takap-tech.com/entry/2023/01/23/234127
/// </summary>
namespace UniRx
{
// 型を指定したpub
public interface IMessagePublisher<T>
{
/// <summary>
/// Send Message to all receiver.
/// </summary>
void Publish(T message);
}
// 型を指定したsub
public interface IMessageReceiver<T>
{
/// <summary>
/// Subscribe typed message.
/// </summary>
IObservable<T> Receive();
}
// pub/sub共通
public interface IMessageBroker<T> : IMessagePublisher<T> , IMessageReceiver<T>
{
public class DefaultImpl : IMessageBroker<T>
{
private readonly IMessageBroker _service;
public DefaultImpl(IMessageBroker service) => _service = service;
public void Publish(T message) => _service.Publish(message);
public IObservable<T> Receive() => _service.Receive<T>();
}
}
// 型の決まったpub/subを取得できるようにメソッドを追加する
public static class MessageBrokerExtensions
{
public static IMessagePublisher<T> GetPublisher<T>(this IMessageBroker self)
{
return new IMessageBroker<T>.DefaultImpl(self);
}
public static IMessageReceiver<T> GetSubscriber<T>(this IMessageBroker self)
{
return new IMessageBroker<T>.DefaultImpl(self);
}
}
// 直接Subscribeできるようにメソッドを追加する
public static class IMessageReceiverExtensions
{
public static IDisposable Subscribe<T>(this IMessageReceiver self, Action<T> action)
{
return self.Receive<T>().Subscribe(action);
}
public static IDisposable Subscribe<T>(this IMessageReceiver<T> self, Action<T> action)
{
return self.Receive().Subscribe(action);
}
}
}
イベントデータ。特にプロパティに持つものはないので空だけど用意する。
/// <summary>
/// Androidのナビゲーションバーの戻るボタンがクリックされた時のイベント
/// デバイスシミュレーターの拡張プラグイン用
/// </summary>
public readonly struct AndroidBackButtonPressedEventArgs
{
}
そして本命のデバイスシミュレーターのプラグイン。
任意の場所に配置。
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using System.Windows.Input;
using UnityEditor.DeviceSimulation;
using UnityEngine;
using UnityEngine.UIElements;
using UniRx;
/// <summary>
/// Androidのナビゲーションバーのシミュレート
/// </summary>
public class AndroidNavigationBarPlugin : DeviceSimulatorPlugin
{
public override string title => "Android NavigationBar";
private Button _backButton;
private IMessagePublisher<AndroidBackButtonPressedEventArgs> _androidBackButtonPressedEventPublisher = MessageBroker.Default.GetPublisher<AndroidBackButtonPressedEventArgs>();
public override void OnCreate()
{
}
public override VisualElement OnCreateUI()
{
VisualElement root = new VisualElement();
_backButton = new Button { text = "Back" };
_backButton.clicked += () =>
{
// キーボードを押したことにできないので代わりにイベントを送信する
_androidBackButtonPressedEventPublisher.Publish(new AndroidBackButtonPressedEventArgs());
};
root.Add(_backButton);
return root;
}
}
#endif
これらのスクリプトを用意して実行すると、このようにAndroid NavigationBarという項目が増えていて「Back」ボタンが表示されている。
このボタンを押す事で、AndroidBackButtonPressedEventArgsのイベントが通知されデバイスシミュレーターでアプリを実行している時にも戻るボタンを押された事に出来る。
しかし当然ながらそれを受け取る処理を作らないといけないので、次にそれをやる。
例えばAndroidの戻るボタンを受け取りたいシーンではこんな感じで、本来の戻るボタンを受け取る処理と、デバイスシミュレーターのコントロールパネルの「Back」ボタンからのイベントの両方を受け取って処理する形にする。
このサンプルではシーンの親クラスでAndroidの戻るボタンの押された状態を受け取り、継承したクラスでOnBackButtonPressedをオーバーライドしてやる形を想定している。
public abstract class SceneBase : MonoBehaviour
{
private IMessageReceiver<AndroidBackButtonPressedEventArgs> _androidBackButtonPressedEventReceiver = MessageBroker.Default.GetSubscriber<AndroidBackButtonPressedEventArgs>();
private void Awake()
{
_androidBackButtonPressedEventReceiver.Subscribe((AndroidBackButtonPressedEventArgs eventArgs) =>
{
OnBackButtonPressed();
}).AddTo(gameObject);
}
void Update()
{
if (IsAndroidBackButtonPressed())
{
OnBackButtonPressed();
}
}
/// <summary>
/// Androidの戻るボタンが押された時に呼ばれる
/// </summary>
public virtual void OnBackButtonPressed()
{
}
private bool IsAndroidBackButtonPressed()
{
#if UNITY_ANDROID
return Input.GetKeyDown(KeyCode.Escape);
#else
return false;
#endif
}
}
Discussion