For this type of game, I might try disabling the interpolation on the NetworkTransforms.
Been looking at this since the Dev Bitz and there are some issues that I noticed:
-
The laser canon transform should be positioned out at the tip of the laser in the prefab
-
The NetworkObjectSpawner sort of hides this ability, so until the sample is updated you could write your own or use the method replacement posted below.
-
There is a mix-match between owner(client) and server NetworkTransforms…the ship is a ClientNetworkTransform and the bullets are normal NetworkTransforms. This means the client will always be at least 1 network tick (if not more depending upon latency) ahead of the lasers.
-
I have a branch that I might see if the samples team would like me to submit that converts the laser buller itself into a ClientNetworkTransform and I spawn the laser bullet with the client/player having ownership.
-
This doesn’t fix the issue all together but it does reduce the latent visuals some.
-
There is also the issue of getting visuals to look right when hosting because:
-
The host needs to spawn its local client’s laser bullets “behind” the current local m_cannonPosition for it too look “closer to right” on the connected client(s) side(s).
-
The clients need to send their current position to the host in order for the host to spawn the bullets more “relative to the client’s real position and not interpolated local position”.
With this all said, there are some things that could be updated in this particular sample to help reduce some of the issues but it won’t get rid of the core issue which is the host client is immediately updated with newly spawned objects while clients will always lag by half RTT (possibly a good case for client-side spawning).
But none of this helps you right now today… soo… here are a few options:
Option #1: Switch everything over to server authoritative (i.e. only use NetworkTransform and not ClientNetworkTransform) and keep like a 60 tick rate. This will assure that everything is synchronized by the host/server. The host player would obviously have the advantage here unless you polled the connected clients’ RTT and added an artificial delay to the host side (regarding player input) in order to “level the playing field”.
foreach(var clientId in NetworkManager.ConnectedClientsIds)
{
averageLatency += NetworkManager.NetworkConfig.NetworkTransport.GetCurrentRtt(clientId);
}
if (NetworkManager.ConnectedClientsIds.Count > 0)
{
averageLatency = averageLatency / NetworkManager.ConnectedClientsIds.Count;
}
Option #2: Stick with the ClientNetworkTransform approach (a bit trickier) which requires adjustments to the laser bullet. This was my initial approach when trying to see if I could reduce the latent visuals (which I did to a degree…), I started with creating a “bullet collider controller” that I setup to spawn as the server being the owner, I removed the RigidBody from the Bullet prefab and added the new “bullet collider controller” prefab as part of the bullet spawning process. On BulletCollider.OnNetworkSpawn I have the server instantiate and parent this collider under the bullet. This makes the bullets “owner authoritative” when it comes to synchronizing the motion but “server authoritative” when it comes to collision:
public override void OnNetworkSpawn()
{
if (IsServer && ServerCollider != null)
{
var objectInstance = Instantiate(ServerCollider);
m_ServerColliderInstance = objectInstance.GetComponent<NetworkObject>();
m_ServerColliderInstance.Spawn();
m_ServerColliderInstance.TrySetParent(NetworkObject, false);
}
base.OnNetworkSpawn();
}
The “BulletColliderController” invokes the same code that was within the BulletController but was migrated out of the BulletController.OnTriggerEnter2D and into a new method “BulletController.OnCollide” that the BulletColliderController invokes:
public class BulletColliderController : NetworkBehaviour
{
private BulletController m_BulletController;
public override void OnNetworkObjectParentChanged(NetworkObject parentNetworkObject)
{
if (parentNetworkObject != null)
{
m_BulletController = parentNetworkObject.GetComponent<BulletController>();
}
base.OnNetworkObjectParentChanged(parentNetworkObject);
}
private void OnTriggerEnter2D(Collider2D collider)
{
if (!IsServer || m_BulletController == null)
return;
m_BulletController.OnCollide(collider);
}
}
This assures the server is still synchronizing/controlling the collisions of the bullets, but the bullets themselves are more visually “accurate” on the client side. However, in the end interpolation will always make the bullets latent by at least 1 network tick… so since the bullets are basically “straight lines” you can easily see the latency involved in how NetworkTransform currently handles its updates (once per network tick…the higher the tick value…the smaller the gap… i.e. 1.0f/NetworkConfig.Tick is the frequency).
Here is a screenshot of the host shooting (right side) and how the client (left side) sees the host with the changes discussed along with some latency calculations and other minor tweaks to get an average “spawning position” between the host and clients:
Here is the client shooting (left side) and how the server/host sees it:
(it is hard to grab screens…next time I do a video)
Either case, the laser bullets’ positions are updated by the client (left hand side) which you can see the host (right hand side) is lagging behind. I was “just firing” on the client side (you can see the parented at the tip of the laser cannon point) but it had yet to spawn when I grabbed that screenshot.
As you are looking at the screenshots, remember the first image the host is moving down rapidly…so launching something will always “lag behind” a bit because…well the host ship is moving downwards right after shooting… same with the client side (second screenshot) but it was moving upwards when I got the screenshot.
This reduces the wide gap that you all have been seeing, but it doesn’t resolve the issue completely.
To resolve the issue visually, I am thinking the visuals would need to be separate from the NetworkObject’s with the ClientNetworkTransform/NetworkTransform (depending upon which way you go with it). The “visual Objects” would then adjust themselves based on who was viewing them and would need to adjust based on:
(assuming ClientNetworkTransform)
-
If owner relative then it most likely would only offset slightly based on like half RTT between the client and the server taking the average velocity of the object moving into consideration.
-
If the host then it would slightly need to adjust ahead since the host sends a position that is slightly behind its current in order to make it “look better” on clients.
-
If it is a client that is not an owner then it needs to know the RTT between the server and the owner (something the server would update the non-authoritative sides with) as well as include half of its own RTT into the calculations to come up with an offset for the visuals that would closer match the host’s visuals.
That is the “general idea” but is not a full explanation…if I get a chance to submit this branch (need to clear it with the Samples group) I will post a link to it here.