Unity×iOS

【Unity×iOS】音量ボタンを検知する方法|ネイティブ連携で簡単実装

スマホアプリの中には、音量ボタンで操作できる便利なUIや、ユーザーの隠れたアクションを検知する仕組みがあります。
しかしUnityには、標準で音量ボタン(ハードボタン)を検出する仕組みがありません
この記事では、iOSの音量ボタン(+/−)の操作をUnityから検知する方法を、ネイティブプラグイン+C#スクリプトの構成で詳しく解説します。
コピペOKのコード付きで、誰でも簡単に導入できます!

この記事で分かること

  • Unityでは音量ボタンが検出できない理由
  • 音量の変化をフックしてボタン操作を検知する仕組み
  • 【コピペOK】Objective-C++プラグイン+Unity連携スクリプト
  • 音量ボタン検出を活用した便利な使い方

なぜUnityでは音量ボタンを検出できないのか?

Unityはタッチやキー入力には対応していますが、
iOSの物理ボタン(音量+/−、サイレントスイッチなど)は標準でアクセスできません。

ただし、iOSではAVAudioSessionやMPVolumeViewを使えば、
音量の変化を検知することで音量ボタンの操作を間接的に検出することができます!

【コピペOK】音量ボタン検出プラグインの作成

.mmファイルを作成する

Assets/Plugins/iOS/VolumeButtonDetector.mm に以下を記述:

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>

static float lastVolume = -1.0f;
static void (*volumeCallback)(int) = NULL;

extern "C" {

void SetVolumeCallback(void (*callback)(int))
{
    volumeCallback = callback;

    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setActive:YES error:nil];
    lastVolume = session.outputVolume;

    [[NSNotificationCenter defaultCenter] addObserverForName:@"AVSystemController_SystemVolumeDidChangeNotification"
                                                      object:nil
                                                       queue:[NSOperationQueue mainQueue]
                                                  usingBlock:^(NSNotification *note) {
        float currentVolume = [session outputVolume];
        if (volumeCallback) {
            if (currentVolume > lastVolume)
                volumeCallback(1); // volume up
            else if (currentVolume < lastVolume)
                volumeCallback(-1); // volume down
        }
        lastVolume = currentVolume;
    }];
}
}

Unity側C#スクリプトを書く

using System;
using System.Runtime.InteropServices;
using UnityEngine;

public class VolumeButtonDetector : MonoBehaviour
{
#if UNITY_IOS && !UNITY_EDITOR
    [DllImport("__Internal")]
    private static extern void SetVolumeCallback(VolumeCallbackDelegate callback);

    private delegate void VolumeCallbackDelegate(int direction);
#else
    private static void SetVolumeCallback(VolumeCallbackDelegate callback) { }
    private delegate void VolumeCallbackDelegate(int direction);
#endif

    public static event Action OnVolumeUp;
    public static event Action OnVolumeDown;

    [AOT.MonoPInvokeCallback(typeof(VolumeCallbackDelegate))]
    private static void OnVolumeChanged(int direction)
    {
        if (direction > 0) OnVolumeUp?.Invoke();
        else if (direction < 0) OnVolumeDown?.Invoke();
    }

    void Start()
    {
        SetVolumeCallback(OnVolumeChanged);
    }
}

使用例(音量ボタンでUI切り替え)

public class SampleUI : MonoBehaviour
{
    void OnEnable()
    {
        VolumeButtonDetector.OnVolumeUp += () => Debug.Log("音量UPボタンが押されました");
        VolumeButtonDetector.OnVolumeDown += () => Debug.Log("音量DOWNボタンが押されました");
    }

    void OnDisable()
    {
        VolumeButtonDetector.OnVolumeUp -= () => Debug.Log("音量UPボタンが押されました");
        VolumeButtonDetector.OnVolumeDown -= () => Debug.Log("音量DOWNボタンが押されました");
    }
}

注意点・制限事項

  • 音量の変化で判定しているため、連打検出には向きません
  • ユーザーが音量をMAX or MINにしていると検出できない
  • 初回呼び出しでAVAudioSession.setActive(true)を忘れると検知できません

活用アイデア(音量ボタンの使い道)

  • バレずにスクショ → 音量ボタンで隠し機能をトリガー
  • 動画・音楽再生中に再生速度切り替え
  • ゲーム中に音量ボタンでUI開閉やモード変更(片手プレイに最適)
  • 設定画面なしでボリューム調整 or 設定パネルを表示

まとめ

項目内容
Unity標準では音量ボタンの検出不可ネイティブ連携が必要
AVAudioSession+通知センターで変化検出
コピペOKプラグイン+C#で簡単導入可能
UX強化や隠し操作トリガーに応用できる

音量ボタンという「いつでも押せる物理UI」を活かして、
ユーザーに新しい操作体験を届けてみましょう!