はじめに
こんにちは、NTTコノキューの中矢です。業務ではUnity、C#と格闘しています。
本記事は、Agoraと呼ばれる通話や配信を簡単に実装できるAPI/SDKのうち、Voice Calling(RTC)におけるUnityでの入門記事です。
この記事が少しでも、Agoraを利用してみたい人の参考になれば幸いです。
AgoraとVoice Calling(RTC)とは
Agora(アゴラ)とは、「高品質な通話・配信・会話型AIの機能を簡単に実装できる『Agora』超低遅延&高拡張性のAPI/SDK」です。
Agoraの概要:https://jp.vcube.com/sdk
AgoraのSDKをUnityなどに組み込めば、音声通話、ビデオ通話、配信などのオンライン体験やリアルタイムコミュニケーション機能の実装が簡単に作ることができます。
Agoraの特徴はSD-RTN(Software-Defined Real-Time Network)と呼ばれるネットワーク基盤で、この方式により超低遅延かつ高い信頼性の通信を実現しているとのことです。
SD-RTNの概要:https://www.agora.io/en/the-agora-platform-advantage/
本記事ではAgoraで利用できる機能のうち、Voice Callingと呼ばれる音声通話の機能を試します。また、Voice CallingはRTC(Real-Time Communication)とも呼ばれているようで、検索するとRTCで引っかかることも多いです。
Voice Callingの概要:https://docs.agora.io/en/voice-calling/overview/product-overview?platform=android
サンプルで動かしたアプリ
本記事でサンプルとして動かしたAndroidアプリは、AgoraのVoice Callingのクイックスタートを参考に作ったものです。
Voice Callingのクイックスタート:https://docs.agora.io/en/voice-calling/get-started/get-started-sdk?platform=unity

このアプリは「Join」ボタンを押すと、通話チャンネル(以下、チャンネル)に参加し、「Leave」ボタンを押すと、チャンネルから退出します。そしてチャンネル参加中は、別端末と音声通話ができます。簡単なトランシーバーみたいなものです。
環境
- Windows 11
- Unity 2022.3.50f1
- Voice SDK Version 4.5.1 (Latest)
Unityの設定
ここからUnityのシーン設定とコードの解説を行います。基本的にはAgoraのクイックスタートと同じですが、本記事ではいくつか補足をしつつ解説します。本記事を読んでからAgoraのクイックスタートを読むとより理解が深まるかなと思います。
まず、後でAndroidでビルドするためにAndroidでSwitch Platformします。
そして以下の画像のようにシーンを設定します。

Canvasを配置し、その配下にボタンを2つ追加、それぞれに「Join」と「Leave」とボタンのテキストに設定します。クイックスタートのコードでは以下のようにButtonオブジェクトを「Join」と「Leave」で検索してイベントを設定しているため、ボタンの名前は「Join」と「Leave」にするよう気を付けます。
private void SetupUI() { GameObject go = GameObject.Find("Leave"); go.GetComponent<Button>().onClick.AddListener(Leave); go = GameObject.Find("Join"); go.GetComponent<Button>().onClick.AddListener(Join); }
続いて空のGameObjectに「Agora」と名づけ、それに「JoinChannelAudio.cs」をアタッチします。ここで「JoinChannelAudio.cs」のサンプルコードは長いため、本記事の最後に全文を記載します。

Agoraの設定
ここからAgoraのコンソール画面での設定を行っていきます。
まずAgoraのログイン画面でログインします。
Agoraコンソールのログイン画面:https://sso2.agora.io/en/login

ログインした最初の画面で白かった場合、画面上部に出てくる「Switch to the new version」を押下し、コンソール画面のバージョンを上げます。
![]()
すると真っ黒なコンソール画面が表示されます。このバージョンの画面が、Agoraのドキュメントで解説されているバージョンの画面になります。以降、この画面で解説していきます。

まずAgoraのプロジェクトを新規作成します。「New Project」を押下します。

Project Nameに任意のプロジェクト名を入力し、Use Caseはそれっぽいものを入れ(ここではEducation > Tutoring)、推奨されているApp ID + Tokenで認証モードを設定します。

これでAgoraのVoice Calling(RTC)のコンソール上での設定は完了です。
サンプルアプリの動かし方
そして今まで組み上げたサンプルを動かします。
まず「JoinChannelAudio.cs」のコンポーネントのApp ID、Channel Name、Tokenを入力します。

