(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]


本ブログの運営者kan.kikuchiが個人で開発した新作VRソフトです!


アプリがバックグラウンドに行ったor戻った事を(エディタでも)検知する方法【Unity】


このエントリーをはてなブックマークに追加


この記事でのバージョン
Unity 2021.1.16f1


はじめに

iPhoneでアプリ起動中に下から上にスワイプしたりすると(ちょっと前の端末ならホームボタンを押すと)

アプリは画面に表示されなくなりますが、終了したわけではなくいつでも再開ができる状態、

いわゆるバックグラウンドに行った状態になります。

f:id:kan_kikuchi:20210822075602g:plain


今回はそんなバックグラウンドに行ったor戻った状態を検知する方法の紹介です!

ちなみにUnityエディタ上でも使う事ができます。

f:id:kan_kikuchi:20210822074402g:plain



OnApplicationPause

まず、MonoBehaviourには

一時停止時(とその解除時)に実行されるOnApplicationPauseというメソッドがあります。

プレイヤーが一時停止したときにすべてのゲームオブジェクトに送信されます


実装方法は以下のような感じのメソッドをMonoBehaviourを継承したクラスに書くだけ。

private void OnApplicationPause(bool pauseStatus) {
  if (pauseStatus) {
    Debug.Log($"アプリが一時停止(バックグラウンドに行った)");
  }
  else {
    Debug.Log($"アプリが再開(バックグラウンドから戻った)");
  }
}


しかものEdit/Project Settings…にあるPlayerVisible In Backgroundのチェックを外せば

f:id:kan_kikuchi:20210822065409j:plain
f:id:kan_kikuchi:20210822065957j:plain


Unityエディタ上で他のアプリを選択した時も機能するようになります。

f:id:kan_kikuchi:20210822070019g:plain


ただ、Unityのバージョンや実機の端末やOSによって機能したりしなかったりするっぽいです。


OnApplicationFocus


次にMonoBehaviourには

選択された時(とその解除時)に実行されるOnApplicationFocusというメソッドがあります。

プレイヤーがフォーカスを取得、または、失ったときに、すべてのゲームオブジェクトに送信されます。


これも実装方法は以下のような感じのメソッドをMonoBehaviourを継承したクラスに書くだけ。

ただし、OnApplicationPauseとはフラグの意味合いが逆になるので注意が必要です。

private void OnApplicationFocus(bool hasFocus) {
  if (hasFocus) {
    Debug.Log($"アプリが選択された(バックグラウンドから戻った)");
  }
  else {
    Debug.Log($"アプリが選択されなくなった(バックグラウンドに行った)");
  }
}


エディタ上ではGameウィンドウを選択しているかどうかという判定になります。

f:id:kan_kikuchi:20210822073311g:plain


ただAndroid(端末やOSによるっぽい?)のスクリーンロック時に呼ばれないという欠点があります。


ApplicationBackgroundObserver

OnApplicationPauseもOnApplicationFocusも一つだけで使うとちょっと不安が残るので、

以下のような感じで併用するのがオススメです。

//バックグラウンドに行っているか
private bool _isBackground = false;
  
private void OnApplicationPause(bool pauseStatus) {
  ChangeBackgroundStatus(pauseStatus);
}

private void OnApplicationFocus(bool hasFocus) {
  ChangeBackgroundStatus(!hasFocus);
}

//アプリがバックグラウンドにいるかのステータスを変更
private void ChangeBackgroundStatus(bool isBackground) {
  if (isBackground == _isBackground) {
    return;
  }

  if (isBackground) {
    Debug.Log($"アプリがバックグラウンドへ");
  }
  else{
    Debug.Log($"アプリがバックグラウンドから復帰");
  }

  _isBackground = isBackground;
}


さらにバックグラウンド監視専用のクラスを作ると、

判定が必要になるたびにいちいち処理を書かなくて良くなるので、これもオススメです。

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

/// <summary>
/// アプリがバックグラウンドにいるかを監視するクラス
/// </summary>
public class ApplicationBackgroundObserver : MonoBehaviour{
  
  //インスタンス
  public static ApplicationBackgroundObserver Instance { get; private set; }
  
  //バックグラウンドに行っているか
  private bool _isBackground = false;
  
