facebook twitter hatena line email

Unity/Csharp//サイレントモードでも音を出す

提供: 初心者エンジニアの簡易メモ
移動: 案内検索

UntyのAndroid側のProjectSetting設定

  • CustomMainManifestにチェックして、Assets/Plugins/Android/AndroidManifest.xml を作成
  • CustomMainGradleTemplateにチェックして、Assets/Plugins/Android/mainTemplate.gradle を作成
  • CustomGradlePropertiesTemplateにチェックして、Assets/Plugins/Android/gradleTemplate.properties を作成
  • minimum api levelをandroid12以上に

Android実機設定

pixel3例

  1. Android実機で、サイレントモードを長押しして、サイレントモード設定を開く
  2. アラームとその他の割り込み/メディアサウンドをONにすると、消音モードでも、音がなるようになる。

注意

以下コードについて

ToggleMuteBypass(bool enable);

消音モードで、音をOFFにしたいときにfalseへ、消音モードで、音をONにしたいときはtrueへ。 起動時に、通常通り消音モードで、音をOFFにしたいときは、そのまま実行しないほうが安全。 alseで実行すると、端末によっては消音モードで音がONになる可能性がある。

実装

com.yourdomain.project1は、適宜、自分のpackage_nameに変更する。

Assets/Plugins/Android/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    
    <application>
        <!--Used when Application Entry is set to Activity, otherwise remove this activity block-->
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
        <!--Used when Application Entry is set to GameActivity, otherwise remove this activity block-->
        <activity android:name="com.unity3d.player.UnityPlayerGameActivity"
                  android:theme="@style/BaseUnityGameActivityTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
            <meta-data android:name="android.app.lib_name" android:value="game" />
        </activity>
    </application>
</manifest>

Assets/Plugins/Android/AudioPlugin.java

package com.yourdomain.project1;

import android.content.Context;
import android.media.AudioManager;
import android.util.Log;

public class AudioPlugin {
    private static final String TAG = "AudioPlugin";

    public static void setMuteBypass(boolean enable, Context context) {
        try {
            AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            
            // STREAM_MUSICの音量で制御
            int streamType = AudioManager.STREAM_MUSIC;
            int maxVolume = audioManager.getStreamMaxVolume(streamType);
            
            if (enable) {
                // 消音モード無効(最大音量に設定)
                audioManager.setStreamVolume(
                    streamType,
                    maxVolume,
                    AudioManager.FLAG_SHOW_UI
                );
                Log.d(TAG, "Mute bypass enabled - Volume set to max");
            } else {
                // 消音モード有効(音量0に設定)
                audioManager.setStreamVolume(
                    streamType,
                    0,
                    AudioManager.FLAG_SHOW_UI
                );
                Log.d(TAG, "Mute bypass disabled - Volume muted");
            }
        } catch (Exception e) {
            Log.e(TAG, "Error controlling audio: " + e.getMessage());
        }
    }
}

Assets/Plugins/Android/mainTemplate.gradle

apply plugin: 'com.android.library'
apply from: '../shared/keepUnitySymbols.gradle'
**APPLY_PLUGINS**

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**}

