目錄
- 🐱?🏍前言
- 🎂Unity 接入 聲網SDK 實作 音頻通話
- 第1??步,創建聲網應用
- 第2??步,獲取相應的SDK
- 第3??步,將SDK接入Unity中
- 第4??步:搭建一個測驗場景,撰寫測驗代碼
- 第5??步:音頻通話API
- 第6??步:效果測驗
- 🎨總結

🐱?🏍前言
- 最近正好在網上看到
聲網,然后就順道了解到了聲網這個平臺,發現聲網的功能還真挺多呢 - 也是一個兼容幾十種平臺的大公司啊,到現在才了解到,也算是相見恨晚~
- 所以就趕緊來用
Unity結合聲網做一個語音聊天房! - 我也是第一次接入聲網的SDK,可能有些地方不是很熟練,正好寫一篇文章來記錄學習一下~
🎂Unity 接入 聲網SDK 實作 音頻通話
先簡單的介紹一下聲網,不了解的小伙伴可以簡單認識一下~
聲網 官網:https://www.agora.io/cn/community/

成立于 2014 年 4 月的聲網Agora 是實時互動 API 平臺行業開創者,是專業服務商,開發者只需簡單呼叫 API,即可在應用內構建多種實時音視頻互動場景,聲網 SDK 已經賦能社交直播、在線教育、游戲電競、IoT、AR/VR、金融、保險、醫療、企業協作等 10 余行業,共計 100 多種場景,
聲網的 SDK(Software Development Kit) 包體積很小,運行時CPU和記憶體占用率低,對于移動端的游戲開發很友好,
2019年7月聲網正式成為了 Unity 官方認證合作伙伴,語音和視頻的 SDK 也已經發布在了 Unity 資源商店中,能夠非常方便的接入,
下面就來一步一步搞一下這個音視頻通話試試吧!
第1??步,創建聲網應用
首先我們需要去聲網,注冊登錄一系列的就不說了
登錄上之后來我們的控制臺創建一個應用,拿到我們創建的這個應用的APPID

由于我們是用來測驗使用的,直接選擇除錯模式就好啦,可以參考官網檔案查看二者的區別:https://docs.agora.io/cn/Agora%20Platform/token?platform=All%20Platforms
創建成功后,點擊APP ID按鈕,將其復制出來,后面的代碼中會用到!
第2??步,獲取相應的SDK
接下來我們要下載一下音視頻的SDK包,下載方式有兩種
一種是在聲網的官網下載對應的UnitySDK包,如下所示

另一種則是在Unity商店里下載,因為前面說了, 聲網和Unity已經進行過合作了

如果是第一種方式在聲網下載的,則是一個壓縮包
里面分別有一個多平臺的工具包 和 一個Unity示例工程
我們直接使用Unity示例工程即可

如果是從Unity商店下載的包,那就直接匯入Unity工程中就好了!

兩種方式都可以,我這里是直接在聲網下載的SDK,以因為Unity商店的網速實在是太卡了,不友好~
第3??步,將SDK接入Unity中
我們這里直接使用UnityHub把在聲網下載的那個Unity示例工程給打開
當然也可以在一個Unity中匯入在Unity商店下載的包,都是一樣的效果

打開之后工程視圖如下所示
- Demo:官方提供的測驗語音 Demo
- Edior:iOS 構建后處理腳本
- Plugins:不同平臺所依賴的庫
- Scripts:SDK 原始碼

