CountdownTimer.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // --------------------------------------------------------------------------------------------------------------------
  2. // <copyright file="CountdownTimer.cs" company="Exit Games GmbH">
  3. // Part of: Photon Unity Utilities,
  4. // </copyright>
  5. // <summary>
  6. // This is a basic CountdownTimer. In order to start the timer, the MasterClient can add a certain entry to the Custom Room Properties,
  7. // which contains the property's name 'StartTime' and the actual start time describing the moment, the timer has been started.
  8. // To have a synchronized timer, the best practice is to use PhotonNetwork.Time.
  9. // In order to subscribe to the CountdownTimerHasExpired event you can call CountdownTimer.OnCountdownTimerHasExpired += OnCountdownTimerIsExpired;
  10. // from Unity's OnEnable function for example. For unsubscribing simply call CountdownTimer.OnCountdownTimerHasExpired -= OnCountdownTimerIsExpired;.
  11. // You can do this from Unity's OnDisable function for example.
  12. // </summary>
  13. // <author>developer@exitgames.com</author>
  14. // --------------------------------------------------------------------------------------------------------------------
  15. using ExitGames.Client.Photon;
  16. using Photon.Realtime;
  17. using UnityEngine;
  18. using UnityEngine.UI;
  19. namespace Photon.Pun.UtilityScripts
  20. {
  21. /// <summary>This is a basic, network-synced CountdownTimer based on properties.</summary>
  22. /// <remarks>
  23. /// In order to start the timer, the MasterClient can call SetStartTime() to set the timestamp for the start.
  24. /// The property 'StartTime' then contains the server timestamp when the timer has been started.
  25. ///
  26. /// In order to subscribe to the CountdownTimerHasExpired event you can call CountdownTimer.OnCountdownTimerHasExpired
  27. /// += OnCountdownTimerIsExpired;
  28. /// from Unity's OnEnable function for example. For unsubscribing simply call CountdownTimer.OnCountdownTimerHasExpired
  29. /// -= OnCountdownTimerIsExpired;.
  30. ///
  31. /// You can do this from Unity's OnEnable and OnDisable functions.
  32. /// </remarks>
  33. public class CountdownTimer : MonoBehaviourPunCallbacks
  34. {
  35. /// <summary>
  36. /// OnCountdownTimerHasExpired delegate.
  37. /// </summary>
  38. public delegate void CountdownTimerHasExpired();
  39. public const string CountdownStartTime = "StartTime";
  40. [Header("Countdown time in seconds")]
  41. public float Countdown = 5.0f;
  42. private bool isTimerRunning;
  43. private int startTime;
  44. [Header("Reference to a Text component for visualizing the countdown")]
  45. public Text Text;
  46. /// <summary>
  47. /// Called when the timer has expired.
  48. /// </summary>
  49. public static event CountdownTimerHasExpired OnCountdownTimerHasExpired;
  50. public void Start()
  51. {
  52. if (this.Text == null) Debug.LogError("Reference to 'Text' is not set. Please set a valid reference.", this);
  53. }
  54. public override void OnEnable()
  55. {
  56. Debug.Log("OnEnable CountdownTimer");
  57. base.OnEnable();
  58. // the starttime may already be in the props. look it up.
  59. Initialize();
  60. }
  61. public override void OnDisable()
  62. {
  63. base.OnDisable();
  64. Debug.Log("OnDisable CountdownTimer");
  65. }
  66. public void Update()
  67. {
  68. if (!this.isTimerRunning) return;
  69. float countdown = TimeRemaining();
  70. this.Text.text = string.Format("Game starts in {0} seconds", countdown.ToString("n0"));
  71. if (countdown > 0.0f) return;
  72. OnTimerEnds();
  73. }
  74. private void OnTimerRuns()
  75. {
  76. this.isTimerRunning = true;
  77. this.enabled = true;
  78. }
  79. private void OnTimerEnds()
  80. {
  81. this.isTimerRunning = false;
  82. this.enabled = false;
  83. Debug.Log("Emptying info text.", this.Text);
  84. this.Text.text = string.Empty;
  85. if (OnCountdownTimerHasExpired != null) OnCountdownTimerHasExpired();
  86. }
  87. public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
  88. {
  89. Debug.Log("CountdownTimer.OnRoomPropertiesUpdate " + propertiesThatChanged.ToStringFull());
  90. Initialize();
  91. }
  92. private void Initialize()
  93. {
  94. int propStartTime;
  95. if (TryGetStartTime(out propStartTime))
  96. {
  97. this.startTime = propStartTime;
  98. Debug.Log("Initialize sets StartTime " + this.startTime + " server time now: " + PhotonNetwork.ServerTimestamp + " remain: " + TimeRemaining());
  99. this.isTimerRunning = TimeRemaining() > 0;
  100. if (this.isTimerRunning)
  101. OnTimerRuns();
  102. else
  103. OnTimerEnds();
  104. }
  105. }
  106. private float TimeRemaining()
  107. {
  108. int timer = PhotonNetwork.ServerTimestamp - this.startTime;
  109. return this.Countdown - timer / 1000f;
  110. }
  111. public static bool TryGetStartTime(out int startTimestamp)
  112. {
  113. startTimestamp = PhotonNetwork.ServerTimestamp;
  114. object startTimeFromProps;
  115. if (PhotonNetwork.CurrentRoom.CustomProperties.TryGetValue(CountdownStartTime, out startTimeFromProps))
  116. {
  117. startTimestamp = (int)startTimeFromProps;
  118. return true;
  119. }
  120. return false;
  121. }
  122. public static void SetStartTime()
  123. {
  124. int startTime = 0;
  125. bool wasSet = TryGetStartTime(out startTime);
  126. Hashtable props = new Hashtable
  127. {
  128. {CountdownTimer.CountdownStartTime, (int)PhotonNetwork.ServerTimestamp}
  129. };
  130. PhotonNetwork.CurrentRoom.SetCustomProperties(props);
  131. Debug.Log("Set Custom Props for Time: "+ props.ToStringFull() + " wasSet: "+wasSet);
  132. }
  133. }
  134. }