前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >未来已来——如何在VR游戏中实现3D语音

未来已来——如何在VR游戏中实现3D语音

原创
作者头像
shusen
发布2020-09-23 11:36:40
2.1K0
发布2020-09-23 11:36:40
举报

我们实际使用GME SDK完成相关的开发,一起来看下代码是如何运行的。本篇是基于Google开源的CardBoard SDK进行的示例程序。

导入SDK

导入 GoogleVR SDK

GoogleVR SDK 官网点击下载SDK,我们演示使用的版本为GVR SDK for Unity v1.200.1。下载完成之后,新建一个 Unity 工程,我们演示使用的版本为 Unity 2018.4.23f1。双击 GoogleVRForUnity_1.200.1.unitypackage 导入SDK。

导入 GME SDK

点击下载指引,找到 【SDK v2.7.1 版本】,点击下载 Unity SDK。解压后将文件拷贝到Unity工程中,删除 Plugin 中的平台文件夹,只保留 Android、gmesdk.bundle以及x86_64。详细参考游戏多媒体引擎Unity工程配置

使用GoogleVR SDK

1、找到示例工程

找到 GoogleVR->Demos->Scenes 下的HelloVR场景,双击打开。

2、工程配置

在PlayerSettings中的XRSettings一栏,勾选Virtual Reality Supported,再添加 Cardboard 组件。

在 Prefernce 中的 External Tools下设置好 Android SDK 的路径及 Android NDK 的路径。

3、导出

将场景HelloVR添加到 Scene In Build,将 Platform 切换到 Android,设置好导出时候的 Package Name,便可以导出验证。

使用GME实时语音

游戏多媒体引擎Unity接入文档首先创建一个代码文件,名字为 GMEVoice,在工程中新建一个空物体,将代码挂载在空物体上。

双击代码文件 GMEVoice,在代码中引入GME。

代码语言:javascript
复制
using TencentMobileGaming;

1、初始化SDK

初始化GME SDK需要 Appid,请在腾讯云游戏多媒体引擎控制台申请,此处我们使用官方提供的测试 Appid。另外还需要自定义一个OpenId,数值大于10000,我们使用随机数来产生。

代码语言:javascript
复制
int int_OpenId = UnityEngine.Random.Range(12345, 22345);
string str_OpenId = int_OpenId.ToString();
int isInit = ITMGContext.GetInstance().Init("1400089356", str_OpenId);

2、调用 Poll 函数

在 Update 中我们调用 Poll 函数。

代码语言:javascript
复制
void Update()
{
     ITMGContext.GetInstance().Poll();
}

3、进入语音房间

进入语音房间需要鉴权,鉴权需要的AuthKey在腾讯云游戏多媒体引擎控制台上获取,与AppID对应。鉴权的具体参数及获取方法参考游戏多媒体引擎Unity接入文档鉴权部分。此处我们使用官方给的测试账号的AppID、AuthKey,进入的房间为 20200601,OpenId为随机出来的数字转string类型。

代码语言:javascript
复制
byte[] authBuffer = GetAuthBuffer("1400089356", "20200601", str_OpenId, "1cfbfd2a1a03a53e");
public static byte[] GetAuthBuffer(string AppID, string RoomID, string OpenId, string AuthKey)
{
    return QAVAuthBuffer.GenAuthBuffer(int.Parse(AppID), RoomID, OpenId, AuthKey);
}

接下来我们对进房事件进行监听,然后在 Unity 的Start 中调用进房函数。

代码语言:javascript
复制
ITMGContext.GetInstance().OnEnterRoomCompleteEvent += new QAVEnterRoomComplete(OnEnterRoomComplete);
ITMGContext.GetInstance().EnterRoom("20200601",ITMGRoomType.ITMG_ROOM_TYPE_FLUENCY, authBuffer);

4、进房回调

调用进房接口之后,需要监听回调并在回调中处理进房结果。如果进房成功便打开麦克风及扬声器。

代码语言:javascript
复制
void OnEnterRoomComplete(int err, string errInfo)
{
    if (err != 0)
        {
            Debug.Log("进房失败,错误码为:" + err);
            return;
        }
    else
        {
            //进房成功
            //打开麦克风
            ITMGContext.GetInstance().GetAudioCtrl().EnableMic(true);
            //打开扬声器
            ITMGContext.GetInstance().GetAudioCtrl().EnableSpeaker(true);
        }
}

5、反初始化

我们需要在销毁代码的时候反初始化整个SDK。

代码语言:javascript
复制
void OnDestroy()
{ 
    ITMGContext.GetInstance().Uninit();    
}

这时候我们可以先尝试一下导出两个windows可执行文件,验证是否进房成功,是否能在进入游戏后就打开麦克风及扬声器进行实时语音对话。

使用3D音效

