Hello. I am making a multiplayer party game using Photon Pun where mobile device players (Android) connect to a VR player and play against each other in a series of minigames. I have run into a major issue when loading the minigame scenes however and have narrowed it down to one script in particular (the script previously worked fine but something I have changed has caused this bug). The script runs all of the minigames basic logic such as waiting for all players to load the scene, countdown timers, etc. The script works fine on the VR headset build but on the mobile build the game immediately reloads into the game select scene (a simple scene that displays the upcoming minigame). I have stepped through the script with the debugger and tried everything I can think of but as far as I can see there should be nothing in this script that should causes this behaviour. When I disable it however, the correct scene stays loaded. I would really appreciate any help on this as I have worked on it on and off for literally weeks now.
The code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using ExitGames.Client.Photon;
using Hashtable = ExitGames.Client.Photon.Hashtable;
using TMPro;
using UnityEngine.Events;
public class NetworkMinigameController : MonoBehaviourPunCallbacks
{
public enum GameState
{
waiting,
starting,
inProgress,
finished
}
private GameState currentGameState;
public const byte START_TIMER_EVENT_CODE = 1;
public double startCountdownTime = 3f;
public double gameOverCooldown = 3f;
private bool countingDown = false;
private double startTime = -1;
private double timerDuration;
public Timer timerDisplay;
public int resultsRoomIndex;
public TextMeshProUGUI livesRemainingText;
public int StartingLives = 3;
public UnityEvent GameStarting;
public UnityEvent GameStarted;
public UnityEvent GameEnd;
public UnityEvent OutOfLives;
private bool eliminated = false;
public override void OnEnable()
{
//start listening for networking events
PhotonNetwork.NetworkingClient.EventReceived += NetworkingClientEventRecieved;
base.OnEnable();
}
public override void OnDisable()
{
//stop listening for network events
PhotonNetwork.NetworkingClient.EventReceived -= NetworkingClientEventRecieved;
base.OnDisable();
}
void Start()
{
//all players set their starting values
Hashtable _table = PhotonNetwork.LocalPlayer.CustomProperties;
_table["Ready"] = false;
_table["Lives"] = StartingLives;
_table["GameScore"] = 0;
PhotonNetwork.LocalPlayer.SetCustomProperties(_table);
currentGameState = GameState.waiting;
//master client game setup
if (PhotonNetwork.IsMasterClient)
{
//start in waiting state (waiting for all players to be ready)
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["GameState"] = GameState.waiting;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
}
}
void Update()
{
//countdown timer
if (countingDown)
{
//updating clock
double currentTimer = PhotonNetwork.Time - startTime;
//timer display
timerDisplay.timeRemaining = timerDuration - currentTimer;
//master check times up
if (PhotonNetwork.IsMasterClient)
{
//timer up!
if (currentTimer >= timerDuration)
{
Debug.Log("Times up!");
countingDown = false;
if (currentGameState == GameState.starting)
{
ChangeGameState(GameState.inProgress);
}
else if (currentGameState == GameState.inProgress)
{
ChangeGameState(GameState.finished);
}
else if (currentGameState == GameState.finished)
{
PhotonNetwork.LoadLevel(resultsRoomIndex);
Debug.Log("Round over. Loading results screen...");
}
}
}
}
}
public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
//the game state has changed
GameState networkGameState = (GameState)PhotonNetwork.CurrentRoom.CustomProperties["GameState"];
if (networkGameState != currentGameState)
{
//update local game state
currentGameState = networkGameState;
//GAME COUNTING DOWN TO START (all players are ready, small delay to actual minigame starts)
if (currentGameState == GameState.starting)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(startCountdownTime);
GameStarting.Invoke();
}
//GAME STARTED (the start delay has finished and the game is beginning)
else if (currentGameState == GameState.inProgress)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(MyGameController.currentMinigame.roundDuration);
GameStarted.Invoke();
}
//GAME END (the game has been won or the timer has finished)
else if (currentGameState == GameState.finished)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(gameOverCooldown);
GameEnd.Invoke();
}
//keep an eye on this!
if (startTime != (double)PhotonNetwork.CurrentRoom.CustomProperties["StartTime"])
{
StartTimer((double)PhotonNetwork.CurrentRoom.CustomProperties["StartTime"]);
}
}
base.OnRoomPropertiesUpdate(propertiesThatChanged);
}
public override void OnPlayerPropertiesUpdate(Photon.Realtime.Player targetPlayer, Hashtable changedProps)
{
//master client waiting for players to be ready
if (PhotonNetwork.IsMasterClient)
{
if (currentGameState == GameState.waiting)
{
if (CheckPlayersReady())
{
//all players are ready so begin the coundown to round start
ChangeGameState(GameState.starting);
}
}
if (currentGameState == GameState.inProgress)
{
if (!AnyAlivePlayers()) //no more players still alive and playing
{
Debug.Log("All players eliminated");
ChangeGameState(GameState.finished);
}
}
}
//local player lives
if (targetPlayer.IsLocal)
{
livesRemainingText.text = PhotonNetwork.LocalPlayer.CustomProperties["Lives"].ToString();
//no more lives
if ((int)PhotonNetwork.LocalPlayer.CustomProperties["Lives"] <= 0)
{
if (!eliminated)
{
OutOfLives.Invoke();
eliminated = true;
Debug.Log(PhotonNetwork.LocalPlayer + "was eliminated!");
}
}
}
base.OnPlayerPropertiesUpdate(targetPlayer, changedProps);
}
bool CheckPlayersReady()
{
foreach(var player in PhotonNetwork.PlayerList)
{
bool isReady = (bool)player.CustomProperties["Ready"];
if (isReady)
{
Debug.Log(player + "is ready");
continue;
}
else
{
//found a player that is not ready
return false;
}
}
//all players ready
Debug.Log("All players ready");
return true;
}
bool AnyAlivePlayers()
{
foreach(var player in PhotonNetwork.PlayerList)
{
bool dead = (int)player.CustomProperties["Lives"] <= 0;
if (dead)
{
continue;
}
else
{
//found a player that is alive! the game can continue...
return true;
}
}
//all players alive
return false;
}
void ChangeGameState(GameState newState)
{
//change the network room properties to the new state
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["GameState"] = newState;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
Debug.Log("Game state update: " + newState.ToString());
}
public void StartNetworkTimer(double duration)
{
Debug.Log("Starting Game");
//master client sets the network room timestamp
if (PhotonNetwork.IsMasterClient)
{
var _startTime = PhotonNetwork.Time;
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["StartTime"] = _startTime;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
timerDisplay.duration = duration;
//tell all players to start timer
//StartNetworkTimerEvent(duration);
}
}
public void NetworkingClientEventRecieved(EventData data)
{
//start timer event
if (data.Code == START_TIMER_EVENT_CODE)
{
object[] content = (object[])data.CustomData;
StartTimer((double)content[0]);
}
}
void StartNetworkTimerEvent(double duration)
{
object[] data = new object[]{duration};
RaiseEventOptions raiseEventOptions = new RaiseEventOptions {Receivers = ReceiverGroup.All};
PhotonNetwork.RaiseEvent(START_TIMER_EVENT_CODE,data,raiseEventOptions,ExitGames.Client.Photon.SendOptions.SendReliable);
}
void StartTimer(double duration)
{
//get the room properties start timer
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
startTime = (double)table["StartTime"];
//start local timer
timerDisplay.duration = duration;
countingDown = true;
timerDuration = duration;
}
}
,I am making a multiplayer game using Photon Pun. A VR player hosts the game and mobile device (Android) players connect and play minigames against them. I have run into a major issue loading scenes (previously it worked fine but something I changed has triggered the bug) On the mobile devices the game select scene (very basic scene which displays the upcoming minigame) is immediately reloaded as soon as the game loads the minigame scene (you can see the minigame for a few frames). The script works fine on the VR headset and as far as I can tell there is shouldn’t be anything that reloads a scene. Any help is much appreciated as I have worked on this on and off for weeks now and I am honestly stumped.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using Photon.Realtime;
using ExitGames.Client.Photon;
using Hashtable = ExitGames.Client.Photon.Hashtable;
using TMPro;
using UnityEngine.Events;
public class NetworkMinigameController : MonoBehaviourPunCallbacks
{
public enum GameState
{
waiting,
starting,
inProgress,
finished
}
private GameState currentGameState;
public const byte START_TIMER_EVENT_CODE = 1;
public double startCountdownTime = 3f;
public double gameOverCooldown = 3f;
private bool countingDown = false;
private double startTime = -1;
private double timerDuration;
public Timer timerDisplay;
public int resultsRoomIndex;
public TextMeshProUGUI livesRemainingText;
public int StartingLives = 3;
public UnityEvent GameStarting;
public UnityEvent GameStarted;
public UnityEvent GameEnd;
public UnityEvent OutOfLives;
private bool eliminated = false;
public override void OnEnable()
{
//start listening for networking events
PhotonNetwork.NetworkingClient.EventReceived += NetworkingClientEventRecieved;
base.OnEnable();
}
public override void OnDisable()
{
//stop listening for network events
PhotonNetwork.NetworkingClient.EventReceived -= NetworkingClientEventRecieved;
base.OnDisable();
}
void Start()
{
//all players set their starting values
Hashtable _table = PhotonNetwork.LocalPlayer.CustomProperties;
_table["Ready"] = false;
_table["Lives"] = StartingLives;
_table["GameScore"] = 0;
PhotonNetwork.LocalPlayer.SetCustomProperties(_table);
currentGameState = GameState.waiting;
//master client game setup
if (PhotonNetwork.IsMasterClient)
{
//start in waiting state (waiting for all players to be ready)
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["GameState"] = GameState.waiting;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
}
}
void Update()
{
//countdown timer
if (countingDown)
{
//updating clock
double currentTimer = PhotonNetwork.Time - startTime;
//timer display
timerDisplay.timeRemaining = timerDuration - currentTimer;
//master check times up
if (PhotonNetwork.IsMasterClient)
{
//timer up!
if (currentTimer >= timerDuration)
{
Debug.Log("Times up!");
countingDown = false;
if (currentGameState == GameState.starting)
{
ChangeGameState(GameState.inProgress);
}
else if (currentGameState == GameState.inProgress)
{
ChangeGameState(GameState.finished);
}
else if (currentGameState == GameState.finished)
{
PhotonNetwork.LoadLevel(resultsRoomIndex);
Debug.Log("Round over. Loading results screen...");
}
}
}
}
}
public override void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
{
//the game state has changed
GameState networkGameState = (GameState)PhotonNetwork.CurrentRoom.CustomProperties["GameState"];
if (networkGameState != currentGameState)
{
//update local game state
currentGameState = networkGameState;
//GAME COUNTING DOWN TO START (all players are ready, small delay to actual minigame starts)
if (currentGameState == GameState.starting)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(startCountdownTime);
GameStarting.Invoke();
}
//GAME STARTED (the start delay has finished and the game is beginning)
else if (currentGameState == GameState.inProgress)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(MyGameController.currentMinigame.roundDuration);
GameStarted.Invoke();
}
//GAME END (the game has been won or the timer has finished)
else if (currentGameState == GameState.finished)
{
Debug.Log("Game state changed: " + currentGameState);
StartNetworkTimer(gameOverCooldown);
GameEnd.Invoke();
}
//keep an eye on this!
if (startTime != (double)PhotonNetwork.CurrentRoom.CustomProperties["StartTime"])
{
StartTimer((double)PhotonNetwork.CurrentRoom.CustomProperties["StartTime"]);
}
}
base.OnRoomPropertiesUpdate(propertiesThatChanged);
}
public override void OnPlayerPropertiesUpdate(Photon.Realtime.Player targetPlayer, Hashtable changedProps)
{
//master client waiting for players to be ready
if (PhotonNetwork.IsMasterClient)
{
if (currentGameState == GameState.waiting)
{
if (CheckPlayersReady())
{
//all players are ready so begin the coundown to round start
ChangeGameState(GameState.starting);
}
}
if (currentGameState == GameState.inProgress)
{
if (!AnyAlivePlayers()) //no more players still alive and playing
{
Debug.Log("All players eliminated");
ChangeGameState(GameState.finished);
}
}
}
//local player lives
if (targetPlayer.IsLocal)
{
livesRemainingText.text = PhotonNetwork.LocalPlayer.CustomProperties["Lives"].ToString();
//no more lives
if ((int)PhotonNetwork.LocalPlayer.CustomProperties["Lives"] <= 0)
{
if (!eliminated)
{
OutOfLives.Invoke();
eliminated = true;
Debug.Log(PhotonNetwork.LocalPlayer + "was eliminated!");
}
}
}
base.OnPlayerPropertiesUpdate(targetPlayer, changedProps);
}
bool CheckPlayersReady()
{
foreach(var player in PhotonNetwork.PlayerList)
{
bool isReady = (bool)player.CustomProperties["Ready"];
if (isReady)
{
Debug.Log(player + "is ready");
continue;
}
else
{
//found a player that is not ready
return false;
}
}
//all players ready
Debug.Log("All players ready");
return true;
}
bool AnyAlivePlayers()
{
foreach(var player in PhotonNetwork.PlayerList)
{
bool dead = (int)player.CustomProperties["Lives"] <= 0;
if (dead)
{
continue;
}
else
{
//found a player that is alive! the game can continue...
return true;
}
}
//all players alive
return false;
}
void ChangeGameState(GameState newState)
{
//change the network room properties to the new state
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["GameState"] = newState;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
Debug.Log("Game state update: " + newState.ToString());
}
public void StartNetworkTimer(double duration)
{
Debug.Log("Starting Game");
//master client sets the network room timestamp
if (PhotonNetwork.IsMasterClient)
{
var _startTime = PhotonNetwork.Time;
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
table["StartTime"] = _startTime;
PhotonNetwork.CurrentRoom.SetCustomProperties(table);
timerDisplay.duration = duration;
//tell all players to start timer
//StartNetworkTimerEvent(duration);
}
}
public void NetworkingClientEventRecieved(EventData data)
{
//start timer event
if (data.Code == START_TIMER_EVENT_CODE)
{
object[] content = (object[])data.CustomData;
StartTimer((double)content[0]);
}
}
void StartNetworkTimerEvent(double duration)
{
object[] data = new object[]{duration};
RaiseEventOptions raiseEventOptions = new RaiseEventOptions {Receivers = ReceiverGroup.All};
PhotonNetwork.RaiseEvent(START_TIMER_EVENT_CODE,data,raiseEventOptions,ExitGames.Client.Photon.SendOptions.SendReliable);
}
void StartTimer(double duration)
{
//get the room properties start timer
Hashtable table = PhotonNetwork.CurrentRoom.CustomProperties;
startTime = (double)table["StartTime"];
//start local timer
timerDisplay.duration = duration;
countingDown = true;
timerDuration = duration;
}
}