ボイスチャット系SDK組み込みを調べがちなUnityエンジニアのCova です。
以前は Vivoxの導入について調べました。
(Vivox についてはQiitaでは最古の記事みたいです)
そして今回は Automatonさんの記事でも紹介があった通り、ついにDiscordSocialSDKでボイスチャット機能が正式にリリースされたとのことです。
Discord といえばゲーマーやストリーマの方々ではなくてはならないほど浸透しているボイスチャット・テキストチャット・映像共有ツールです。
様々なゲームでもDiscord を内部に取り入れてボイスチャット機能を実装しているマルチプレイゲームも存在します。
そこで今回はインディーズから大手まで、おそらく今後需要が高まるであろう、Discord のボイチャ機能をUnity のアプリに組み込む方法を実際に行ってみました。
STEP1. SDK取得までのフロー
具体的な手順は公式がわかりやすく記事を用意してくれているので、基本的にはこの手順に従ってください。
ざっくり説明すると
- アカウント作る
- チームアカウント作る
- DiscordSDKを適応するDiscordApplication (いわゆるProject)を作成
- SDKの有効化
- SDK配布ページにアクセスする
- SDKをDL
という流れになります。
1. アカウントを作る
Discord 公式にアクセスしてアカウントを作ってください。
(流石に今回はアカウント作成前提で話を進めます)
2. チームアカウントを作る
とあるので、とりあえずチームアカウントを作るページに移動しましょう。
そうするとTopページに New Team ボタンが表示されているので押します。
入力完了するとTeamInformation の画面に切り替わってチームが作成されます
3. DiscordSDKを適応するDiscordApplication (いわゆるProject)を作成
ということでApplication作成ページにアクセスします
そうするとTeamと似たUIで Applications ページに New Application
ボタンがあるので押します
Application 作成ポップアップが表示されるので
そうすると人間かどうかを確認されるので人間アピールしましょう
無事作成できるとこんな感じの画面に遷移します。
ここまできたらapp作成は終了です。
4. SDKの有効化
ということでSDKを有効化しましょう。
さきほどのApplication のページの左側のTab に OAuth2
というタブがあるのでそれを選択します
そうすると Public Client
という項目があるのでそこのToggle をONにしましょう。
その後、左側のTab にある Getting Started
というタブがあるのでそれを選択してください。
開発者名や連絡先などを入力して Submit
ボタンを押します
Let's GO!!
5. SDK配布ページにアクセスする
無事Let's goボタンを押すとGetting Started のページが変わり、APIDocument 等のリンクが表示されたページにいくので、一番下のDownloadsを選択します
6. SDKをDL
DLページにくるとバージョンと各種SDKが表示されるので、Unity開発者はUnityアイコンのPluginとSampleを、UE開発者はUEのアイコンのPluginとSampleをDLしましょう
STEP.2 公式のSample を確認しよう
公式のSampleは先ほどDLした DiscordSocialSdk-Unity Sample
-xxxx.zip です。
zipを解凍するとUnityProject が丸っと入っているのでUnityHub から立ち上げます。
検証環境
項目 | version |
---|---|
MacOS | Tahoe 26.0 ※VisionOS26の検証も並行でやってるのでベータ版を使ってます |
Android | 15.0 |
Unity | 6000.0.54f1 |
Discord | 1.4.9649 |
Sample の中身
Assets/Scenes/SampleScene.unity を開くと以下のようなUIが出てきます。
左側のHierarchyビューを見て分かる通り、GameObjectが2つしか存在しません。
それなのにUIは色々組まれています。
そうです。 Unity の UIToolKit
を用いてDiscord のSampleScene のUIは組まれております。
Discord 社のエンジニアの方々のSkill が非常に高いことをここからも伺えます。(2025年現在でUIToolKit を用いてバリバリ開発している企業の方が少ない。まだuGUI メインの時代なので)
1. アカウントの認証
Token が空の場合はまず Get Token with OAuth
ボタンを押します。
そうするとブラウザに切り替わる (スマホだとブラウザが立ち上がる) のでDiscordアカウントにログインします。
そうすると、このSampleアプリと連携するかのモーダルが出現するので一番したまでスクロールして認証を押すとOAuth認証による認証が行われてゲームないでアカウント情報を利用できます。
2. Social 機能
OAuth認証が通った後、画面左側に色々機能が開放されます。
ここでFriend ボタンを押すとFriend 一覧を取得できます。
ちなみにアカウントを選択すると普通にFriend の削除やBlock なども行えてしまうので扱いには注意してください。
3. テキストチャット
画面左側のLobbies 横の +ボタン
を押すと新規にLobby を作成します。
この Lobby は テキストチャットチャンネル
と ボイスチャットチャンネル
を統合したチャンネルのことです。
Lobby 作成には The void
と書かれている部分に任意の名前を入れることでLobbyが作成されます。
Lobby名は被ると厄介なのでなるべくUnique になるように命名するとよさそうです
現在Lobby の削除APIはありません。
公式Documentによるとデフォルトは5分のTimerが設定されていて、無人状態からTimerが作動して一定時間経つと自動でLobby が削除される仕組みになっています
Lobby に無事入るとメンバーリストが表示されると共にテキスト入力エリアも下部に表示されます。
↓
テキスト入力すると上記のような形でメッセージログが表示されます。
4. ボイスチャット
ボイスチャットはLobby 入室後でしか機能しないため、まずはLobbyに参加してください
ボイスチャットは先ほどのLobby の右側にある Join Voice
ボタンを押すとそのLobby のボイスチャンネルに参加できます。
参加すると画面左下のUIに接続Status 表示が追加されます。
Mute/Speaker Mute はそのまま使えます。
歯車アイコンを押すと音声デバイスの設定画面が表示されるのでDiscordアプリ の音声設定同様のことが可能です
- Q. サーバーミュートは?
- A. SampleアプリのUIにはありません。
ただし、APIは用意されているので下記リンクよりご確認ください
https://discord.com/developers/docs/resources/voice#modify-user-voice-state
STEP.3 Unity にSDKを組み込む
DLしたSDKですが、一応UnityPackage 対応されています。
ここの公式に紹介されている通り
- 導入したいUnityProjectを作る
- Packages/ 以下にDLしたSDKを解凍する
- UnityPackageManager のWindowを開く
- 左上の+ボタンから Install Package from Disk... を選ぶ
- Packages->com.discord.partnersdk->package.json を選ぶ
- 無事画像のように追加されてれば導入完了
接続用のC#コードであるDiscordManager.cs は公式に記載されているものを引っ張ってくるのが手っ取り早いです。
using UnityEngine;
using UnityEngine.UI;
using Discord.Sdk;
using System.Linq;
public class DiscordManager : MonoBehaviour
{
[SerializeField]
private ulong clientId; // Set this in the Unity Inspector from the dev portal
[SerializeField]
private Button loginButton;
[SerializeField]
private Text statusText;
private Client client;
private string codeVerifier;
void Start()
{
client = new Client();
// Modifying LoggingSeverity will show you more or less logging information
client.AddLogCallback(OnLog, LoggingSeverity.Error);
client.SetStatusChangedCallback(OnStatusChanged);
// Make sure the button has a listener
if (loginButton != null)
{
loginButton.onClick.AddListener(StartOAuthFlow);
}
else
{
Debug.LogError("Login button reference is missing, connect it in the inspector!");
}
// Set initial status text
if (statusText != null)
{
statusText.text = "Ready to login";
}
else
{
Debug.LogError("Status text reference is missing, connect it in the inspector!");
}
}
private void OnLog(string message, LoggingSeverity severity)
{
Debug.Log($"Log: {severity} - {message}");
}
private void OnStatusChanged(Client.Status status, Client.Error error, int errorCode)
{
Debug.Log($"Status changed: {status}");
statusText.text = status.ToString();
if(error != Client.Error.None)
{
Debug.LogError($"Error: {error}, code: {errorCode}");
}
if (status == Client.Status.Ready)
{
ClientReady();
}
}
private void ClientReady()
{
Debug.Log($"Friend Count: {client.GetRelationships().Count()}");
Activity activity = new Activity();
activity.SetType(ActivityTypes.Playing);
activity.SetState("In Competitive Match");
activity.SetDetails("Rank: Diamond II");
client.UpdateRichPresence(activity, (ClientResult result) => {
if (result.Successful()) {
Debug.Log("Rich presence updated!");
} else {
Debug.LogError("Failed to update rich presence");
}
});
}
private void StartOAuthFlow() {
var authorizationVerifier = client.CreateAuthorizationCodeVerifier();
codeVerifier = authorizationVerifier.Verifier();
var args = new AuthorizationArgs();
args.SetClientId(clientId);
args.SetScopes(Client.GetDefaultPresenceScopes());
args.SetCodeChallenge(authorizationVerifier.Challenge());
client.Authorize(args, OnAuthorizeResult);
}
private void OnAuthorizeResult(ClientResult result, string code, string redirectUri) {
Debug.Log($"Authorization result: [{result.Error()}] [{code}] [{redirectUri}]");
if (!result.Successful()) {
return;
}
GetTokenFromCode(code, redirectUri);
}
private void GetTokenFromCode(string code, string redirectUri) {
client.GetToken(clientId,
code,
codeVerifier,
redirectUri,
(result, token, refreshToken, tokenType, expiresIn, scope) => {
if (token != "") {
OnReceivedToken(token);
} else {
OnRetrieveTokenFailed();
}
});
}
private void OnReceivedToken(string token) {
Debug.Log("Token received: " + token);
client.UpdateToken(AuthorizationTokenType.Bearer, token, (ClientResult result) => { client.Connect(); });
}
private void OnRetrieveTokenFailed() { statusText.text = "Failed to retrieve token"; }
}
(公式がStep by Step でこのDiscordManager の差分とそこまでのStepにおけるコードを記載してくれているので初中級者でも導入できるんじゃないかと思います。とても親切)
ApplicationID とClientID
同じです。
Application Topページの ApplicationID
をそのまま引っ張ってきてもらってOKです。
FAQ
Mac で動かないよ
DiscordのSDKがMacOSだとデフォルトでセキュリティチェックに引っかかるため、設定→プライバシーとセキュリティ→セキュリティ の項目にDiscordSDKの項目が表示されているので有効化してあげてください。
まとめ
Discord のSDK導入解説とSample の機能紹介を行いました。
Discordはゲーマーに人気のため今後はどんどんDiscord内蔵ゲームアプリが出てくると思います。
Lobbyを二つ用意しておいて、いわゆる生存者側部屋と天国部屋みたいなボイスチャンネルの自動移動とかはかなり需要がありそうなので、それをゲーム側が自動でとり行ってくれると、ユーザーとしては非常に便利になりそうですね。