I have a basic finite-state machine for controlling turn-based play, but I am finding that SyncVar is practically worthless to me in syncing the state because the hook isn’t called with every change.
Here’s what I’m trying:
class GameController : NetworkBehaviour // instantiated by the client
{
enum GameState
{
TurnStart,
PlayerTurn,
TurnEnd
// etc.
};
[SyncVar(hook = "OnGameStateChanged")]
GameState state;
private void OnGameStateChanged(GameState newState)
{
state = newState;
// do some work
}
private void Update()
{
if (!isServer)
{
return;
}
switch (state)
{
case GameState.TurnStart:
// do some work
state = GameState.PlayerTurn;
break;
case GameState.PlayerTurn:
// updated by player Command elsewhere in script
break;
case GameState.TurnEnd:
// do some work
state = GameState.TurnStart; // start the next player's turn
default:
break;
}
}
// example of player Command located in other script
[Command]
private void CmdEndTurn()
{
state = GameState.TurnEnd;
// work is done in Update
}
}
I am testing this on a connection between one host (server+client) and one other client. I would expect the OnGameStateChanged method to be invoked on the client every time that state is changed. What I’m actually experiencing on the client is it going straight from GameState.PlayerTurn back to GameState.TurnStart, which is to say that the hook is never being invoked with GameState.TurnEnd. This means that my client is missing vital synchronizations.
What I’ve had to do instead is remove the SyncVar altogether and just use an RPC:
class GameController : NetworkBehaviour // instantiated by the client
{
enum GameState
{
TurnStart,
PlayerTurn,
TurnEnd
// etc.
}
GameState state;
[ClientRpc]
private void RpcOnGameStateChanged(GameState newState)
{
state = newState;
// do some work
}
private void Update()
{
if (!isServer)
{
return;
}
GameState oldState = state;
switch (state)
{
case GameState.TurnStart:
// do some work
state = GameState.PlayerTurn;
break;
case GameState.PlayerTurn:
// updated by player Command elsewhere in script
break;
case GameState.TurnEnd:
// do some work
state = GameState.TurnStart; // start the next player's turn
default:
break;
}
if (state != oldState)
{
RpcOnGameStateChanged(state);
}
}
// example of player Command located in other script
[Command]
private void CmdEndTurn()
{
state = GameState.TurnEnd;
// work is done in Update
}
}
This workaround works the way I need it to, but is there any way to make a SyncVar call its hook on the clients with every change?