■ はじめに
オンラインゲーム作りたいなーと思ったので、個人的にまとめてみたいと思います。
(UGSは機能が豊富なので、やる気があれば当記事を随時更新します。)
■ UGSについて
- Unity Gaming Services(UGS):Unityが提供する便利なツール群のこと。
- ネットワーク関係の機能や、マッチング機能、ボイスチャット機能などのオンライン用のツール群
- ゲームのパフォーマンス管理、プレイヤーの行動とかを記録する機能もあるっぽい
- まだベータ版の機能が多い
● 料金
■ 機能一覧
● Netcode
- 接続、同期などの機能を簡単に扱える
- ローカルマルチプレイは、最低これだけでも実装可能
使用例
- ゲームの開始はこんな感じのメソッドを使う
ゲームに参加.cs
NetworkManager.Singleton.StartServer(); // サーバーとして接続開始 NetworkManager.Singleton.StartHost(); // ホストとして接続開始 NetworkManager.Singleton.StartClient(); // クライアントとして接続開始
- NetworkBehaviourクラスを継承したスクリプトをNetworkObjectコンポーネントがアタッチされたオブジェクトにアタッチすると、RPCやネットワーク変数などを利用できる
RPCとかについて.cs
[ClientRpc]
private void DoSomethingClientRpc(int randomInteger, ClientRpcParams clientRpcParams = default)
{
if (IsOwner) return;
// Run your client-side logic here!!
Debug.LogFormat("GameObject: {0} has received a randomInteger with value: {1}", gameObject.name, randomInteger);
}
● Transport
-
ネットワーク系の処理のSDK
-
UDP, WebSocketを利用している
-
マルチプラットフォーム対応可能
-
手動でも細かく設定してサーバーに接続したりできるぽい
● Authentication
- プレイヤーの認証系の機能を使える
- RelayやLobbyなどの機能を使う際に必須
使い道
- オンラインゲームでのプレイヤーの情報の保存など
- マルチプラットフォームでの連携サービスによるデータの同期
サインイン
-
匿名認証:他の連携サービスを利用せずに匿名でゲームにサインインする
- ソシャゲなどの「ゲストでログイン」的な感じ
- サードパーティー認証:GooglePlayやApple Game Center、SteamなどのサードパーティーのIDプロバイダを利用してゲームにサインインする
使用例
-
小規模なゲームで、マルチプラットフォームを考えてなければ、基本的に匿名認証でいい
匿名認証.csasync void Awake() { // サービス初期化 await UnityServices.InitializeAsync(); // サインイン時にログ表示 AuthenticationService.Instance.SignedIn += () => { print("Sined in:" + AuthenticationService.Instance.PlayerId); }; // サインイン await AuthenticationService.Instance.SignInAnonymouslyAsync(); }
-
サードパーティー認証
● Relay
- Unityが提供するサーバーサービス
- Relay Server、Relay Allocationという機能がある
- Server:サーバー系の操作
- Allocation:サーバーに対してプレイヤー用の空きスロットを予約する感じの操作をする
- Steamworksなどの別のサーバーサービスを利用する場合、Relayは使わなくていいぽい
使用例
RelayManager.cs
/* Fields */
[SerializeField] int allocationCount = 1;
//-------------------------------------------------------------------
/* Methods */
public async Task<string> CreateRelay()
{
try {
// 割り当て作成
var allocation = await RelayService.Instance.CreateAllocationAsync(allocationCount);
// サーバーデータ設定
var relayServerData = new RelayServerData(allocation, "dtls");
var transport = Unity.Netcode.NetworkManager.Singleton.GetComponent<UnityTransport>();
transport.SetRelayServerData(relayServerData);
// 参加コードの取得
var joinCode = await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId);
// ホストとして開始
Unity.Netcode.NetworkManager.Singleton.StartHost();
print($"サーバーを作成しました({allocation.AllocationId})");
return joinCode;
}
catch (RelayServiceException e) {
Debug.LogException(e);
throw e;
}
}
public async Task JoinRelay(string joinCode)
{
try {
// コードをもとに参加
var joinedAllocation = await RelayService.Instance.JoinAllocationAsync(joinCode);
// サーバーデータ設定
RelayServerData serverData = new RelayServerData(joinedAllocation, "dtls");
Unity.Netcode.NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(serverData);
// クライアントとして開始
Unity.Netcode.NetworkManager.Singleton.StartClient();
print($"サーバーに参加しました({joinedAllocation.AllocationId})");
}
catch (RelayServiceException e) {
Debug.LogException(e);
throw e;
}
}
● Lobby
- ロビー(部屋)を作成して、他のプレイヤーとマッチングする機能
- フレンド間でロビーコードを共有して参加できる、いわゆるプライベートマッチなども再現できる
- ロビーの検索機能も充実していて、フィルター機能や並び替え機能もある
使用例
ロビー作成.cs
/// <summary> ロビー作成 </summary>
public async Task<Lobby> CreateLobby(string relayJoinCode)
{
string lobbyNameID = Random.Range(1, 10000).ToString("D4");
string lobbyName = "Lobby" + lobbyNameID;
// ロビー設定
CreateLobbyOptions options = new CreateLobbyOptions {
// Relayの参加コードをロビーのデータに登録する
Data = new Dictionary<string, DataObject> {
{LOBBYS_JOIN_CODE_KEY, new DataObject(DataObject.VisibilityOptions.Member, relayJoinCode ,DataObject.IndexOptions.S1) },
},
};
try {
// まだ削除されていない場合、
await UniTask.WaitWhile(() => isCreated, cancellationToken: destroyCancellationToken);
// ロビー作成
var lobby = await LobbyService.Instance.CreateLobbyAsync(lobbyName, 2, options);
// イベント追加
await LobbyService.Instance.SubscribeToLobbyEventsAsync(lobby.Id, EventCallbacks);
print($"ロビー({lobby.Id})を作成しました");
// 作成したロビー情報を保存
isCreated = true;
JoinedLobbyID = lobby.Id;
return lobby;
}
catch (LobbyServiceException e) {
Debug.LogException(e);
throw e;
}
}
ロビー検索.cs
/// <summary> ロビー検索 </summary>
public async Task<IReadOnlyList<Lobby>> SearchLobbies()
{
// 検索オプション指定
QueryLobbiesOptions options = new QueryLobbiesOptions {
Count = queryCount, // 検索結果数
// フィルター指定
Filters = new List<QueryFilter> {
new QueryFilter(QueryFilter.FieldOptions.AvailableSlots, "0", QueryFilter.OpOptions.GT), // 満員のロビーは検索しない
},
// ソート順指定
Order = new List<QueryOrder> {
new QueryOrder(false, QueryOrder.FieldOptions.AvailableSlots), // 空き人数の降順で並び変え
},
};
try {
// 検索
QueryResponse response = await LobbyService.Instance.QueryLobbiesAsync(options);
return response.Results;
}
catch (LobbyServiceException e) {
throw e;
}
}
● MatchMaker(編集中)
- マッチング関係の機能を扱える
- プレイヤーのランクに応じたマッチングなどを簡単に実装できる
● Multiplayer Tools
- プロファイラーや、ネットワークのシミュレーションができる機能などがまとまったツール群
プロファイラー
● Multiplayer Play Mode (MPPM)
- Editor上で複数のゲームウィンドウやコンソールを表示できる機能
- プレイヤーに対してタグを付与して、チーム分けしたりできる
- ビルドする手間が減るのですごく便利ですが、プレイヤーごとにプロジェクト用のフォルダが作成されるので、容量取られるのが欠点
- また、Unityバージョン2023.1以降でのみ利用可能
- 現状ベータ版ですが、すごく便利
使用例
- Unity Authenticationとの併用には注意が必要
-
普通に匿名サインインすると、同じIDが反映されてしまうので、プレイヤーごとに異なるIDを割り当てる必要がある
-
一番簡単なのは、プレイヤーがゲームを起動するたびにIDを変更する方法
.csprotected override async void Awake() { base.Awake(); // サービス初期化 await UnityServices.InitializeAsync(); AuthenticationService.Instance.SignedIn += () => { print($"{AuthenticationService.Instance.PlayerId} was signedin."); }; #if UNITY_EDITOR // リセット(MPPM用) AuthenticationService.Instance.ClearSessionToken(); #endif await AuthenticationService.Instance.SignInAnonymouslyAsync(); SetEvents(); }
- ただ、この方法だとIDが変更されて起動するたびにオンラインで保存されたデータなどがリセットされる
- 今後のMPPMのアップデートに期待…?
-
■ さいごに
- 情報量が多く、学習コスト高めですが、汎用性高めなのでPhotonよりも便利だと思います
- でも結構コストかかるんで、簡易的なプロジェクトはPhotonでいいかもです
● 参考
UGS
Netcode
参考動画
Unity Authenticatoin
Relay
参考動画
Lobby
参考動画
Other
Unity TransPort
Multiplayer Tools
MPPM