I am building a multiplayer FPS using Netcode for GameObjects in shared authority mode.
I wanted to sync the transforms of bullets by setting:
- spawn pose
- network-synced time that the bullet was shot
and use time-since-shot to position the bullet along its ray.
private void Update() {
float timeSinceSpawned = currentTime - spawnTime;
`transform.position = spawnPos + transform.forward * timeSinceSpawned;
}
I thought that NetworkManager.LocalTime would work, as docs suggested the value was accurate across all clients, but bullets shot by other players would appear either several meters in-front or behind their gun for the observer.
I made a quick sanity check using the code provided by the docs where a cube should ping-pong in sync between clients. When I add a simulated network delay, the cubes move out-of-sync which, if my understanding of NetworkManager.LocalTime is correct, should not be. In the attached video one of the clients is the Host.
Is there a better way to sync these bullet transforms? Is my understanding of LocalTime incorrect?
You mean the current animation pose? Since that is affected by local time ie not network synchronized and interpolated with local frame times this may add additional inaccuracies.
I’m just taking an educated guess here base on the assumption that the animation position is based on local blend times and I don’t think animations are synchronized all that perfectly when running on two computers each running at different framerates. That effect would get compounded the faster the animation works, be it a keyframe animation or merely the player rotating the camera.
Yes: you don’t sync them! Particularly if the FPS is either coop or only casual-competitive. Which I assume is the case since you seem to be using Netcode for GameObjects, which is specifically marketed as “for coop games” primarily because it lacks important competitive features like server-side rollback.
For each client, the shots are fired locally. It’s the server who needs to determine whether and what a shot hits. And we only care about hitting things that take damage, walls and so forth can be totally ignored which amounts to a significant number of projectiles. While the local player only cares about the shots firing from the gun, not a meter to the side, lagging behind while moving.
Now on occassion a player may hit a target locally, but miss on the server-side. You can take two approaches, either allow the client to override the server’s decision, or just not apply damage. Same for the other way around, but if the server registers a hit but the client doesn’t you probably want to err on the shooter’s side.
Question is: do other players care about whether and what another player’s projectiles hit or miss? Not so much as they’re too busy doing their own thing.
I think I was unclear.
I am not syncing the bullet’s position continuously, only the start pose and the time at which it was shot.
private void Update() {
float timeSinceSpawned = currentTime - spawnTime;
`transform.position = spawnPos + transform.forward * timeSinceSpawned;
}
I am not doing any animations or interpolation or anything like that
I figured if I had a time value that was (nearly) synced across all players, I could (nearly) perfectly match the positions of bullets despite any network latency.
I thought that NetworkManager.LocalTime was the (nearly) synced time value I wanted. The docs mention using it to synchronize transforms without network messages: NetworkTime and ticks | Unity Multiplayer
Actually trying the example, though, suggests otherwise.
From my own experience I can say, it’s really easy to make some stupid mistake reasoning about network time interpolation. 
It takes putting all the facts on the table. Like, have you logged what the LocalTime actually is? What are current and spawn times, both network times or one of them Time.time? Perhaps one doesn’t go well with the other.
Perhaps the network time keeps advancing during the duration of a frame (there are Time.times that work like that!). Also, quite likely the network local time only gets updated every network tick which defaults to 30 Hz.
You could also try and use the LocalTick and extrapolate that with the network tick time (ie 1/30) and then compare that with LocalTime. Perhaps NGO applies some smoothing or extrapolation to that which may or may not be helpful.
Perhaps you should actually be using ServerTime as the frame of reference.
Things like that make network timing really tricky.
I am interested about timing and overall figuring this stuff out too. Rollback could be coded, I know it’s complicated, maybe somebody already figured this out.
I appreciate your reply, but I think the attached video suggests a bigger issue. It looks like the docs are misleading or something is wrong with network time. The moving cubes test is almost verbatim from the docs, and the docs suggest that the cubes should stay synchronized.