How to Synchronize Bullets and other Game Objects across Clients?

I am working on a Top-Down 2D Multiplayer Shooter, and so far I have been able to use Netcode for Game Objects to get a Host / Server running and multiple clients connected. The problem I am encountering is with getting bullets synchronized across the clients and server. Below is the code on the Player Object that does the shooting.

public class Shooting : NetworkBehaviour
{
    public Transform firePoint;
    public GameObject bulletPrefab;
    public float bulletForce = 5f;

    // Update is called once per frame
    void Update()
    {
        if (!IsOwner) return;

        if(Input.GetMouseButtonDown(0))
        {
            ShootServerRpc();
        }
    }

    [ServerRpc]
    private void ShootServerRpc()
    {
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        bullet.GetComponent<NetworkObject>().Spawn(true);
        bullet.GetComponent<Rigidbody2D>().AddForce(firePoint.up * bulletForce, ForceMode2D.Impulse);
    }
}

And here is the script that is on the bullet:

public class Bullet : NetworkBehaviour
{
    public GameObject hitEffect;

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "player")
        {
            Debug.Log("Hit Player!");
            collision.gameObject.GetComponent<TopDownMovement>().TakeDamageServerRpc();
        }
        BulletHitServerRpc();
    }

    [ServerRpc]
    private void BulletHitServerRpc()
    {
        GameObject effect = Instantiate(hitEffect, transform.position, transform.rotation);
        Destroy(effect, 1f);
        //GetComponent<NetworkObject>().Despawn(gameObject);
        Destroy(gameObject);
    }
}

It’s worth noting that I have a movement script on the player that makes the firepoint always point towards the mouse, so whenever the Shooting Server RPC is called, the force is indeed added in the correct direction and the bullet flies forward. The issue is this: The bullet appears on the clients later than it does on the server / host, and it lags behind. Then, when the bullet hits an object or wall on the server, it despawns, causing the bullet to just vanish mid-shot on the clients. The bullets are being moved via a force applied to their RigidBody2Ds, and they report their position to the server with the “Client Network Transform” scripts, since I am not worried about client/server authority or players cheating. Lastly I have also tried shooting bullets with and without the “Network RigidBody2D” script to the same effect on both.

Is there a way to sync up bullets so the game is playable for clients? Thanks in advance for all the help!

Bullets, or any fast moving object for that matter, are typically not synchronized over the network. It depends on the speed of the objects for sure. A missile that’s maybe twice as fast as a player should probably be synchronized, but bullets that travel ten times faster should not be synchronized. More so if we are talking actual gunfire - those are typically implemented by raycasting because their velocity can be considered “infinite” in terms of game speed except for extremely realistic sim shooters.

The way to go about this, assuming a client starts firing fast bullets:

  • client tells server that it starts firing (server relays that to other clients)
  • client locally spawns bullets (not synchronized)
  • other clients also locally spawn high-speed bullets coming from the shooting client given the info they got from the server - the shooting commences until the server tells clients that the shooting client has stopped shooting
  • server knows client’s weapon, firing rate and all that and calculates any impacts and sends that info to clients

Sometimes bullets may miss but hit, or visually hit but actually do not inflict damage. You’ve seen that in other games - this is to be expected unless the server simulation can do the fancy stuff NGO currently doesn’t yet do, mainly server-side rollback (calculating the state of the simulation back at the time when the client started shooting, not when the message was received by the server).

1 Like