畅直播
  • iOS
  • Android : Java
  • macOS
  • Windows
  • Web
  • Flutter
  • 产品简介
    • 概述
    • 发布日志
    • 基本概念
    • 产品优势
    • 应用场景
    • 限制说明
    • 升级指南
  • 计费说明
  • 下载
  • 快速开始
  • 直播推流
  • 直播拉流
  • 通信能力
  • 房间能力
  • 音频能力
  • 视频能力
  • 直播能力
  • 其他能力
  • 最佳实践
  • 客户端 API
  • 服务端 API
  • 常用错误码
  • 常见问题
  • 文档中心
  • 畅直播
  • 通信能力
  • 游戏语音

游戏语音

更新时间:2024-02-22 11:06

1 功能简介

1.1 概念解释

  • 范围:收听者接收音频的范围。
  • 方位:指收听者在游戏世界坐标中的位置和朝向,详情可参考 5.5 设置收听者的当前位置
  • 收听者:房间内接收音频的用户
  • 发声者:房间内发送音频的用户。

1.2 功能描述

ZEGO Express SDK 从 2.11.0 版本起,新增游戏语音模块,主要包括:范围语音、3D 音效、小队语音。

功能 说明
范围语音
房间内的收听者对音频的接收距离有范围限制,若发声者与自己的距离超过该范围,则无法听到声音。为保证语音清晰,附近超过 20 人发声时,只能听到离自己最近的 20 个发声者的声音。

假如设置音频接收距离的最大范围为 R,若发声者离收听者的距离为 r,则:

  • 当 r < R 时,表示发声者在正常范围内,收听者可以听到声音。
  • 当 r ≥ R 时,表示发声者超出了最大范围,收听者无法听到声音。

上图仅以范围语音模式为“全世界”时为例,更多不同模式组合关系下的声音可达情况请参考 5.10.2 设置通用语音模式

3D 音效
声音有 3D 空间感,且按距离衰减。
通用语音模式
玩家可以选择加入小队,并支持在房间内自由切换“全世界”、“仅小队”、“隐秘小队”语音模式。

  • 全世界:玩家能与世界范围内其他玩家互相通话,同时能够与队友相互通话。
  • 仅小队:玩家仅与队友互相通话。
  • 隐秘小队:玩家可与队友互相通话,能且仅能听到世界范围内其他玩家的语音。

1.3 适用场景

游戏语音功能适用于吃鸡类游戏、元宇宙类场景。

在吃鸡类游戏中,小队语音提供编队功能,在游戏开始前和开始后都可以更换小队,开发者无需关注流分组以及推拉流的实现,直接实现小队语音功能。

在吃鸡游戏和元宇宙场景中,提供 3D 音效能力,在收听发声者音效时,有方向感距离感,让场景感受更真实。

2 示例源码下载

请参考 下载示例源码 获取源码。

相关源码请查看 “/ZegoExpressExample/Examples/AdvancedAudioProcessing/RangeAudio” 目录下的文件。

3 前提条件

在实现范围语音之前,请确保:

4 注意事项

使用范围语音功能时请务必关注如下注意事项,以免影响接入。

如果您已经使用 ZEGO Express SDK 的实时音视频功能,需要注意以下事项:

5 使用步骤

/Pics/Express/RangeVoice_android.png

上图仅展示了实现游戏语音功能的核心步骤与接口,开发者可以根据业务需要,参考下文文档的详细介绍,实现其他相关接口。

5.1 创建引擎

调用 createEngine 接口,将申请到到 AppID 和 AppSign 传入参数 “appID” 和 “appSign”,创建引擎单例对象。引擎当前只支持同时创建一个实例,超出后将返回 null。

/** 定义 SDK 引擎对象 */
ZegoExpressEngine engine;

ZegoEngineProfile profile = new ZegoEngineProfile();
/** 请通过官网注册获取,格式为 123456789L */
profile.appID = appID;
/** 64个字符,请通过官网注册获取,格式为"0123456789012345678901234567890123456789012345678901234567890123" */
profile.appSign = appSign;
/** 通用场景接入,请根据实际情况选择合适的场景 */
profile.scenario = ZegoScenario.DEFAULT;
/** 设置app的application 对象 */
profile.application = getApplication();
/** 创建引擎 */
engine = ZegoExpressEngine.createEngine(profile, null);

5.2 创建范围语音模块

