Untiy 如何检测Android Ios 是否正在播放音乐

最近有玩家发来邮件,对我们的游戏提了一个要求。就是他想一边收听其它APP播放的音乐一边玩我们的游戏,而又不想我们游戏的背景音乐扰乱他正在收听的音乐。要实现这个需求,其实就是要检测手机是否有其它APP正在使用系统播放音乐。翻了一遍Unity的音频管理组件,发现没有相关接口直接可以探测手机是否正在被其它应用播放音乐。这个就有点小麻烦了,还得针对各个移动平台写原生方法进行检测。接下来我们就一起来探讨一下怎么实现这个玩家提出来的需求。

首先我们实现Android平台的。

先下载一个Android studio,建立一个空Activity的模块,并添加一个MusicPlayer类,如下图:

我们的重点是MusicPlayer类,类的代码如下:

ini 复制代码
package com.music.checkplay;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class MusicPlayer {
    private Activity _unityActivity;
    private Context _context;
    private Class<?> _unityPlayer;
    private Method _unitySendMessage;
    private AudioManager _audio;
    public void Init()
    {
        if(_unityActivity == null)
        {
            try {
                _unityPlayer = Class.forName("com.unity3d.player.UnityPlayer");
                Activity avt = (Activity) _unityPlayer.getDeclaredField("currentActivity").get(_unityPlayer);
                _unityActivity = avt;
                _context = avt;
                _audio = (AudioManager)_unityActivity.getSystemService(Service.AUDIO_SERVICE);
            }
            catch (ClassNotFoundException e){
                System.out.println(e.getMessage());
            }
            catch (IllegalAccessException e)
            {
                System.out.println(e.getMessage());
            }
            catch (NoSuchFieldException e)
            {
                System.out.println(e.getMessage());
            }
        }
    }

    private boolean CallUnity(String goName, String functionName, Object... args)
    {
        try {
            if(_unitySendMessage == null)
                _unitySendMessage = _unityPlayer.getMethod("UnitySendMessage", String.class, String.class, Object.class);
            _unitySendMessage.invoke(_unityPlayer, goName, functionName, args);
            return true;
        } catch (IllegalAccessException e)
        {
            System.out.println(e.getMessage());
        }
        catch (NoSuchMethodException e)
        {
            System.out.println(e.getMessage());
        }
        catch (InvocationTargetException e)
        {
            System.out.println(e.getMessage());
        }
        return false;
    }

    ///当前系统音乐是否处于待机状态
    public boolean IsMusicActive()
    {
        return _audio.isMusicActive();
    }

     //是否有音乐在播放
    public boolean IsMusicPlay()
    {
        List<AudioPlaybackConfiguration> apcs = _audio.getActivePlaybackConfigurations();
        for (AudioPlaybackConfiguration config : apcs)
        {
            int conty = config.getAudioAttributes().getContentType();
            if(conty == 2)
                return  true;
        }
        return false;
    }
}

Init() 方法通过反射方法获取UnityActivity, 并把各类变量保存下来。

(AudioManager)_unityActivity.getSystemService(Service.AUDIO_SERVICE) 这一行代码是获取android audio system service, 后面的音频占用输出检测主要是通过这个服务进行检测的。

方法IsMusicActive()是检测当前Music类型的音频是否被激活。AudioManager.isMusicActive() 方法无论是否有其它应用正在播放音乐,这个方法始终返回ture。靠这个方法检测音乐或曲目正在播放显然是不靠谱的。个人对这个方法的应用理解,更倾向于是检测音乐频道是否处于待机状态。

IsMusicPlay() 方法是通过捕获音频内容的属性数据分析出是否正在播放音乐类内容,如果是音乐类内容,则contentType类型返回值为2. 后面我们主要用这个方法来检测系统是否正在播放着音乐类内容。

我们把模块输出为 aar包,把它复制到unity plugins文件夹下面,这样android平台的原生检测方法就算完成了。如下图:

接下来我们继续解决IOS 平台的检测音乐播放问题。

打开XCode, 新建一个 checkPlay.mm文件,输入如下代码:

arduino 复制代码
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