第4??步:搭建一個測驗場景,撰寫測驗代碼
示例工程中有個場景,我們可以使用它的,也可以自己新建一個場景測驗!
簡單修改一下場景UI,一起來看下代碼
場景中新建一個腳本HelloUnity3D,掛載到場景中即可
using UnityEngine;
using UnityEngine.UI;
#if(UNITY_2018_3_OR_NEWER)
using UnityEngine.Android;
#endif
using agora_gaming_rtc;
public class HelloUnity3D : MonoBehaviour
{
public InputField mChannelNameInputField;//頻道號
public Text mShownMessage;//提示
public Text versionText;//版本號
public Button joinChannel;//加入房間
public Button leaveChannel;//離開房間
public Button muteButton;//靜音
private IRtcEngine mRtcEngine = null;
// 輸入App ID后,在App ID外洗掉##
[SerializeField]
private string AppID = "app_id";
void Awake()
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 30;
muteButton.enabled = false;
CheckAppId();
}
// 進行初始化
void Start()
{
#if (UNITY_2018_3_OR_NEWER)
// 判斷是否有麥克風權限,沒有權限的話主動申請權限
if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Permission.RequestUserPermission(Permission.Microphone);
}
#endif
joinChannel.onClick.AddListener(JoinChannel);
leaveChannel.onClick.AddListener(LeaveChannel);
muteButton.onClick.AddListener(MuteButtonTapped);
mRtcEngine = IRtcEngine.GetEngine(AppID);
versionText.GetComponent<Text>().text = "Version : " + getSdkVersion();
//加入頻道成功后的回呼
mRtcEngine.OnJoinChannelSuccess += (string channelName, uint uid, int elapsed) =>
{
string joinSuccessMessage = string.Format("加入頻道 回呼 uid: {0}, channel: {1}, version: {2}", uid, channelName, getSdkVersion());
Debug.Log(joinSuccessMessage);
mShownMessage.GetComponent<Text>().text = (joinSuccessMessage);
muteButton.enabled = true;
};
//離開頻道回呼,
mRtcEngine.OnLeaveChannel += (RtcStats stats) =>
{
string leaveChannelMessage = string.Format("離開頻道回呼時間 {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}", stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate);
Debug.Log(leaveChannelMessage);
mShownMessage.GetComponent<Text>().text = (leaveChannelMessage);
muteButton.enabled = false;
// 重置靜音鍵狀態
if (isMuted)
{
MuteButtonTapped();
}
};
//遠端用戶加入當前頻道回呼,
mRtcEngine.OnUserJoined += (uint uid, int elapsed) =>
{
string userJoinedMessage = string.Format("遠端用戶加入當前頻道回呼 uid {0} {1}", uid, elapsed);
Debug.Log(userJoinedMessage);
mShownMessage.GetComponent<Text>().text = (userJoinedMessage);
};
//遠端用戶離開當前頻道回呼,
mRtcEngine.OnUserOffline += (uint uid, USER_OFFLINE_REASON reason) =>
{
string userOfflineMessage = string.Format("遠端用戶離開當前頻道回呼 uid {0} {1}", uid, reason);
Debug.Log(userOfflineMessage);
mShownMessage.GetComponent<Text>().text = (userOfflineMessage);
};
// 用戶音量提示回呼,
mRtcEngine.OnVolumeIndication += (AudioVolumeInfo[] speakers, int speakerNumber, int totalVolume) =>
{
if (speakerNumber == 0 || speakers == null)
{
Debug.Log(string.Format("本地用戶音量提示回呼 {0}", totalVolume));
}
for (int idx = 0; idx < speakerNumber; idx++)
{
string volumeIndicationMessage = string.Format("{0} onVolumeIndication {1} {2}", speakerNumber, speakers[idx].uid, speakers[idx].volume);
Debug.Log(volumeIndicationMessage);
}
};
//用戶靜音提示回呼
mRtcEngine.OnUserMutedAudio += (uint uid, bool muted) =>
{
string userMutedMessage = string.Format("用戶靜音提示回呼 uid {0} {1}", uid, muted);
Debug.Log(userMutedMessage);
mShownMessage.GetComponent<Text>().text = (userMutedMessage);
};
//發生警告回呼
mRtcEngine.OnWarning += (int warn, string msg) =>
{
string description = IRtcEngine.GetErrorDescription(warn);
string warningMessage = string.Format("發生警告回呼 {0} {1} {2}", warn, msg, description);
Debug.Log(warningMessage);
};
//發生錯誤回呼
mRtcEngine.OnError += (int error, string msg) =>
{
string description = IRtcEngine.GetErrorDescription(error);
string errorMessage = string.Format("發生錯誤回呼 {0} {1} {2}", error, msg, description);
Debug.Log(errorMessage);
};
// 當前通話統計回呼,每兩秒觸發一次,
mRtcEngine.OnRtcStats += (RtcStats stats) =>
{
string rtcStatsMessage = string.Format("onRtcStats callback duration {0}, tx: {1}, rx: {2}, tx kbps: {3}, rx kbps: {4}, tx(a) kbps: {5}, rx(a) kbps: {6} users {7}",
stats.duration, stats.txBytes, stats.rxBytes, stats.txKBitRate, stats.rxKBitRate, stats.txAudioKBitRate, stats.rxAudioKBitRate, stats.userCount);
//Debug.Log(rtcStatsMessage);
int lengthOfMixingFile = mRtcEngine.GetAudioMixingDuration();
int currentTs = mRtcEngine.GetAudioMixingCurrentPosition();
string mixingMessage = string.Format("Mixing File Meta {0}, {1}", lengthOfMixingFile, currentTs);
//Debug.Log(mixingMessage);
};
//語音路由已發生變化回呼,(只在移動平臺生效)
mRtcEngine.OnAudioRouteChanged += (AUDIO_ROUTE route) =>
{
string routeMessage = string.Format("onAudioRouteChanged {0}", route);
Debug.Log(routeMessage);
};
//Token 過期回呼
mRtcEngine.OnRequestToken += () =>
{
string requestKeyMessage = string.Format("OnRequestToken");
Debug.Log(requestKeyMessage);
};
// 網路中斷回呼(建立成功后才會觸發)
mRtcEngine.OnConnectionInterrupted += () =>
{
string interruptedMessage = string.Format("OnConnectionInterrupted");
Debug.Log(interruptedMessage);
};
// 網路連接丟失回呼
mRtcEngine.OnConnectionLost += () =>
{
string lostMessage = string.Format("OnConnectionLost");
Debug.Log(lostMessage);
};
// 設定 Log 級別
mRtcEngine.SetLogFilter(LOG_FILTER.INFO);
// 1.設定為自由說話模式,常用于一對一或者群聊
//mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_COMMUNICATION);
//2.設定為直播模式,適用于聊天室或互動式視頻流等場景,
//mRtcEngine.SetChannelProfile (CHANNEL_PROFILE.CHANNEL_PROFILE_LIVE_BROADCASTING);
//3.設定為游戲模式,這個組態檔使用較低位元率的編解碼器,消耗更少的電力,適用于所有游戲玩家都可以自由交談的游戲場景,
//mRtcEngine.SetChannelProfile(CHANNEL_PROFILE.CHANNEL_PROFILE_GAME);
//設定直播場景下的用戶角色,
//mRtcEngine.SetClientRole (CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER);
}
private void CheckAppId()
{
Debug.Assert(AppID.Length > 10, "請先在Game Controller物件上填寫你的AppId,.");
GameObject go = GameObject.Find("AppIDText");
if (go != null)
{
Text appIDText = go.GetComponent<Text>();
if (appIDText != null)
{
if (string.IsNullOrEmpty(AppID))
{
appIDText.text = "AppID: " + "UNDEFINED!";
appIDText.color = Color.red;
}
else
{
appIDText.text = "AppID: " + AppID.Substring(0, 4) + "********" + AppID.Substring(AppID.Length - 4, 4);
}
}
}
}
/// <summary>
/// 加入頻道
/// </summary>
public void JoinChannel()
{
// 從界面的輸入框獲取頻道名稱
string channelName = mChannelNameInputField.text.Trim();
Debug.Log(string.Format("從界面的輸入框獲取頻道名稱 {0}", channelName));
if (string.IsNullOrEmpty(channelName))
{
return;
}
// 加入頻道
// channelKey: 動態秘鑰,我們最開始沒有選擇 Token 模式,這里就可以傳入 null;否則需要傳入服務器生成的 Token
// channelName: 頻道名稱
// info: 開發者附帶資訊(非必要),不會傳遞給頻道內其他用戶
// uid: 用戶ID,0 為自動分配
//mRtcEngine.JoinChannelByKey(channelKey: null, channelName: channelName, info: "extra", uid: 0);
//加入頻道并設定發布和訂閱狀態,
mRtcEngine.JoinChannel(channelName, "extra", 0);
}
/// <summary>
/// 離開頻道
/// </summary>
public void LeaveChannel()
{
// 離開頻道
mRtcEngine.LeaveChannel();
string channelName = mChannelNameInputField.text.Trim();
Debug.Log(string.Format("left channel name {0}", channelName));
}
void OnApplicationQuit()
{
if (mRtcEngine != null)
{
// 銷毀 IRtcEngine
IRtcEngine.Destroy();
}
}
/// <summary>
/// 查詢 SDK 版本號,
/// </summary>
/// <returns></returns>
public string getSdkVersion()
{
string ver = IRtcEngine.GetSdkVersion();
return ver;
}
bool isMuted = false;
void MuteButtonTapped()
{
//設定靜音或者取消靜音
string labeltext = isMuted ? "Mute" : "Unmute";
Text label = muteButton.GetComponentInChildren<Text>();
if (label != null)
{
label.text = labeltext;
}
isMuted = !isMuted;
// 設定靜音(停止推送本地音頻)
mRtcEngine.EnableLocalAudio(!isMuted);
Debug.Log("靜音方法執行完成");
}
}
上述代碼中都有注釋,一個完整的語音通話案例就寫好了!
只需要把自己在聲網后臺獲取到的APP ID 賦值一下就好了~
第5??步:音頻通話API
在聲網有關于視頻通話的一堆API,我們可以來參考一下
音頻通話API:https://docs.agora.io/cn/Voice/API%20Reference/unity/index.html
這里我們只介紹幾種核心的API,也是在本次實體中用到的做重點介紹,其他的可以有時間的時候自己研究一下 ~



第6??步:效果測驗
可以先在編輯器下看看運行效果
我這里是 打包成PC的一個exe檔案運行 和 在編輯器下運行
讓這兩個進行語音通話,效果如下:
語音通話的 API 呼叫時序見下圖:

🎨總結
- 本文簡單做了一個使用Unity實作語音通話的案例
- 其實非常簡單,根本就沒怎么動手做,因為官網的這個工程中已經幫我們把實體場景和腳本都寫好了
- 核心就是上面那張時序圖一樣,先進行初始化,然后加入頻道聊天就可以了!
- 下期我們再來整一個視頻通話的小案例繼續學習一下,其實原理都差不多,主要是代碼有所不同,那我們下次再見啦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/355419.html
標籤:其他
上一篇:C語言實作入門級小游戲——掃雷
