今回の話
【PUN】PhotonCloudのコールバック処理をUniRxでスマートに書く「PhotonRx」 の続きの話です。
やったこと
PhotonCloudのサーバに接続する処理や、部屋に参加する処理をTask
で管理できるようにしました。
PUNのイベントをHookするコンポーネントを用意して、そこからTaskCompletionSource
を介してTask
を取得できるようにしています。
ログインイベントの取得を行うコンポーネント
public class ConnectionEventHook : MonoBehaviour
{
private object gate = new object();
private List<TaskCompletionSource<IResult<DisconnectCause, bool>>> observers
= new List<TaskCompletionSource<IResult<DisconnectCause, bool>>>();
public Task<IResult<DisconnectCause, bool>> Connect(Action connectAction)
{
var tcs = new TaskCompletionSource<IResult<DisconnectCause, bool>>();
lock (gate)
{
observers.Add(tcs);
}
connectAction();
return tcs.Task;
}
private void OnConnectedToPhoton()
{
if (PhotonNetwork.autoJoinLobby) return;
lock (gate)
{
var targets = observers.ToArray();
observers.Clear();
foreach (var t in targets)
{
t.SetResult(Success.Create<DisconnectCause, bool>(true));
}
}
}
private void OnJoinedLobby()
{
if (!PhotonNetwork.autoJoinLobby) return;
lock (gate)
{
var targets = observers.ToArray();
observers.Clear();
foreach (var t in targets)
{
t.SetResult(Success.Create<DisconnectCause, bool>(true));
}
}
}
private void OnFailedToConnectToPhoton(DisconnectCause cause)
{
lock (gate)
{
var targets = observers.ToArray();
observers.Clear();
foreach (var t in targets)
{
t.SetResult(Failure.Create<DisconnectCause, bool>(cause));
}
}
}
}
これをstaticメソッド経由で呼び出せるようにしています。
public static class PhotoTask
{
public static Task<IResult<DisconnectCause, bool>> ConnectToBestCloudServer(string gameVersion)
{
return Connect(() => PhotonNetwork.ConnectToBestCloudServer(gameVersion));
}
public static Task<IResult<DisconnectCause, bool>> ConnectToMaster(string masterServerAddress, int port, string appID, string gameVersion)
{
return Connect(() => PhotonNetwork.ConnectToMaster(masterServerAddress, port, appID, gameVersion));
}
public static Task<IResult<DisconnectCause, bool>> ConnectToRegion(CloudRegionCode region, string gameVersion)
{
return Connect(() => PhotonNetwork.ConnectToRegion(region, gameVersion));
}
public static Task<IResult<DisconnectCause, bool>> ConnectUsingSettings(string gameVersion)
{
return Connect(() => PhotonNetwork.ConnectUsingSettings(gameVersion));
}
private static Task<IResult<DisconnectCause, bool>> Connect(Action connectAction)
{
var eventHook = GetOrAddComponent<ConnectionEventHook>(PhotonEventManager.Instance.gameObject);
return eventHook.Connect(connectAction);
}
//以下略
}
導入方法
【PhotonRx】に追加機能としていれたので、こちらのライブラリを導入してください。
使用例
using UnityEngine;
using System.Threading.Tasks;
using PhotonRx;
public class TaskSample : MonoBehaviour
{
async void Start()
{
PhotonNetwork.autoJoinLobby = true;
var isConnected = await Connect();
if (!isConnected) return;
var isJoined = await JoinRoom();
Debug.Log(isJoined);
}
private async Task<bool> Connect()
{
// サーバに接続
var connect = await PhotoTask.ConnectUsingSettings("v1");
if (connect.IsFailure)
{
// 失敗
Debug.LogError(connect.ToFailure.Value);
}
return connect.IsSuccess;
}
private async Task<bool> JoinRoom()
{
// 適当な部屋に参加する
var randomJoined = await PhotoTask.JoinRandomRoom();
// 成功なら終わり
if (randomJoined.IsSuccess) return true;
// 部屋を作って参加する
var created = await PhotoTask.CreateRoom("test", null, null, null);
if (!created.IsSuccess)
{
//失敗
Debug.LogError(created.ToFailure.Value);
}
return created.IsSuccess;
}
}
Taskの結果として利用しているIResult型について
Task
の返り値としてオリジナルのIResult
型を利用しています。
public static Task<IResult<DisconnectCause, bool>> ConnectUsingSettings(string gameVersion)
public static Task<IResult<FailureReason, bool>> JoinRoom(string roomName)
こちらは関数型プログラミング言語におけるEither
型っぽいものを目指して作ったものです。
成功または失敗のどちらかを取る型であり、IsSuccess
またはIsFailure
で成否の判定が行なえます。
/// <summary>
/// 成功か失敗かどちらかの状態を表す
/// </summary>
/// <typeparam name="L">失敗時の型</typeparam>
/// <typeparam name="R">成功時の型</typeparam>
public interface IResult<L, R>
{
bool IsSuccess { get; }
bool IsFailure { get; }
Success<L, R> ToSuccess { get; }
Failure<L, R> ToFailure { get; }
IResult<L2, R2> Bind<L2, R2>(Func<L, IResult<L2, R2>> fl, Func<R, IResult<L2, R2>> fr);
IResult<L, R> AsResult { get; }
}
あとなんとなくmap
とfaltMap
も実装してみましたけど、たぶん使うことは無いでしょう。
参考
まとめ
PUNのイベントはさすがに公式でTask化してほしい。