android {
    namespace "com.unity3d.player"
    ndkPath "**NDKPATH**"
    ndkVersion "**NDKVERSION**"

    compileSdk **APIVERSION**
    buildToolsVersion = "**BUILDTOOLS**"

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    defaultConfig {
        manifestPlaceholders = [unityPlayerActivity: "com.unity3d.player.UnityPlayerActivity"]
        minSdk **MINSDK**
        targetSdk **TARGETSDK**
        ndk {
            abiFilters **ABIFILTERS**
            debugSymbolLevel **DEBUGSYMBOLLEVEL**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
        consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
**DEFAULT_CONFIG_SETUP**
    }

    lint {
        abortOnError false
    }

    androidResources {
        noCompress = **BUILTIN_NOCOMPRESS** + unityStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
    }**PACKAGING**
}
**IL_CPP_BUILD_SETUP**
**SOURCE_BUILD_SETUP**
**EXTERNAL_SOURCES**

Assets/Plugins/Android/gradle-wrapper.properties

org.gradle.jvmargs=-Xmx4096m
android.useAndroidX=true
android.enableJetifier=true

Assets/Plugins/Android/gradleTemplate.properties

org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M
org.gradle.parallel=true
unityStreamingAssets=**STREAMING_ASSETS**
**ADDITIONAL_PROPERTIES**

Assets/Plugins/iOS/AudioPlugin.mm

#import <AVFoundation/AVFoundation.h>

extern "C" {
    void ConfigureAudioSession(bool ignoreMute) {
        AVAudioSession *session = [AVAudioSession sharedInstance];
        
        NSError *error = nil;
        [session setCategory:AVAudioSessionCategoryPlayback
                 withOptions:AVAudioSessionCategoryOptionMixWithOthers
                       error:&error];
        
        if (ignoreMute) {
            // 消音モードを無視する設定
            [session setMode:AVAudioSessionModeDefault error:&error];
            [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
            NSLog(@"消音モード無視 ON");
        } else {
            // デフォルト設定に戻す
            [session setMode:AVAudioSessionModeDefault error:&error];
            NSLog(@"消音モード無視 OFF");
        }
        
        [session setActive:YES error:&error];
    }
}

Assets/Scripts/AudioMannerManager.cs

using UnityEngine;
using System.Runtime.InteropServices;

public class AudioMannerManager : MonoBehaviour
{
    public static AudioMannerManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    #if UNITY_IOS
    [DllImport("__Internal")]
    private static extern void ConfigureAudioSession(bool ignoreMute);
    #endif

    // 消音モードで、音をOFFにしたいときにfalseへ、消音モードで、音をONにしたいときはtrueへ
    // 起動時に、通常通り消音モードで、音をOFFにしたいときは、そのまま実行しないほうが安全。
    // falseで実行すると、端末によっては消音モードで音がONになる可能性がある。
    public void ToggleMuteBypass(bool enable)
    {
        Debug.Log($"Attempting to {(enable ? "enable" : "disable")} mute bypass");

        #if UNITY_ANDROID && !UNITY_EDITOR
        AndroidToggleMuteBypass(enable);
        #elif UNITY_IOS && !UNITY_EDITOR
        iOSToggleMuteBypass(enable);
        #else
        Debug.LogWarning("Mute bypass not supported on this platform");
        #endif
    }

    #if UNITY_ANDROID
    private void AndroidToggleMuteBypass(bool enable)
    {
        try
        {
            AndroidJavaClass pluginClass = new AndroidJavaClass("com.yourdomain.project1.AudioPlugin");
            AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")
                .GetStatic<AndroidJavaObject>("currentActivity");
            
            pluginClass.CallStatic("setAudioMode", enable, activity);
            Debug.Log($"Android mute bypass set to: {enable}");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Android audio mode change failed: {e.Message}");
        }
    }
    #endif

    #if UNITY_IOS
    private void iOSToggleMuteBypass(bool enable)
    {
        try
        {
            ConfigureAudioSession(enable);
            Debug.Log($"iOS mute bypass set to: {enable}");
        }
        catch (System.Exception e)
        {
            Debug.LogError($"iOS audio session config failed: {e.Message}");
        }
    }
    #endif
}

AudioPlayTest.cs

using UnityEngine;
using UnityEngine.UI;

public class AudioPlayTest : MonoBehaviour
{
    [SerializeField] Button playButton;
    // テスト用のメソッド
    void Start()
    {
        playButton.onClick.AddListener(OnClickButton);
    }
    void OnClickButton()
    {
        GetComponent<AudioSource>().Play();
        Handheld.Vibrate(); // バイブレーションテスト
    }
}

Assets/Scripts/ToggleButtonController.cs

using UnityEngine;
using UnityEngine.UI;

public class ToggleButtonController : MonoBehaviour
{
    [SerializeField] private Toggle toggle;
    [SerializeField] private Text statusText;

    private void Start()
    {
        toggle.onValueChanged.AddListener(OnToggleChanged);
        UpdateStatusText(false); // 初期状態表示
    }

    private void OnToggleChanged(bool isOn)
    {
        AudioMannerManager.Instance.ToggleMuteBypass(isOn);
        UpdateStatusText(isOn);
        Handheld.Vibrate(); // バイブレーション
    }

    private void UpdateStatusText(bool isOn)
    {
        statusText.text = $"Mute Bypass: {(isOn ? "ON" : "OFF")}";
    }
}

適宜コンポーネントを作って、ScriptsをAddComponentして、SerializeFieldの部分にコンポーネントをドラッグする。

Androidで、Dexエラーが出たら、Libraryを消してUnity再起動したら直った。

Xcodeでも、ビルド途中で止まるエラーが出たら、Libraryを消してUnity再起動したら直った。