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] 取消勾选,否则打开游戏其它音乐就自动停止了。

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

相关推荐
土丁爱吃大米饭36 分钟前
AIGC工具助力2D游戏美术全流程
aigc·小游戏·游戏开发·ai助力
陈尕六3 天前
从零开始的 Godot 之旅 — EP6:更优雅地实现角色场景
godot·游戏开发
AI广告侠4 天前
游戏开发者的AI革命:从灵感枯竭到效率狂飙,我的日常链路被AI重塑了 ——一个独立游戏开发者的AI工具箱实战指南
游戏·游戏开发
总有刁民想爱朕ha4 天前
Python自动化从入门到实战(23):Python打地鼠游戏开发
开发语言·python·游戏开发
腾讯云云开发5 天前
CloudBase + AI 游戏开发新范式,3小时极速开发
ai编程·游戏开发·小程序·云开发
ZHOUYUANN6 天前
我用JavaScript复刻了某宝的小游戏动物大迁徙消消乐
前端·javascript·游戏开发
gopyer8 天前
180课时吃透Go语言游戏后端开发11:Go语言中的并发编程
golang·go·游戏开发·并发编程