调用的 createRangeAudio 方法创建范围语音实例。

ZegoRangeAudio rangeAudio = engine.createRangeAudio();
if (rangeAudio == null) {
    printf("创建范围语音实例模块失败");
}

5.3 监听范围语音事件回调

可以根据需要调用 setEventHandler 接口为麦克风设置事件回调,用于监听麦克风的开启状态 onRangeAudioMicrophoneStateUpdate 通知。

// set range audio event handler
rangeAudio.setEventHandler(new IZegoRangeAudioEventHandler() {
    @Override
    public void onRangeAudioMicrophoneStateUpdate(ZegoRangeAudio rangeAudio, ZegoRangeAudioMicrophoneState state, int errorCode) {
        super.onRangeAudioMicrophoneStateUpdate(rangeAudio, state, errorCode);
        AppLogger.getInstance().callApi("microphone state update. state: %s, errorCode: %d", state, errorCode);
    }
});

5.4 登录房间

传入用户 ID 参数 userID 和 userName 创建 ZegoUser 用户对象后,调用 loginRoom 接口,传入房间 ID 参数 roomID 和用户参数 user,登录房间。

  • 同一个 AppID 内,需保证 roomID 全局唯一。
  • 同一个 AppID 内,需保证 userID 全局唯一,建议开发者将其设置成一个有意义的值,可将 userID 与自己业务账号系统进行关联。
  • userID 与 userName 不能为空,否则会导致登录房间失败。
/** 创建用户 */
ZegoUser user = new ZegoUser("user1");

/** 开始登录房间 */
engine.loginRoom("room1", user);

当用户已成功登录房间后,如果应用异常退出,在重启应用后,开发者需先调用 logoutRoom 接口退出房间,再调用 loginRoom 接口重新登录房间。

5.5 设置收听者的当前位置

开发者可以通过调用 updateSelfPosition 接口,设置听者自身的所在位置和方位,或者在自身方位发生变化时更新自己在世界坐标系中的位置和朝向。

  • 在调用 enableSpeaker 打开扬声器之前如果没有调用该接口设置位置信息,则无法接收除小队以外其他人的声音。
  • 自身坐标系三个轴的坐标值可以通过第三方 3D 引擎的旋转角度换算矩阵得到。
参数名
描述
position
自身在世界坐标系中的坐标,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。
axisForward
自身坐标系前轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。
axisRight
自身坐标系右轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。
axisUp
自身坐标系上轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。
// 自身在世界坐标系中的坐标,顺序是前、右、上。
float[] position = new float[]{100.0, 100.0, 100.0};
// 自身坐标系前朝向的单位向量。
float[] axisForward = new float[]{1.0,0.0,0.0};
// 自身坐标系右朝向的单位向量。
float[] axisRight = new float[]{0.0,1.0,0.0};
// 自身坐标系上朝向的单位向量。
float[] axisUp = new float[]{0.0,0.0,1.0};

rangeAudio.updateSelfPosition(position, axisForward, axisRight, axisUp);

5.6 添加或更新发声者位置信息

登录房间成功后,需要通过调用 updateAudioSource 接口,添加或更新发声者的位置信息。

  • 全世界模式下:需要更新房间内,收听者和所有发声者的位置。隐秘小队模式下:需要更新房间内,所有在音频接收范围内,且为全世界模式的发声者的位置。如果未设置发声者位置,或者发声者超出收听者范围,会出现听不到声音的情况。
  • 这里发声者指的是房间内其他人,收听者指的是自己。
  • userID:为房间内其他发声用户的 ID。
  • position:发声者在世界坐标系中的坐标,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。
// 用户在世界坐标系中的坐标,顺序是前、右、上。
float[] position = new float[]{100.0, 100.0, 100.0};
// 添加/更新用户位置
rangeAudio.updateAudioSource("abc",position);

5.7 (可选)设置音频接收距离

请根据业务需要,选择是否设置音频接收距离。如果不设置,默认表示仅能接收本小队的成员声音。

调用 setAudioReceiveRange 接口设置听者接收音频距离的最大范围,即以自身为起点,3D 空间中以设置的距离为立体空间。设置该范围后,在开启 3D 音效的情况下,声音将会随距离的增加而衰减,直至超出所设置的范围,则不再有声音。

// 设置音频接收距离的最大范围,超过该范围的音源声音会听不见
rangeAudio.setAudioReceiveRange(1000);