  //アプリがバックグラウンドにいるかのステータスを変更された時のイベント
  public event Action<bool> ChangedBackgroundStatus = delegate {};

  //=================================================================================
  //初期化
  //=================================================================================

  //起動時に実行される
  [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  static void Initialize(){
    //オブジェクトとコンポーネントを作成し、シーン遷移では破棄されないように
    var observerObject = new GameObject("ApplicationBackgroundObserver");
    DontDestroyOnLoad(observerObject);
    Instance = observerObject.AddComponent<ApplicationBackgroundObserver>();
  }

  //=================================================================================
  //バックグラウンドにいるかのステータス切り替え
  //=================================================================================

  private void OnApplicationPause(bool pauseStatus) {
    ChangeBackgroundStatus(pauseStatus);
  }

  private void OnApplicationFocus(bool hasFocus) {
    ChangeBackgroundStatus(!hasFocus);
  }

  //アプリがバックグラウンドにいるかのステータスを変更
  private void ChangeBackgroundStatus(bool isBackground) {
    if (isBackground == _isBackground) {
      return;
    }
    _isBackground = isBackground;
    ChangedBackgroundStatus(_isBackground);
  }

}


なお、上記のスクリプトを作ればApplicationBackgroundObserverのオブジェクト

自動で生成&永続化されるため事前準備の必要はないですし、

f:id:kan_kikuchi:20210822073630j:plain


使い方もイベントに処理を登録するだけと凄く簡単です。

ただし、シーン遷移時等のオブジェクト破棄する時にイベントから処理を解除する必要はあります。

private void Start() {
  //イベントに処理登録
  ApplicationBackgroundObserver.Instance.ChangedBackgroundStatus += OnChangeBackgroundStatus;
}

//アプリがバックグラウンドにいるかのステータスが変わった
private void OnChangeBackgroundStatus(bool isBackground) {
  if (isBackground) {
    Debug.Log($"アプリがバックグラウンドへ");
  }
  else{
    Debug.Log($"アプリがバックグラウンドから復帰");
  }
}
f:id:kan_kikuchi:20210822074402g:plain


ちなみにUniRxを使えば、



ReactivePropertyを使ってもう少し簡単に実装出来ますし、

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;

/// <summary>
/// アプリがバックグラウンドにいるかを監視するクラス
/// </summary>
public class ApplicationBackgroundObserver : MonoBehaviour{
  
  //インスタンス
  public static ApplicationBackgroundObserver Instance { get; private set; }
  
  //バックグラウンドに行っているか
  private readonly ReactiveProperty<bool> _isBackground = new ReactiveProperty<bool>();
  public IObservable<bool> IsBackgroundObservable => _isBackground;

  //=================================================================================
  //初期化
  //=================================================================================

  //起動時に実行される
  [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  static void Initialize(){
    //オブジェクトとコンポーネントを作成し、シーン遷移では破棄されないように
    var observerObject = new GameObject("ApplicationBackgroundObserver");
    DontDestroyOnLoad(observerObject);
    Instance = observerObject.AddComponent<ApplicationBackgroundObserver>();
  }

  //=================================================================================
  //バックグラウンドにいるかのステータス切り替え
  //=================================================================================

  private void OnApplicationPause(bool pauseStatus) {
    ChangeBackgroundStatus(pauseStatus);
  }

  private void OnApplicationFocus(bool hasFocus) {
    ChangeBackgroundStatus(!hasFocus);
  }

  //アプリがバックグラウンドにいるかのステータスを変更
  private void ChangeBackgroundStatus(bool isBackground) {
    if (isBackground == _isBackground.Value) {
      return;
    }
    _isBackground.Value = isBackground;
  }

}


AddToのおかげでイベントの解除も自動で行えるようになります。

private void Start() {
  //イベントを購読
  ApplicationBackgroundObserver.Instance.IsBackgroundObservable.Subscribe(OnChangeBackgroundStatus).AddTo(gameObject);
}

//アプリがバックグラウンドにいるかのステータスが変わった
private void OnChangeBackgroundStatus(bool isBackground) {
  if (isBackground) {
    Debug.Log($"アプリがバックグラウンドへ");
  }
  else{
    Debug.Log($"アプリがバックグラウンドから復帰");
  }
}