- Home /
Photon Multiplayer Countdown TImer
Hey everyone
I'm currently trying to get a countdown timer to work correctly online. The countdown start once the first person enters the room online, but The problem is when another player enters the room the countdown for that player starts from the beginning '60 sec' instead of sharing the current countdown started by the first player who entered.
I've tried making the countdown method a RPC allbuffed but i'm honestly not that familiar with network programming. Can anyone me out?
Thanks in advance
using UnityEngine;
using System.Collections;
public class CountDownTimer : MonoBehaviour
{
public float elevatorCountdown;
public bool startElevators;
private PhotonView myphotonView;
void Start ()
{
myphotonView = gameObject.GetComponent<PhotonView>();
}
void Update ()
{
//StartElevatorCountdown();
myphotonView.RPC("StartElevatorCountdown", PhotonTargets.AllBuffered);
}
[PunRPC]
void StartElevatorCountdown()
{
this.elevatorCountdown -= Time.deltaTime;
if (this.elevatorCountdown <= 0)
{
this.elevatorCountdown = 0;
startElevators = true;
}
}
void OnGUI()
{
if (elevatorCountdown > 0)
{
GUI.Label (new Rect (100, 100, 200, 100), "Elevators will depart in " + (int)elevatorCountdown + "seconds");
}
}
}
Only the master client should start the countdown. Sending an RPC every frame is not advisable, use OnPhotonSerializeView. Use PhotonNetwork.time to synchronize timed operations over a network.
A better approach would be for the master to send an RPC with the current network timestamp, then anyone joining would calculate the difference between the start time and the current network time.
note: this code is untested and only meant as an example :
public float elevatorCountdown = 300f; // 5 $$anonymous$$s
void Start()
{
if ( PhotonNetwork.is$$anonymous$$asterClient ) // host starts the countdown
{
myphotonView.RPC( "StartElevatorCountdown", PhotonTargets.AllBuffered, PhotonNetwork.Time );
}
}
[PunRPC]
void StartElevatorCountdown( double elevatorCountdownTimerStart )
{
elevatorCountdown -= (float)( PhotonNetwork.Time - elevatorCountdownTimerStart );
StartCoroutine( "ElevatorCountdown" );
}
IEnumerator ElevatorCountdown()
{
while ( elevatorCountdown > 0f )
{
yield return new WaitForEndOfFrame();
elevatorCountdown -= Time.deltaTime;
}
startElevators = true;
}
Hey
Thanks for the help, I've tried out your code is their any reason why my onGUI would not work for connected player now? When players connect now their countdown just stays at 60sec.
I did get this error GetLocalizedString can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, ins$$anonymous$$d move initialization code to the Awake or Start function.
Am I correct in thinking it's talking about the Coroutine?
That is a strange error. A search found this : http://forum.unity3d.com/threads/what-does-this-error-mean.338592/
So it seems to be a problem with loading level tree prefab terrain. If you double-click on the error message in the console, it should take you to the line in your script that it is regarding.
If you have errors that's probably why the OnGUI function isn't working. I did notice you havn't converted elevator countdown to a string.
GUI.Label (new Rect (100, 100, 200, 100), "Elevators will depart in " + ( (int)elevatorCountdown ).ToString() + "seconds");
Answer by ibouchallikht · Jun 10, 2017 at 10:27 AM
I don't know if this is to late, but you have to call the RPC as masterclient to all clients..
Hi man! Could you please give more details?
I'm trying to do basically the same thing but i can't understand how to read the RPC. I put the following code on a gameobject always present in the scene which has a photon view on it, but I can't find a solution.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimerCountdown : Photon.$$anonymous$$onoBehaviour {
private float startTime = 10;
public static string TimeCountdown;
string timestring;
// Use this for initialization
void Start () {
startTime = 180;
}
// Update is called once per frame
void Update () {
if(PhotonNetwork.playerList.Length >= 2)
{
float t = 0;
t = Time.time;
int $$anonymous$$ = 3 - ((int)t / 60);
int sec = 59 - ((int)t % 60);
string $$anonymous$$utes = $$anonymous$$.ToString();
string seconds = sec.ToString();
timestring = $$anonymous$$utes + ":" + seconds;
photonView.RPC("TimeLeft", PhotonTargets.AllBufferedViaServer, timestring);
TimeCountdown = TimeLeft(timestring);
}
else
{
TimeCountdown = "--:--";
}
}
[PunRPC]
public string TimeLeft(string timeleft)
{
string receivedString = timeleft;
Debug.Log(receivedString);
return receivedString;
}
}
I appreciate any help.
Cheers
Hi,
sending RPCs in each Update call is a bad idea, because you will waste a lot of traffic and messages. A solution for this might be to store a start time in the Custom Room Properties and let each client calculate the timer on their own. Therefore add the start time (you can get this by using PhotonNetwork.ServerTimestamp) and the timer duration to the Custom Room Properties. Some thing like this:
int startTime = PhotonNetwork.ServerTimestamp;
int duration = 180;
Hashtable ht = new Hashtable() { { "startTime", startTime }, { "duration", duration } };
PhotonNetwork.room.SetCustomProperties(ht);
The clients will receive those Properties when OnPhotonCustomRoomPropertiesChanged gets called. When receiving both values, they can calculate the timer in the Update function locally on their own.
public void OnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged)
{
if (propertiesThatChanged.Contains$$anonymous$$ey("startTime") && propertiesThatChanged.Contains$$anonymous$$ey("duration"))
{
storedStartTime = (int)propertiesThatChanged["startTime"];
storedDuration = (int)propertiesThatChanged["duration"];
}
}
public void Update()
{
// Check PhotonView.is$$anonymous$$ine condition before doing anything
if (storedDuration != 0 && storedDuration != 0)
{
int timerDuration = (PhotonNetwork.ServerTimestamp - storedStartTime) / 1000;
int currentTimer = storedDuration - timerDuration;
Debug.Log(timerDuration + " " + currentTimer);
}
}
Additionally it would be a good idea to store a bool value, too, which describes if the timer is running.
Answer by alarm656 · Feb 15, 2018 at 01:38 PM
void Update()
{
if (startRoundWhenTimeIsSynced)
{
this.StartRoundNow (); // the "time is known" check is done inside the method.
}
if (PhotonNetwork.playerList.Length >= 2)
{
pV.RPC ("CountDown", PhotonTargets.AllBufferedViaServer);
}
}
[PunRPC]
void CountDown ()
{
double elapsedTime = (PhotonNetwork.time - StartTime);
double remainingTime = SecondsPerTurn - (elapsedTime % SecondsPerTurn);
int turn = (int)(elapsedTime / SecondsPerTurn);
t.text = "Игра начинается через: " + remainingTime.ToString ("f0");
if (remainingTime >= 0)
{
print ("WorksPerfect");
}
}