如果以上步骤完成后,能够进入游戏后进行实时语音通话,那么我们接下来开始接入3D音效效果。游戏多媒体引擎3D音效文档

1、引入音效文件

点击下载地址下载音效文件,此文件为官方提供。我们把这个模型文件命名为3d_model,并放在Unity工程目录StreamingAssets下,确保能随可执行文件一同导出。我们写一个协程,用来将这个音效文件拷贝到Application.persistentDataPath下,方便引用。

代码语言:javascript
复制
StartCoroutine(copyFileFromAssetsToPersistent("3d_model"));

public IEnumerator copyFileFromAssetsToPersistent(string fileName)
{
    string fromPath = Application.streamingAssetsPath + "/" + fileName;
    string toPath = Application.persistentDataPath + "/" + fileName;
    if (!File.Exists(toPath))
    {
        Debug.Log("copying from " + fromPath + " to " + toPath);
#if UNITY_ANDROID && !UNITY_EDITOR
        WWW www1 = new WWW(fromPath);
        yield return www1;
        File.WriteAllBytes(toPath, www1.bytes);
        Debug.Log("file copy done");
        www1.Dispose();
        www1 = null;
#else
        File.WriteAllBytes(toPath, File.ReadAllBytes(fromPath));
#endif
    }
        yield return null;
}

2、初始化3D音效

我们在进房成功的回调中,初始化3D音效,路径为我们协程中写的路径。

代码语言:javascript
复制
string filePath = Application.persistentDataPath + "/3d_model";
int ret = QAVError.OK;
ret = ITMGContext.GetInstance().GetAudioCtrl().InitSpatializer(filePath);

3、开启3D音效

使用接口EnableSpatializer开启3D音效,在这里我们进房成功后,初始化3D音效成功后就启动3D音效。第二个参数与范围语音有关,此处不需关注。

代码语言:javascript
复制
ITMGContext.GetInstance().GetAudioCtrl().EnableSpatializer(true, false);

4、设置范围

设置语音的接收范围,此处的范围参数涉及到衰减,单位为Unity中的距离单位,为了突出效果,此处我们设置为100000。

代码语言:javascript
复制
ITMGContext.GetInstance().GetRoom().UpdateAudioRecvRange(100000);

5、更新自身坐标

通过更新坐标到服务器,游戏多媒体引擎服务器会根据房间内成员的坐标将声音进行3D处理,为了保证3D音效的实时性,需要在Update函数中每帧更新自身坐标。

在此Demo中,由于我们的代码挂载在另一个空物体上,所以我们需要将摄像机的位置实时传到接口中,我们声明一个GameObject,用于传递Demo中Player的坐标。

代码语言:javascript
复制
public GameObject currentPlayer;

在Unity编辑器中,我们将Player附给currentPlayer。

此处为了使3D效果明显,我们更新坐标时,将position都乘以一个100的系数进行放大。

代码语言:javascript
复制
void Update()
{
      ITMGContext.GetInstance().Poll();
      if (isRoomEntered)
      {
            Transform selftrans = currentPlayer.gameObject.transform;
            Matrix4x4 matrix = Matrix4x4.TRS(Vector3.zero, selftrans.rotation, Vector3.one);
            int[] position = new int[3] {
                     (int)selftrans.position.z*100, 
                     (int)selftrans.position.x*100, 
                     (int)selftrans.position.y*100 
                  };
           float[] axisForward = new float[3] { matrix.m22, matrix.m02, matrix.m12 };
           float[] axisRight = new float[3] { matrix.m20, matrix.m00, matrix.m10 };
           float[] axisUp = new float[3] { matrix.m21, matrix.m01, matrix.m11 };
           ITMGContext.GetInstance().GetRoom().UpdateSelfPosition(position,
             axisForward, 
             axisRight, 
             axisUp);
       }
}

最终代码:

代码语言:javascript
复制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using TencentMobileGaming;
public class GMEVoice : MonoBehaviour
{
    // 用于传递摄像机位置
    public GameObject currentPlayer;
    // 用于判断进房状态
    private bool isRoomEntered = false;