extern "C"
{
    //是否有音乐在播放
    bool IsMusicPlay()
    {
        //bool isPlaying = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
        bool playing = AVAudioSession.sharedInstance.isOtherAudioPlaying;
        return playing;   
    }
}

然后把这个checkPlay.mm文件复制到unity plugins文件夹下面,如下图:

到此android 和 ios 的原生方法已全部完成,接下来的部分就是unity C# 部分对各平台原生方法的调用了。

unity 下 新建一个原生方法管理类,如NativeMgr.cs c# 类,类的代码如下:

csharp 复制代码
using System;
using System.Collections.Generic;
using UnityEngine;
#if (UNITY_IOS)
using System.Runtime.InteropServices;
#endif

namespace Gamelogic
{    
    public class NativeMgr
    {
#if (UNITY_ANDROID)
        private static AndroidJavaObject _musicPlayer;
#elif (UNITY_IOS)
        [DllImport("__Internal")] private static extern bool IsMusicPlay();
#endif
        private static bool _init = false;
        public static void Init()
        {
            if(_init) return;

#if (UNITY_ANDROID)
            _musicPlayer = new AndroidJavaObject("com.music.checkplay.MusicPlayer");
            _musicPlayer.Call("Init");

#endif
            _init = true;
        }

        /// <summary>
        /// 是否有音乐在播放
        /// </summary>
        /// <returns></returns>
        public static bool HasMusicPlay()
        {
#if(UNITY_ANDROID)
            return _musicPlayer.Call<bool>("IsMusicPlay");
#elif (UNITY_IOS)
            return IsMusicPlay();
#endif
        }
    }
}

类内封装了android 和 ios 不同的调用原生代码逻辑,使得业务层可以忽略夸平台内容。

业务层的使用如下:

csharp 复制代码
        /// <summary>
        /// 进入游戏
        /// </summary>
        public void EnterGame()
        {   
            NativeMgr.Init();
            AudioMgr.Instance.PlayBg(ResConfig.Audio_bg);

            if (NativeMgr.HasMusicPlay())
            {
                LogMgr.Log($"{nameof(EnterGame)} 有其它app在播放音乐,将停止游戏内背景音乐");
                AudioMgr.Instance.StopBg();
            }

            LangMgr.LoadLang(LangMgr.CurLang);          
            LangMgr.OnLangChanged = OnChangedLang;
        }

注意,打包游戏时,记得把PlayerSetting [Mute other audio sources] 取消勾选,否则打开游戏其它音乐就自动停止了。

至此分平台检测是否有音乐正在播放的需求已经实现。

相关推荐
北冥没有鱼啊2 天前
UE 像素和线框盒子 材质
c++·ue5·游戏开发·虚幻·材质
大飞pkz3 天前
【Unity】使用XLua进行热修复
unity·c#·游戏引擎·lua·游戏开发·xlua·lua热修复
工藤新一¹4 天前
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 19)
开发语言·c++·游戏引擎·游戏开发·sdl
大飞pkz7 天前
【Unity】使用XML进行数据读存的简单例子
xml·unity·c#·游戏引擎·游戏开发·数据读写
大飞pkz7 天前
【Unity】如何解决UI中的Button无法绑定带参数方法的问题
ui·unity·游戏引擎·游戏开发·开发记录·button绑定
大飞pkz7 天前
【Unity】使用LitJson保存和读取数据的例子
unity·游戏引擎·游戏开发·数据保存和读取·游戏中的数据处理·类似jsonunility
大模型铲屎官8 天前
Unity C# 与 Shader 交互入门:脚本动态控制材质与视觉效果 (含 MaterialPropertyBlock 详解)(Day 38)
c语言·unity·c#·交互·游戏开发·材质·shader
Thomas游戏开发9 天前
Unity3D Timeline扩展与自定义事件处理
前端框架·unity3d·游戏开发
龙智DevSecOps解决方案10 天前
CI/CD解决方案TeamCity在游戏开发中的应用价值与优势分析
ci/cd·游戏开发·jetbrains·持续集成·teamcity
工藤新一¹10 天前
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 14)
开发语言·c++·游戏引擎·游戏开发·sdl·实践项目