本文档暂不适用于 Web 平台。
5.5 设置收听者的当前位置
。ZEGO Express SDK 从 2.11.0 版本起,新增游戏语音模块,主要包括:范围语音、3D 音效、小队语音。
功能 | 说明 |
---|---|
范围语音 |
房间内的收听者对音频的接收距离有范围限制,若发声者与自己的距离超过该范围,则无法听到声音。为保证语音清晰,附近超过 20 人发声时,只能听到离自己最近的 20 个发声者的声音。
假如设置音频接收距离的最大范围为 R,若发声者离收听者的距离为 r,则:
上图仅以范围语音模式为“全世界”时为例,更多不同模式组合关系下的声音可达情况请参考 5.10.2 设置通用语音模式。 |
3D 音效 |
声音有 3D 空间感,且按距离衰减。 |
通用语音模式 |
玩家可以选择加入小队,并支持在房间内自由切换“全世界”、“仅小队”、“隐秘小队”语音模式。
|
游戏语音功能适用于吃鸡类游戏、元宇宙类场景。
在吃鸡类游戏中,小队语音提供编队功能,在游戏开始前和开始后都可以更换小队,开发者无需关注流分组以及推拉流的实现,直接实现小队语音功能。
在吃鸡游戏和元宇宙场景中,提供 3D 音效能力,在收听发声者音效时,有方向感距离感,让场景感受更真实。
请参考 下载示例源码 获取源码。
相关源码请查看 “lib/topics/AudioAdvanced/range_audio” 目录下的文件。
在实现范围语音之前,请确保:
使用范围语音功能时请务必关注如下注意事项,以免影响接入。
如果您已经使用 ZEGO Express SDK 的实时音视频功能,需要注意以下事项:
上图仅展示了实现游戏语音功能的核心步骤与接口,开发者可以根据业务需要,参考下文文档的详细介绍,实现其他相关接口。
调用 createEngineWithProfile 接口,将申请到到 AppID 传入参数 appID,创建引擎单例对象。
/** 请通过官网注册获取,格式为 123456789 */
int appID = appID;
/** 请通过官网注册获取,格式为 '0123456789012345678901234567890123456789012345678901234567890123'(共64个字符) */
String appSign = appSign;
/** 通用场景接入 */
ZegoScenario scenario = ZegoScenario.General;
var profile = ZegoEngineProfile(appID, scenario, appSign: appSign);
/** 创建引擎 */
ZegoExpressEngine.createEngineWithProfile(profile);
// 在实时音视频 SDK 做纯音频场景时,可以关闭摄像头,此时将不需要摄像头权限和推视频流
// ZegoExpressEngine.instance.enableCamera(false);
调用的 createRangeAudio 方法创建范围语音实例。
var rangAudio = await ZegoExpressEngine.instance.createRangeAudio();
if (rangAudio == null) {
print("创建范围语音实例模块失败");
}
可以根据需要实现 ZegoExpressEngine.onRangeAudioMicrophoneStateUpdate 麦克风设置事件回调,用于监听麦克风的开启状态通知。
// set range audio event handler
ZegoExpressEngine.onRangeAudioMicrophoneStateUpdate = (ZegoRangeAudio rangeAudio, ZegoRangeAudioMicrophoneState state, int errorCode) {
};
传入用户 ID 参数 userID 和 userName 创建 ZegoUser 用户对象后,调用 loginRoom 接口,传入房间 ID 参数 roomID 和用户参数 user,登录房间。
/** 创建用户 */
var user = ZegoUser.id("user1");
/** 开始登录房间 */
ZegoExpressEngine.instance.loginRoom("room1", user);
当用户已成功登录房间后,如果应用异常退出,在重启应用后,开发者需先调用 logoutRoom 接口退出房间,再调用 loginRoom 接口重新登录房间。
开发者可以通过调用 updateSelfPosition 接口,设置听者自身的所在位置和方位,或者在自身方位发生变化时更新自己在世界坐标系中的位置和朝向。
参数名 |
描述 |
position |
自身在世界坐标系中的坐标,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。 |
axisForward |
自身坐标系前轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。 |
axisRight |
自身坐标系右轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。 |
axisUp |
自身坐标系上轴的单位向量,参数是长度为 3 的 float 数组,三个值依次表示前、右、上的坐标值。 |
// 自身在世界坐标系中的坐标,顺序是前、右、上。
var position = Float32List.fromList(<double>[100.0, 100.0, 100.0]);
// 自身坐标系前朝向的单位向量。
var axisForward = Float32List.fromList(<double>[1.0,0.0,0.0]);
// 自身坐标系右朝向的单位向量。
var axisRight = Float32List.fromList(<double>[0.0,1.0,0.0]);
// 自身坐标系上朝向的单位向量。
var axisUp = Float32List.fromList(<double>[0.0,0.0,1.0]);
rangAudio?.updateSelfPosition(position, axisForward, axisRight, axisUp);
登录房间成功后,可以通过调用 updateAudioSource 接口,添加或更新发声者的位置信息。
// 用户在世界坐标系中的坐标,顺序是前、右、上。
var position = Float32List.fromList(<double>[100.0, 100.0, 100.0]);
// 添加/更新用户位置
rangAudio?.updateAudioSource("abc",position);
调用 setAudioReceiveRange 接口设置听者接收音频距离的衰减范围。距离小于 min 时,音量不会随着距离的增加而衰减;距离大于 max 时,将无法听到对方的声音。
// 设置 3D 音效的衰减范围区间 [min, max]
var param = ZegoReceiveRangeParam(0.5, 1.0);
rangeAudio.setAudioReceiveRange(param);
开发者可以将 min 设置为 0、max 为听者接收音频距离的最大范围,即以自身为起点,3D 空间中以设置的距离为立体空间。设置该范围后,在开启 3D 音效的情况下,声音将会随距离的增加而衰减,直至超出所设置的范围,则不再有声音。
// 设置音频接收距离的最大范围,超过该范围的音源声音会听不见
var param = ZegoReceiveRangeParam(0, 1000);
rangeAudio.setAudioReceiveRange(param);
如果不设置音频接收距离,则表示只能接收本小队内的成员声音,无法接收小队外的所有声音。设置后,小队内的语音,不会受到音频接收距离的限制,也不会有 3D 音效。
调用 enableSpatializer 接口设置 3D 音效,enable 取值为 true 时表示开启 3D 音效,此时房间内非小队成员的音频,会随着发声者离自身的距离和方向的变化而产生空间感的变化,为 false 时表示关闭 3D 音效。(可随时开启或关闭)
该功能只对小队以外的人生效。
rangAudio?.enableSpatializer(true);
开发者可以将媒体播放器或音效播放器作为声源,并设置其在世界坐标系中的位置。该功能可应用于在虚拟世界场景中的指定位置播放背景音乐,使其拥有 3D 音效效果。
实现此功能完成以下步骤:
调用 enableSpatializer 接口开启 3D 音效。
设置本地播放器的 3D 音效
媒体播放器:
请参考 媒体播放器 了解如何创建媒体播放器与加载媒体资源。
音效播放器:
请参考 音效文件播放器 了解如何创建音效播放器与加载音效资源。
ZegoAudioEffectPlayState.Playing
后,调用 ZegoAudioEffectPlayer.updatePosition 接口设置正在播放的音效资源在世界坐标系中的位置。// 开启 3D 音效,此步骤为前置条件
rangeAudio?.enableSpatializer(true);
// 设置本地播放器的位置
// 媒体播放器
// 1. 创建媒体播放器
ZegoMediaPlayer mediaPlayer = engine.createMediaPlayer();
// 2. 设置媒体播放器在世界坐标系中的位置
var mediaPlayerPosition = Float32List.fromList(<double>[100.0, 100.0, 100.0]);
mediaPlayer.updatePosition(mediaPlayerPosition);
// 3. 使用媒体播放器加载媒体资源
ZegoMediaPlayerResource resource = ZegoMediaPlayerResource.defaultResource();
resource.loadType = ZegoMultimediaLoadType.FilePath;
resource.filePath = "path";
await mediaPlayer.loadResourceWithConfig(resource);
// 音效播放器
// 1. 创建音效播放器
ZegoAudioEffectPlayer audioEffectPlayer = engine.createAudioEffectPlayer();
// 2. 开始播放音效资源
int effectSoundID = 1;
String path = "path";
audioEffectPlayer.start(effectSoundID, path);
// 3. 收到 [onAudioEffectPlayStateUpdate] 回调状态为 ZegoAudioEffectPlayState.Playing 后
// 设置音效播放器在世界坐标系中的位置
var audioEffectPlayerPosition = Float32List.fromList(<double>[100.0, 100.0, 100.0]);
await audioEffectPlayer.updatePosition(effectSoundID, audioEffectPlayerPosition);
登录房间成功后:
调用 enableMicrophone 接口设置是否开启麦克风,当 enable 取值为 true 时表示开启,此时 SDK 将会自动使用主通道推音频流,为 false 时表示关闭。(可随时开启或关闭)
开发者可以通过监听 onRangeAudioMicrophoneStateUpdate 事件回调,来获取麦克风更新后的状态。
调用 enableSpeaker 接口设置是否开启扬声器,enable 取值为 true 时表示开启,此时将会自动拉取房间内的音频流,为 false 时表示关闭。(可随时开启或关闭)
调用 enableSpeaker 接口后,当超过最大拉流数限制(目前为 20 路)时,会优先拉取小队内成员音频流(需设置小队模式),再拉取世界内距离自身范围最近的音频流。
// 开启麦克风
rangAudio?.enableMicrophone(true);
// 开启扬声器
rangAudio?.enableSpeaker(true);
调用 setTeamID 接口可根据需要设置想要加入的小队 ID(可随时变更 ID),设置 ID 后即可直接加入。加入小队后,与同一小队内队员之间的交流不受范围语音和 3D 音效的限制。
rangAudio?.setTeamID("123");
调用 setRangeAudioMode 接口设置范围语音模式(可随时切换模式),mode 参数取值为 ZegoRangeAudioModeWorld 或 ZegoRangeAudioSecretTeam 时表示可以听到所有处于世界模式的人的声音,取值为 ZegoRangeAudioModeTeam 时表示只能听到同一小队内其他成员的声音。
语音模式 | 参数取值 | 功能描述 |
---|---|---|
全世界 |
World |
设置该模式后,此用户能与小队成员互相通话,且能与范围内其他全世界模式的人互相通话。 |
仅小队 |
Team |
设置该模式后,此用户仅能与小队成员互相通话。 |
隐秘小队 |
SecretTeam |
设置该模式后,此用户能与小队成员互相通话,且能单向接收范围内全世界模式的人的语音。 |
rangAudio?.setRangeAudioMode(ZegoRangeAudioMode.World);
不同范围语音模式下,发声者声音的可接收情况有所不同。
是否在同一小队 | 是否在最大范围内 | 范围语音模式 | A 能否听到 B 的声音 | B 能否听到 A 的声音 |
---|---|---|---|---|
同一小队 |
是 |
全世界(World) |
是 |
是 |
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
否 |
全世界(World) |
是 |
是 |
|
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
不同小队 |
是 |
全世界(World) |
是 |
是 |
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
是 |
||
否 |
全世界(World) |
否 |
否 |
|
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
否 |
是否在同一小队 | 是否在最大范围内 | 范围语音模式 | A 能否听到 B 的声音 | B 能否听到 A 的声音 |
---|---|---|---|---|
同一小队 |
是 |
全世界(World) |
是 |
是 |
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
否 |
全世界(World) |
是 |
是 |
|
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
不同小队 |
是 |
全世界(World) |
否 |
否 |
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
否 |
||
否 |
全世界(World) |
否 |
否 |
|
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
否 |
是否在同一小队 | 是否在最大范围内 | 范围语音模式 | A 能否听到 B 的声音 | B 能否听到 A 的声音 |
---|---|---|---|---|
同一小队 |
是 |
全世界(World) |
是 |
是 |
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
否 |
全世界(World) |
是 |
是 |
|
仅小队(Team) |
是 |
是 |
||
隐秘小队(SecretTeam) |
是 |
是 |
||
不同小队 |
是 |
全世界(World) |
是 |
否 |
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
否 |
||
否 |
全世界(World) |
否 |
否 |
|
仅小队(Team) |
否 |
否 |
||
隐秘小队(SecretTeam) |
否 |
否 |
通过自定义语音模式,您可以自由控制音频的收发逻辑,以完成各种音频互动,示例如下: 假设 A、B、C 为同一小队成员,且 C 在 A 的接收范围内,可通过表格中的配置完成预期的音频体验
用户 | 发声预期 | 收听预期 | 发声模式配置 | 收听模式配置 | 备注 |
---|---|---|---|---|---|
A |
向小队成员和范围内其他用户发送音频 |
收听小队成员和范围内其他用户的音频 |
全部 |
全部 |
A 能同时与 B、C 交流 |
B |
仅向小队成员发送音频 |
仅收听小队成员的音频 |
小队 |
小队 |
B 仅能与 A 交流 |
C |
仅向范围内的用户发送音频 |
仅收听范围内用户的音频 |
世界 |
世界 |
C 仅能与 A 交流 |
rangAudio?.setRangeAudioCustomMode(ZegoRangeAudioSpeakMode.All, ZegoRangeAudioListenMode.All);
当不再使用范围语音模块时可调用 destroyRangeAudio 接口销毁,释放范围语音模块占用的资源。
ZegoExpressEngine.instance.destroyRangeAudio(rangAudio!);
调用 logoutRoom 接口退出房间,退出后将自动关闭麦克风和扬声器(即无法发送自己的音频,也无法收听别人的声音),并清空发声者信息列表。
// 退出房间
ZegoExpressEngine.instance.logoutRoom("roomID");
为保证语音清晰,附近超过 20 人发声时,只能听到离自己最近的 20 个发声者的声音。如果超过 20 人且距离一样,则按照调用 updateAudioSource 接口时,每个 userID 首次传入的先后顺序而定。
范围语音的“范围”指的是收听范围。
小队中的语音是普通连麦的效果,暂无 3D 音效。
范围语音当前使用主路发送音频,如果客户已经使用主路,则会存在冲突。
联系我们
文档反馈