同时您也可以通过 setAudioReceiveRange 接口进一步控制衰减范围。距离小于 min 时,音量不会随着距离的增加而衰减;距离大于 max 时,将无法听到对方的声音。

// 设置 3D 音效的衰减范围区间 [min, max] 
ZegoReceiveRangeParam param = new ZegoReceiveRangeParam();
param.min = reciveRangeMin;
param.max = reciveRangeMax;
rangeAudio.setAudioReceiveRange(param);

如果不设置音频接收距离,则表示只能接收本小队内的成员声音,无法接收小队外的所有声音。设置后,小队内的语音,不会受到音频接收距离的限制,也不会有 3D 音效。

5.8 (可选)实现 3D 音效

请根据业务需要,选择是否开启 3D 音效、以及是否设置本地播放器的 3D 音效。

5.8.1 开启 3D 音效

调用 enableSpatializer 接口设置 3D 音效,enable 取值为 true 时表示开启 3D 音效,此时房间内非小队成员的音频,会随着发声者离自身的距离和方向的变化而产生空间感的变化,为 false 时表示关闭 3D 音效。(可随时开启或关闭)

该功能只对小队以外的人生效。

rangeAudio.enableSpatializer(true);

5.8.2 设置本地播放器的 3D 音效

开发者可以将媒体播放器或音效播放器作为声源,并设置其在世界坐标系中的位置。该功能可应用于在虚拟世界场景中的指定位置播放背景音乐,使其拥有 3D 音效效果。

  1. 调用 enableSpatializer 接口开启 3D 音效。

  2. 设置本地播放器的 3D 音效

// 开启 3D 音效,此步骤为前置条件
rangeAudio.enableSpatializer(true);

// 设置本地播放器的位置
// 媒体播放器
// 1. 创建媒体播放器
ZegoMediaPlayer mediaPlayer = engine.createMediaPlayer();
// 2. 设置媒体播放器在世界坐标系中的位置
float [] mediaPlayerPosition = new float[3];
mediaPlayer.updatePosition(mediaPlayerPosition);
// 3. 使用媒体播放器加载媒体资源
ZegoMediaPlayerResource resource = new ZegoMediaPlayerResource();
resource.loadType = ZegoMultimediaLoadType.FILE_PATH;
resource.filePath = "path";
mediaPlayer.loadResourceWithConfig(resource, new IZegoMediaPlayerLoadResourceCallback() {
    @Override
    public void onLoadResourceCallback(int errorCode) {
        if (errorCode == 0)
            mediaPlayer.start();
    }
});

// 音效播放器
// 1. 创建音效播放器
ZegoAudioEffectPlayer audioEffectPlayer = engine.createAudioEffectPlayer();
// 2. 开始播放音效资源
int effectSoundID = 1;
String path = "path";
audioEffectPlayer.start(effectSoundID, path, null);
// 3. 收到 [onAudioEffectPlayStateUpdate] 回调状态为 ZegoAudioEffectPlayState.Playing 后
//    设置音效播放器在世界坐标系中的位置
float [] audioEffectPlayerPosition = new float[3];
audioEffectPlayer.updatePosition(effectSoundID, audioEffectPlayerPosition);

5.9 开启麦克风、扬声器

登录房间成功后:

  • 调用 enableMicrophone 接口,设置是否开启麦克风。当 enable 取值为 true 时表示开启,此时 SDK 将会自动使用主通道推音频流,为 false 时表示关闭。(可随时开启或关闭)

    开发者可以通过监听 onRangeAudioMicrophoneStateUpdate 事件回调,获取麦克风更新后的状态。

  • 调用 enableSpeaker 接口,设置是否开启扬声器。enable 取值为 true 时表示开启,此时将会自动拉取房间内的音频流,为 false 时表示关闭。(可随时开启或关闭)

调用 enableSpeaker 接口后,当超过最大拉流数限制(目前为 20 路)时,会优先拉取小队内成员音频流(需设置小队模式),再拉取世界内距离自身范围最近的音频流。

// 开启麦克风
rangeAudio.enableMicrophone(true);
// 开启扬声器
rangeAudio.enableSpeaker(true);

5.10 (可选)加入小队、设置语音模式

请根据业务需要,选择是否实现游戏小队、小队语音等功能。

5.10.1 加入小队

