I started creating a deterministic RTS in Unity in November 2017. I thought it would be fun & interesting project to recreate a ClashRoyale style game in a sci-fi setting with some additional features.
I knew from the start I wanted the game to be based around great multiplayer. I saw this as what I enjoy most in these sort of games both for the gameplay and the technical challenges involved in the dev process.
Here's a progress vlog for the first six months.
I was very impressed with how easy it was to get going with Unity. I’ve been developing games for over a decade but that doesn’t always mean it’s seamless to learn a new engine.
Getting started - determinism & multiplayer
I picked Photon TrueSync to help me get started with multiplayer. It’s been extremely useful even if I’ve not really used it in the intended way.
TrueSync is designed around a lock-step deterministic model where only the inputs are sent over the network. This was very appealing, however I didn’t want to be too hamstrung by their API. What I needed was something simpler where I could just use their tech purely for lockstep and networking and keep my simulation code separate.
I eventually ended up having a single TrueSyncBehaviour called TSPlayerInput. This allowed me to keep my own sim code fairly clean and not dependent on TrueSync.
public override void OnSyncedInput ()
{
//
// Write player inputs
m_writeInputs.Clear ();
m_simulationController.PollInput (ref m_writeInputs);
if (m_writeInputs.m_spawns.Count > 0)
{
WriteCommands (m_writeInputs.m_spawns, SpawnCommandId);
}
if (m_writeInputs.m_emotes.Count > 0)
{
WriteCommands (m_writeInputs.m_emotes, EmoteCommandId);
}
TrueSyncInput.SetInt (CheckSumId, JDX.Checksum.Instance ().GetChecksumValue ());
}
{
//
// Read inputs for each player and update the sim if it's the last player
int inputPlayerId = owner.Id;
// Each player has it's own input lists separate to other players so it is necessary
// to process each players inputs in OnSyncedUpdate.
{
ReadCommands (ref m_readInputs.m_spawns, SpawnCommandId);
ReadCommands (ref m_readInputs.m_emotes, EmoteCommandId);
m_simulationController.ReadInputs (inputPlayerId, m_readInputs);
m_readInputs.Clear ();
int playerFrameChecksum = TrueSyncInput.GetInt (CheckSumId);
m_simulationController.SetPlayerChecksum (inputPlayerId, playerFrameChecksum);
}
// OnSyncedUpdate is called for every player on each device. We only want the simulation
// to be run a single time on each device though. So only for the last player after ProcessInputs()
// has been called for each player do we tick the sim controller.
{
int lastPlayerIndex = TrueSyncManager.Players.Count - 1;
int lastPlayerId = TrueSyncManager.Players [lastPlayerIndex].Id;
if ((inputPlayerId == lastPlayerId) || (inputPlayerId == OfflinePlayerId))
{
m_simulationController.FixedUpdateNetwork ();
}
}
}
No comments:
Post a Comment