App IDはAgoraのコンソール画面から作成したプロジェクトを選択し、以下画面のBasic Settingsから取得します。

Channel Nameは任意の名前を入力し、Tokenは上記画面の「Generate Temp Token」ボタンを押下して取得すれば、アプリは動作します。

2台端末があれば、それぞれにビルドすると、トランシーバーができます。 もし1台しかなくても、スマホアプリとUnityエディタでトランシーバーができます。
コード解説
最後にJoinChannelAudio.csのコードを見ていきます。 本サンプルアプリでは大きく3つの処理が書かれています。
- RTCエンジンの初期化
- チャンネル参加
- チャンネル退出
まずはRTCエンジンの初期化は以下です。
void Awake() { // RTCエンジンの初期化 SetupAudioSDKEngine(); // イベントの初期化 InitEventHandler(); // UIの初期化(解説しない) SetupUI(); }
SetupAudioSDKEngineメソッド内は以下です。
private void SetupAudioSDKEngine() { // RTCエンジン作成 RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine(); // RTCエンジンのパラメータを設定================================ RtcEngineContext context = new RtcEngineContext(); context.appId = _appID; context.channelProfile = CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING; context.audioScenario = AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT; // ========================================================= // RTCエンジンを上のパラメータで初期化 RtcEngine.Initialize(context); }
ここでAgoraのRTCエンジンを初期化しています。書いてあることは、RTCエンジンをインスタンス化して、パラメータを設定しているだけです。
channelProfileは2人以上のユーザが入るチャンネルであると設定しており、audioScenarioはデフォルトで設定しています。
続いてチャンネル参加は以下です。チャンネルとはAgoraで通信する「部屋」みたいなもので、同じチャンネル内で通信ができる、といったものです。
public void Join() { Debug.Log("Joining " + _channelName); // Audioを有効にする RtcEngine.EnableAudio(); // チャンネルの設定をする===================================== ChannelMediaOptions options = new ChannelMediaOptions(); // マイクでキャプチャした音声を勝手に送信する options.publishMicrophoneTrack.SetValue(true); // 受信した音声を勝手に再生する options.autoSubscribeAudio.SetValue(true); // このチャンネルでは、音声はブロードキャストで送信する設定とする options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING); // チャンネル参加時のユーザの役割はブロードキャスターである options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER); // ========================================================= // チャンネルに参加する RtcEngine.JoinChannel(_token, _channelName, 0, options); }
やっていることは単純で、Audioを有効にし、パラメータを設定し、チャンネルに参加するだけです。
ちなみに、ここでpublishMicrophoneTrack.SetValue(true)をすることで、マイクで取得した音声をチャンネル参加中に送信、autoSubscribeAudio.SetValue(true)で、受信した音声をチャンネル参加中に再生する設定にしています。
最後にチャンネル退出です。これだけでチャンネル退出です。簡単ですね。
public void Leave() { Debug.Log("Leaving " + _channelName); // チャンネルから退出する RtcEngine.LeaveChannel(); // Audioを無効にする RtcEngine.DisableAudio(); }
また、AgoraのRTCにはユーザのチャンネルの入退室時にイベント発行することもでき、それは以下で設定しています。実際のアプリに組み込むときは、このイベントをうまく使っていきたいところです。
internal class UserEventHandler : IRtcEngineEventHandler { private readonly JoinChannelAudio _audioSample; internal UserEventHandler(JoinChannelAudio audioSample) { _audioSample = audioSample; } // 自身がチャンネルへの参加成功したときに呼ばれる public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed) { Debug.Log("OnJoinChannelSuccess"); } // チャンネルに別のユーザが参加したときに呼ばれる public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed) { Debug.Log("Remote user joined"); } // 別のユーザがチャンネル退出したときに呼ばれる public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason) { } } }
まとめ
本記事ではAgoraのVoice Callingのクイックスタートのコードを参考にサンプルアプリを作成、解説しました。
元のクイックスタートのドキュメントにはシーケンスや、トークン発行のサーバの作成方法も記載されているため、Agoraを使って何かしら通信するサービスを作りたい場合、本記事を足掛かりにAgoraのドキュメントを読むと幸せになるかもしれません。
Agoraドキュメント:https://docs.agora.io/en/voice-calling/overview/product-overview
サンプルコード全文
ここではサンプルコード全文を載せます。
ほとんどクイックスタートのコードと変わらないですが、クイックスタートのコードをそのまま使うと、TextMeshProを使っていなかったりなどで少し不便だったため、少し改造しています。参考になれば幸いです。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Agora.Rtc; #if UNITY_ANDROID using UnityEngine.Android; #endif public class JoinChannelAudio : MonoBehaviour { // Fill in your app ID [SerializeField] private string _appID = ""; // Fill in your channel name [SerializeField] private string _channelName = ""; // Fill in a temporary token [SerializeField] private string _token = ""; internal IRtcEngine RtcEngine; #if UNITY_ANDROID private ArrayList permissionList = new ArrayList() { Permission.Microphone }; #endif void Awake() { // RTCエンジンの初期化 SetupAudioSDKEngine(); // イベントの初期化 InitEventHandler(); // UIの初期化(解説しない) SetupUI(); } // Update is called once per frame void Update() { CheckPermissions(); } void OnApplicationQuit() { if (RtcEngine != null) { Leave(); // Destroy IRtcEngine RtcEngine.Dispose(); RtcEngine = null; } } private void CheckPermissions() { #if UNITY_ANDROID foreach (string permission in permissionList) { if (!Permission.HasUserAuthorizedPermission(permission)) { Permission.RequestUserPermission(permission); } } #endif } private void SetupUI() { GameObject go = GameObject.Find("Leave"); go.GetComponent<Button>().onClick.AddListener(Leave); go = GameObject.Find("Join"); go.GetComponent<Button>().onClick.AddListener(Join); } private void SetupAudioSDKEngine() { // RTCエンジン作成 RtcEngine = Agora.Rtc.RtcEngine.CreateAgoraRtcEngine(); // RTCエンジンのパラメータを設定================================ RtcEngineContext context = new RtcEngineContext(); context.appId = _appID; context.channelProfile = CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING; context.audioScenario = AUDIO_SCENARIO_TYPE.AUDIO_SCENARIO_DEFAULT; // ========================================================= // RTCエンジンを上のパラメータで初期化 RtcEngine.Initialize(context); } public void Join() { Debug.Log("Joining " + _channelName); // Audioを有効にする RtcEngine.EnableAudio(); // チャンネルの設定をする===================================== ChannelMediaOptions options = new ChannelMediaOptions(); // マイクでキャプチャした音声を勝手に送信する options.publishMicrophoneTrack.SetValue(true); // 受信した音声を勝手に再生する options.autoSubscribeAudio.SetValue(true); // このチャンネルでは、音声はブロードキャストで送信する設定とする options.channelProfile.SetValue(CHANNEL_PROFILE_TYPE.CHANNEL_PROFILE_LIVE_BROADCASTING); // チャンネル参加時のユーザの役割はブロードキャスターである options.clientRoleType.SetValue(CLIENT_ROLE_TYPE.CLIENT_ROLE_BROADCASTER); // ========================================================= // チャンネルに参加する RtcEngine.JoinChannel(_token, _channelName, 0, options); } public void Leave() { Debug.Log("Leaving " + _channelName); // チャンネルから退出する RtcEngine.LeaveChannel(); // Audioを無効にする RtcEngine.DisableAudio(); } // イベントのコールバック(チャンネル入室時に通知など)の初期化 private void InitEventHandler() { UserEventHandler handler = new UserEventHandler(this); RtcEngine.InitEventHandler(handler); } // Implement your own callback class by inheriting the IRtcEngineEventHandler interface class internal class UserEventHandler : IRtcEngineEventHandler { private readonly JoinChannelAudio _audioSample; internal UserEventHandler(JoinChannelAudio audioSample) { _audioSample = audioSample; } // 自身がチャンネルへの参加成功したときに呼ばれる public override void OnJoinChannelSuccess(RtcConnection connection, int elapsed) { Debug.Log("OnJoinChannelSuccess"); } // チャンネルに別のユーザが参加したときに呼ばれる public override void OnUserJoined(RtcConnection connection, uint uid, int elapsed) { Debug.Log("Remote user joined"); } // 別のユーザがチャンネル退出したときに呼ばれる public override void OnUserOffline(RtcConnection connection, uint uid, USER_OFFLINE_REASON_TYPE reason) { } } }