调用 setTeamID 接口可根据需要设置想要加入的小队 ID(可随时变更 ID),设置 ID 后即可直接加入。加入小队后,与同一小队内队员之间的交流不受范围语音和 3D 音效的限制。

rangeAudio.setTeamID("123");

5.10.2 设置通用语音模式

调用 setRangeAudioMode 接口设置范围语音模式(可随时切换模式),mode 参数取值为 ZegoRangeAudioModeWorld 或 ZegoRangeAudioSecretTeam 时表示可以听到所有处于世界模式的人的声音,取值为 ZegoRangeAudioModeTeam 时表示只能听到同一小队内其他成员的声音。

语音模式 参数取值 功能描述
全世界
WORLD
设置该模式后,此用户能与小队成员互相通话,且能与范围内其他全世界模式的人互相通话。
仅小队
TEAM
设置该模式后,此用户仅能与小队成员互相通话。
隐秘小队
SECRET_TEAM
设置该模式后,此用户能与小队成员互相通话,且能单向接收范围内全世界模式的人的语音。
rangeAudio.setRangeAudioMode(ZegoRangeAudioMode.WORLD);

不同范围语音模式下,发声者声音的可接收情况有所不同。

  • 假设 A 用户的模式为 “全世界”,则 B 用户在不同范围语音模式下的声音可接收情况如下:
是否在同一小队 是否在最大范围内 范围语音模式 A 能否听到 B 的声音 B 能否听到 A 的声音
同一小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
不同小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
  • 假设 A 用户的模式为 “仅小队”,则 B 用户在不同范围语音模式下的声音可接收情况如下:
是否在同一小队 是否在最大范围内 范围语音模式 A 能否听到 B 的声音 B 能否听到 A 的声音
同一小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
不同小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
  • 假设 A 用户的模式为 “隐秘小队”,则 B 用户在不同范围语音模式下的声音可接收情况如下:
是否在同一小队 是否在最大范围内 范围语音模式 A 能否听到 B 的声音 B 能否听到 A 的声音
同一小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
不同小队
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)
全世界(World)
仅小队(Team)
隐秘小队(SecretTeam)

5.10.3 设置自定义语音模式

  • 自定义语音模式和通用语音模式不可以同时使用。
  • 如需使用自定义语音模式,请确保游戏语音内的所有线上用户使用的 Express SDK 为 3.3.0 及以上版本。

通过自定义语音模式,您可以自由控制音频的收发逻辑,以完成各种音频互动,示例如下: 假设 A、B、C 为同一小队成员,且 C 在 A 的接收范围内,可通过表格中的配置完成预期的音频体验

用户 发声预期 收听预期 发声模式配置 收听模式配置 备注
 A
向小队成员和范围内其他用户发送音频
收听小队成员和范围内其他用户的音频
全部
全部
A 能同时与 B、C 交流
B
仅向小队成员发送音频
仅收听小队成员的音频
小队
小队
B 仅能与 A 交流
C
仅向范围内的用户发送音频
仅收听范围内用户的音频
世界
世界
C 仅能与 A 交流
rangeAudio.setRangeAudioCustomMode(ZegoRangeAudioSpeakMode.ALL, ZegoRangeAudioListenMode.ALL);

5.11 销毁范围语音模块

当不再使用范围语音模块时可调用 destroyRangeAudio 接口销毁,释放范围语音模块占用的资源。

engine.destroyRangeAudio(rangeAudio);

5.12 退出房间

调用 logoutRoom 接口退出房间,退出后将自动关闭麦克风和扬声器(即无法发送自己的音频,也无法收听别人的声音),并清空发声者信息列表。

// 退出房间
engine.logoutRoom("roomID");

6 常见问题

  1. 收听范围内的流最多支持同时拉多少路?

为保证语音清晰,附近超过 20 人发声时,只能听到离自己最近的 20 个发声者的声音。如果超过 20 人且距离一样,则按照调用 updateAudioSource 接口时,每个 userID 首次传入的先后顺序而定。

  1. 范围语音的“范围”指的是收听范围还是发声范围?

范围语音的“范围”指的是收听范围。

  1. 小队语音中的成员连麦有 3D 音效吗?

小队中的语音是普通连麦的效果,暂无 3D 音效。

  1. 若本身就在调用推流接口,此时需要使用范围语音会存在冲突吗?

范围语音当前使用主路发送音频,如果客户已经使用主路,则会存在冲突。

本篇目录