本文介绍如何在你的 iOS app 里实现元语聊的重要功能。
元语聊场景提供语聊者和游客两种默认角色,角色权限说明如下:
语聊者 | 游客 | |
---|---|---|
发布音频流 | ✔ | ✘ |
订阅远端用户的音频流 | ✔ | ✔ |
本地渲染虚拟形象 | ✔ | ✔ |
同步虚拟形象给远端用户 | ✔ | ✘ |
互动(占座) | ✔ | ✘ |
实现元语聊的总体流程如下:
开始前,请确保你的开发环境满足以下条件:
参考以下步骤开通元语聊服务:
搭建元语聊场景需要使用声网 RTC 4.0.1 元语聊特殊版 SDK。该版本是官网声网 RTC SDK 4.0.1 的分支版本,主要新增了一些为元语聊场景定制的功能。
参考以下步骤集成声网 RTC 4.0.1 元语聊特殊版 SDK:
联系销售获取声网 RTC 4.0.1 元语聊特殊版 SDK,并解压。
打开解压文件,将 libs
路径下的所有文件拷贝到你的项目路径下。
打开 Xcode,添加对应动态库,确保添加的动态库 Embed 属性设置为 Embed & Sign。
本节介绍在集成声网 RTC 4.0.1 元语聊特殊版 SDK 后,如何实现元语聊核心业务模块的功能。
实现流程中需要用到声网 SDK 的以下类:
AgoraRtcEngineKit
类:提供实时音视频功能。AgoraMetachatKit
、AgoraMetachatScene
和 AgoraLocalUserAvatar
类:提供元语聊的核心功能相关方法。AgoraMetachatEventDelegate
和 AgoraMetachatSceneEventDelegate
类:提供元语聊的核心功能相关回调。创建 3D 场景之前,你需要进行初始化、下载场景资源。
你可以参考以下示例代码:
// Objective-c
// 创建并初始化 AgoraRtcEngineKit
AgoraRtcEngineConfig *rtcEngineConfig = [[AgoraRtcEngineConfig alloc] init];
rtcEngineConfig.appId = @"";
rtcEngineConfig.areaCode = AgoraAreaCodeTypeGlobal;
AgoraRtcEngineKit *rtcEngine = [AgoraRtcEngineKit sharedEngineWithConfig:_rtcEngineConfig
delegate:self];
[rtcEngine enableAudio];
[rtcEngine enableVideo];
[rtcEngine setChannelProfile:AgoraChannelProfileLiveBroadcasting];
[rtcEngine setClientRole:AgoraClientRoleBroadcaster];
// 创建并初始化 AgoraMetachatKit
NSString *cachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
metachatConfig.localDownloadPath = cachesPath;
metachatConfig.rtcEngine = rtcEngine;
AgoraMetachatKit *metachatKit = [AgoraMetachatKit sharedMetachatWithConfig:metachatConfig];
// Swift
// 创建并初始化 AgoraRtcEngineKit
let rtcEngineConfig = AgoraRtcEngineConfig()
rtcEngineConfig.appId = ""
rtcEngineConfig.areaCode = .global
rtcEngine = AgoraRtcEngineKit.sharedEngine(with: rtcEngineConfig, delegate: self)
rtcEngine?.setChannelProfile(.liveBroadcasting)
rtcEngine?.setClientRole(.broadcaster)
// 创建并初始化 AgoraMetachatKit
let metachatConfig = AgoraMetachatConfig()
metachatConfig.appId = ""
metachatConfig.rtmToken = ""
metachatConfig.userId = ""
metachatConfig.delegate = self
let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true)
metachatConfig.localDownloadPath = paths.first!
metachatConfig.rtcEngine = rtcEngine
let metachatKit = AgoraMetachatKit.sharedMetachatWithConfig(_: metachatConfig)
// Objective-c
// 获取场景资源列表
[metachatKit getSceneInfos];
// 获取场景资源列表回调
- (void)onGetSceneInfosResult:(NSMutableArray* _Nonnull)sceneInfos errorCode:(NSInteger)errorCode;
// 判断资源是否需要下载(本地没有资源或者资源有更新则需要下载)
[metachatKit isSceneDownloaded:sceneId];
// 下载资源
[metachatKit downloadScene:sceneId];
// 资源下载进度回调
- (void)onDownloadSceneProgress:(NSInteger)sceneId
progress:(NSInteger)progress
state:(AgoraMetachatDownloadStateType)state;
// 取消下载
[metachatKit cancelDownloadScene:sceneId];
// Swift
// 获取场景资源列表
metachatKit?.getSceneInfos()
// 获取场景资源列表回调
func onGetSceneInfosResult(_ scenes: NSMutableArray, errorCode: Int)
// 判断资源是否需要下载(本地没有资源或者资源有更新则需要下载)
metachatKit?.isSceneDownloaded(sceneId)
// 下载资源
metachatKit?.downloadScene(sceneId)
// 资源下载进度回调
func onDownloadSceneProgress(_ sceneId: Int, progress: Int, state: AgoraMetachatDownloadStateType)
// 取消下载
metachatKit?.cancelDownloadScene(sceneId)
搭建元语聊首先需要创建 3D 场景。
你可以参考以下示例代码:
// Objective-c
// 创建场景
AgoraMetachatSceneConfig *config = [[AgoraMetachatSceneConfig alloc] init];
config.delegate = self;
[metachatKit createScene:config];
// createScene 结果回调,metaChatScene 对象的值为回调获取
- (void)onCreateSceneResult:(AgoraMetachatScene *_Nullable)scene
errorCode:(NSInteger)errorCode;
// Swift
// 创建场景
let config = AgoraMetachatSceneConfig()
config.delegate = self
metachatKit?.createScene(config)
// createScene 结果回调,metaChatScene 对象的值为回调获取
func onCreateSceneResult(_ scene: AgoraMetachatScene?, errorCode: Int)
进入 3D 场景之前,你可以先设置好用户的基本信息、模型信息、装扮信息、捏脸信息等。
你可以参考以下示例代码:
// Objective-c
// 创建场景渲染所需的视图
UIView<AgoraMetaViewProtocol> *renderView = [metachatScene createRenderView:AgoraMetachatVenderTypeUnity
region:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
renderView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// user 信息
AgoraMetachatUserInfo *userInfo = [[AgoraMetachatUserInfo alloc] init];
userInfo.userId = @"";
userInfo.userName = @"";
userInfo.userIconUrl = @"";
// avatar 信息
AgoraMetachatAvatarModelInfo *avatarInfo = [[AgoraMetachatAvatarModelInfo alloc] init];
for (AgoraMetachatBundleInfo *info in sceneInfo.bundles) {
if (info.bundleType == AgoraMetachatBundleTypeAvatar) {
avatarInfo.bundleCode = info.bundleCode;
break;
}
}
avatarInfo.localVisible = true;
avatarInfo.remoteVisible = true;
avatarInfo.syncPosition = true;
// 换装信息
AgoraMetachatDressInfo *dressInfo = [[AgoraMetachatDressInfo alloc] init];
dressInfo.extraCustomInfo = [dressStr dataUsingEncoding:NSUTF8StringEncoding];
// 设置 userInfo(用户信息)、modelInfo(模型信息)、dressInfo(换装信息)
AgoraMetachatLocalUserAvatar *localUserAvatar = [metachatScene getLocalUserAvatar];
[localUserAvatar setUserInfo:userInfo];
[localUserAvatar setModelInfo:avatarInfo];
[localUserAvatar setDressInfo:dressInfo];
// 进入场景所需信息
AgoraMetachatEnterSceneConfig *enterSceneConfig = [[AgoraMetachatEnterSceneConfig alloc] init];
enterSceneConfig.roomName = @"";
enterSceneConfig.sceneView = renderView;
enterSceneConfig.sceneId = sceneInfo.sceneId;
enterSceneConfig.extraCustomInfo = [extraInfo dataUsingEncoding:NSUTF8StringEncoding];
// 进入场景
[metachatScene enterScene:enterSceneConfig];
// enterScene结果回调
- (void)metachatScene:(AgoraMetachatScene *_Nonnull)scene onEnterSceneResult:(NSInteger)errorCode;
// Swift
// 创建场景渲染所需的视图
let renderView = metachatScene?.createRenderView(
AgoraMetachatVenderType.unity,
region: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT))
renderView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// user 信息
let userInfo = AgoraMetachatUserInfo.init()
userInfo?.userId = ""
userInfo?.userName = ""
userInfo?.userIconUrl = ""
// avatar 信息
let avatarInfo = AgoraMetachatAvatarModelInfo.init()
for info in sceneInfo.bundles {
if info.bundleType == .avatar {
avatarInfo.bundleCode = info.bundleCode
break
}
}
avatarInfo.localVisible = true
avatarInfo.remoteVisible = true
avatarInfo.syncPosition = true
// 换装信息
let dressInfo = AgoraMetachatDressInfo()
dressInfo.extraCustomInfo = dressStr!.data(using: String.Encoding.utf8)
// 设置 userInfo(用户信息)、modelInfo (模型信息)、dressInfo (换装信息)
localUserAvatar = metachatScene?.getLocalUserAvatar()
localUserAvatar?.setUserInfo(userInfo)
localUserAvatar?.setModelInfo(avatarInfo)
localUserAvatar?.setDressInfo(dressInfo)
// 设置进入场景所需要的配置信息
let enterSceneConfig = AgoraMetachatEnterSceneConfig()
enterSceneConfig.roomName = ""
enterSceneConfig.sceneView = renderView
enterSceneConfig.sceneId = sceneInfo.sceneId
enterSceneConfig.extraCustomInfo = extraInfo!.data(using: String.Encoding.utf8)
// 进入场景
metachatScene?.enter(enterSceneConfig)
// enterScene 结果回调
func metachatScene(_ scene: AgoraMetachatScene, onEnterSceneResult errorCode: Int)
游客可以上麦成为语聊者,从而发布音频流、向所有人展示自己的虚拟形象。
你可以参考以下示例代码:
// Objective-c
// 是否为语聊者
BOOL isBroadcaster = (role == AgoraClientRole.broadcaster);
// 设置 avatar 信息
avatarInfo.localVisible = true;
avatarInfo.remoteVisible = isBroadcaster;
avatarInfo.syncPosition = isBroadcaster;
localUserAvatar = [metachatScene getLocalUserAvatar];
[localUserAvatar setModelInfo: avatarInfo];
[localUserAvatar applyInfo];
// Swift
// 是否为语聊者
var isBroadcaster = (role == AgoraClientRole.broadcaster)
// 设置 avatar 信息
avatarInfo.localVisible = true
avatarInfo.remoteVisible = isBroadcaster
avatarInfo.syncPosition = isBroadcaster
localUserAvatar = metachatScene?.getLocalUserAvatar()
localUserAvatar?.setModelInfo(avatarInfo)
localUserAvatar?.applyInfo()
在 3D 场景中,游客和语聊者都可以对自己的虚拟形象实时换装。详见换装和捏脸。
为用户的音频流和背景音乐添加空间音效,目前仅支持本地直角坐标系计算方案,实现方法详见空间音效。
为 3D 场景添加视频播放功能,可以实现一起看视频、一起 KTV 等玩法。目前仅支持播放本地视频文件,实现方法详见实时共赏影音。
切换场景时,必须先退出当前的 3D 场景,然后创建新的 3D 场景。
// Objective-c
// 退出场景
[rtcEngine leaveChannel:nil];
[metachatScene leaveScene];
// 退出场景回调
- (void)metachatScene:(AgoraMetachatScene *_Nonnull)scene
onLeaveSceneResult:(NSInteger)errorCode {
dispatch_async(dispatch_get_main_queue(), ^{
[metachatScene destroy];
metachatScene = nil;
});
}
// 销毁 AgoraMetachatScene 对象的回调
- (void)metachatScene:(AgoraMetachatScene *_Nonnull)scene
onReleasedScene:(NSInteger)errorCode {
dispatch_async(dispatch_get_main_queue(), ^{
[AgoraMetachatKit destroy];
metachatKit = nil;
[AgoraRtcEngineKit destroy];
rtcEngine = nil;
});
}
// Swift
// 退出场景
rtcEngine?.leaveChannel()
metachatScene?.leave()
// 退出场景回调
func metachatScene(_ scene: AgoraMetachatScene, onLeaveSceneResult errorCode: Int) {
DispatchQueue.main.async {
metachatScene?.destroy()
metachatScene = nil
}
}
// 销毁 AgoraMetachatScene 对象的回调
func metachatScene(_ scene: AgoraMetachatScene, onReleasedScene errorCode: Int) {
DispatchQueue.main.async {
AgoraMetachatKit.destroy()
metachatKit = nil
AgoraRtcEngineKit.destroy()
rtcEngine = nil
}
}
联系声网销售获取元语聊场景的示例项目,查看其中的源代码。
所有 API 的参数描述详见元语聊 API 参考。
使用 3D 场景的过程中,不能销毁调用 createRenderView
创建的视图。该视图只有退出整个 app 时才能销毁。