    // Start is called before the first frame update
    void Start()
    {
        // 初始化
        int int_OpenId = UnityEngine.Random.Range(12345, 22345);
        string str_OpenId = int_OpenId.ToString();
        int isInit = ITMGContext.GetInstance().Init("1400089356", str_OpenId);

        // 拷贝3D音效文件
        StartCoroutine(copyFileFromAssetsToPersistent("3d_model"));

        // 进房
        byte[] authBuffer = GetAuthBuffer("1400089356", "20200601",
 str_OpenId, "1cfbfd2a1a03a53e");
    if (isInit == 0) {
       ITMGContext.GetInstance().OnEnterRoomCompleteEvent +=
 new QAVEnterRoomComplete(OnEnterRoomComplete);
       ITMGContext.GetInstance().EnterRoom("20200601", ITMGRoomType.ITMG_ROOM_TYPE_FLUENCY, authBuffer);
    }
}

// Update is called once per frame
void Update()
{
   ITMGContext.GetInstance().Poll();

   // 如果进房成功,开始更新自身位置
   if (isRoomEntered)
   {
       Transform selftrans = currentPlayer.gameObject.transform;
       Matrix4x4 matrix = Matrix4x4.TRS(Vector3.zero, selftrans.rotation, Vector3.one);
       int[] position = new int[3] {
 (int)selftrans.position.z*100,
 (int)selftrans.position.x*100, 
 (int)selftrans.position.y*100 };
       float[] axisForward = new float[3] { matrix.m22, matrix.m02, matrix.m12 };
       float[] axisRight = new float[3] { matrix.m20, matrix.m00, matrix.m10 };
       float[] axisUp = new float[3] { matrix.m21, matrix.m01, matrix.m11 };
      ITMGContext.GetInstance().GetRoom().UpdateSelfPosition(position, 
axisForward, 
axisRight, 
axisUp);
    }
}
    
/// <summary>
/// 鉴权
/// </summary>
/// <param name="AppID"></param>
/// <param name="RoomID"></param>
/// <param name="OpenId"></param>
/// <param name="AuthKey"></param>
/// <returns></returns>
public static byte[] GetAuthBuffer(string AppID, string RoomID, 
string OpenId, string AuthKey)
{
return QAVAuthBuffer.GenAuthBuffer(int.Parse(AppID), RoomID, OpenId, AuthKey);
}

/// <summary>
/// 进房成功回调处理
/// </summary>
/// <param name="err"></param>
/// <param name="errInfo"></param>
void OnEnterRoomComplete(int err, string errInfo)
{
if (err != 0)
{
Debug.Log("进房失败,错误码为:" + err);
       return;
}
else
{
      isRoomEntered = true;
      //进房成功
      //打开麦克风
      ITMGContext.GetInstance().GetAudioCtrl().EnableMic(true);
      //打开扬声器
      ITMGContext.GetInstance().GetAudioCtrl().EnableSpeaker(true);

      // 初始化3D音效
      string filePath = Application.persistentDataPath + "/3d_model";
      Debug.Log("3D音效文件路径:"+ filePath);
      int ret_Init = ITMGContext.GetInstance().GetAudioCtrl().InitSpatializer(filePath);
      Debug.Log("初始化3D音效是否成功:"+ ret_Init);

          int ret_Enable = ITMGContext.GetInstance().GetAudioCtrl().EnableSpatializer(true, 
false);
      Debug.Log("开启3D音效是否成功:" + ret_Enable);
      if (ret_Enable != QAVError.OK)
      {
          return;
      }

      ITMGContext.GetInstance().GetRoom().UpdateAudioRecvRange(100000);
}
}

/// <summary>
/// 反初始化
/// </summary>
void OnDestroy()
{ 
ITMGContext.GetInstance().Uninit();    
}

/// <summary>
/// 用于从streamingAssets拷贝文件
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public IEnumerator copyFileFromAssetsToPersistent(string fileName)
{
string fromPath = Application.streamingAssetsPath + "/" + fileName;
string toPath = Application.persistentDataPath + "/" + fileName;
if (!File.Exists(toPath))
{
         Debug.Log("copying from " + fromPath + " to " + toPath);
#if UNITY_ANDROID && !UNITY_EDITOR
         WWW www1 = new WWW(fromPath);
         yield return www1;
         File.WriteAllBytes(toPath, www1.bytes);
         Debug.Log("file copy done");
         www1.Dispose();
         www1 = null;
#else
          File.WriteAllBytes(toPath, File.ReadAllBytes(fromPath));
#endif
}
yield return null;
}
}

验证并导出

我们将导出Android可执行文件apk文件进行验证。我们将Player的坐标x设置为3,导出一个apk,之后调整x的数值为-3,再导出一个apk,这样两个player的位置便是不同的。

进入VR游戏后,我们可以听到3D效果的实时语音。

技术创作101训练营

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导入SDK
    • 导入 GoogleVR SDK
      • 导入 GME SDK
      • 使用GoogleVR SDK
        • 1、找到示例工程
          • 2、工程配置
            • 3、导出
            • 使用GME实时语音
              • 1、初始化SDK
                • 2、调用 Poll 函数
                  • 3、进入语音房间
                    • 4、进房回调
                      • 5、反初始化
                      • 使用3D音效
                        • 1、引入音效文件
                          • 2、初始化3D音效
                            • 3、开启3D音效
                              • 4、设置范围
                                • 5、更新自身坐标
                                • 验证并导出
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档