👏
Photonでマルチプレイ同期するタイマーの実装
環境
- Unity
- PUN2
やりたいこと
- マルチプレイで同期するタイマーの実装
- 誰でもスタート/ストップでき、その状況はルーム全員に共有される
- 後からルームに入った人も、その時点でのタイマーの状態に同期する
設計
- PhotonのRoomのCustomPropertiesを基軸とする(残り時間と、タイマーが動いているか、を渡す)
- あるLocalPlayerがタイマーをStart/Stopするときに、そのLocalPlayerがRoomのCustomPropertiesを更新する。
- PunCallbackのOnRoomPropertiesUpdateを用いて、Roomの全員が直近に更新されたCustomPropertiesを受け取る。(ここまでで、その時点でRoomにいるプレイヤー全員がタイマーを同期することが可能になる)
- 他のPlayerがRoomにJoinするタイミングで、RoomのMasterClientがCustomPropertiesをアップデートする。これにより、後からRoomのJoinしたプレイヤーにもタイマーが同期される。
コード
(自分のサービスのコードの重要部分を抜粋したのでこのままでは動かない)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
public class TimerManager : MonoBehaviourPunCallbacks
{
// トータル制限時間
private float remainingTimeSecond;
[SerializeField]
private float maxTimeMinute;
private bool isCounting = false;
// PhotonのCustomProperties用キー定数
private const string REMAINING_TIME_SECOND_KEY = "remainingTime";
private const string IS_COUNTING_KEY = "isCounting";
void Update()
{
if (!isCounting)
{
return;
}
// 制限時間が0秒以下なら何もしない
if (remainingTimeSecond <= 0f)
{
return;
}
CountDown();
}
private void CountDown()
{
remainingTimeSecond -= Time.deltaTime;
// Scene内のUIに残り時間を表示する
DisplayRemainingTime();
}
// 一時停止
public void Pause()
{
isCounting = false;
SetRoomCustomPropertiesForTimer();
}
// 再開
public void Resume()
{
isCounting = true;
SetRoomCustomPropertiesForTimer();
}
private void DisplayRemainingTime()
{
// Scene内のUIに残り時間を表示する
}
private void SetRoomCustomPropertiesForTimer()
{
var props = new ExitGames.Client.Photon.Hashtable();
props[REMAINING_TIME_SECOND_KEY] = remainingTimeSecond;
props[IS_COUNTING_KEY] = isCounting;
PhotonNetwork.CurrentRoom.SetCustomProperties(props);
}
private void SetTimerUI()
{
// UIのOnOffなどを制御
}
#region PunCallbacks
public override void OnPlayerEnteredRoom(Player newPlayer)
{
if (PhotonNetwork.IsMasterClient)
{
SetRoomCustomPropertiesForTimer();
}
}
public override void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged)
{
if (propertiesThatChanged[REMAINING_TIME_SECOND_KEY] != null)
{
remainingTimeSecond = (float)propertiesThatChanged[REMAINING_TIME_SECOND_KEY];
}
if (propertiesThatChanged[IS_COUNTING_KEY] != null)
{
isCounting = (bool)propertiesThatChanged[IS_COUNTING_KEY];
SetTimerUI();
}
}
#endregion
}
Discussion