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例
- Android実機で、サイレントモードを長押しして、サイレントモード設定を開く
- アラームとその他の割り込み/メディアサウンドを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再起動したら直った。