Unity Multiplayer Using Mirror (with Rigidbodies)

(code and test scene available on the Downloads page for Patrons)

In the modern age of gaming, multiplayer is almost a requirement for any game. As good as your gameplay loop or story is people will eventually get bored if there isn’t ever anything new to experience. That’s why BRs got so popular, endless new experiences. (Or in my case, endless failure) But needless to say multiplayer adds a lot to your game.

Now usually I post SteamVR tutorials but this time we’re just going to do a simple 3D game. Disappointing I know. But I promise to put out some VR multiplayer stuff soon. For this one, we are just going to have some wasd controlled cubes rolling around with full physics.

Now the physics was hard to get right, at first I tried running physics on both clients and the syncing them, but I had a lot of problems with that so I gave up. So we are going to run all the physics on the server and just deal with it.

The thing about Mirror that makes it easier to program for than other networking is that the server runs the same code that the client runs, so you don’t have to worry about learning a new language or building two different projects. Also, you can run the server on your own computer to test it yourself so there is that.

Getting Started

To get started you will want to download the latest version of Mirror from Github, it is on the asset store but I’ve heard that it is outdated most of the time. For reference, I’m using version 5.5.5. Import the Unity package into a new project and we should be ready to start coding.

Before we do anything we need to set up our networking stuff. Create an empty, name it something like NetMan and make sure it’s at the center of your scene to satisfy my OCD. Add the NetworkManager and NetworkManagerHud script from the mirror runtime folder. The NetworkManager controls our connection to the server or clients, and the NetworkManagerHud gives us a nice HUD to control the NetworkManager.

Next, Create a plane to serve as a floor and some obstacles for you to move around and move the main camera so you can see everything. It should look something like this:

Create a cube at the center of this plane, this is going to be our avatar. Create new red material for it so we can see it against the background, A Rigidbody, and finally a NetworkIdentity Script. Now all that’s left is to add our own scripts.

We are going to use two scripts, one to handle player input, and one to handle the physics syncing.

using UnityEngine;
using Mirror;

public class PlayerControl : NetworkBehaviour
{
    [SyncVar]
    public Vector3 Control; //This is a sync var, mirror automatically shares and syncs this variable across all of the scripts on objects with the same network identity, but it can only be set by the server.

    public Color c;//color to change to if we are controlling this one
   
    void Update()
    {
        if (GetComponent<NetworkIdentity>().hasAuthority)//make sure this is an object that we ae controlling
        {
            GetComponent<Renderer>().material.color = c;//change color
            Control = new Vector3(Input.GetAxis("Horizontal")*.2f, 0, Input.GetAxis("Vertical")*.2f);//update our controll varible
            GetComponent<PhysicsLink>().ApplyForce(Control,ForceMode.VelocityChange);//Use our custom force function
            if (Input.GetAxis("Cancel")==1)//if we press the esc button
            {
                GetComponent<PhysicsLink>().CmdResetPose();//reset our position
            }
        }
    }
}

Next, we have our Physics handler script:

using UnityEngine;
using Mirror;

public class PhysicsLink : NetworkBehaviour
{
    public Rigidbody rb;

    [SyncVar]//all the essental varibles of a rigidbody
    public Vector3 Velocity;
    [SyncVar]
    public Quaternion Rotation;
    [SyncVar]
    public Vector3 Position;
    [SyncVar]
    public Vector3 AngularVelocity;

    void Update()
    {
        if (GetComponent<NetworkIdentity>().isServer)//if we are the server update the varibles with our cubes rigidbody info
        {
            Position = rb.position;
            Rotation = rb.rotation;
            Velocity = rb.velocity;
            AngularVelocity = rb.angularVelocity;
            rb.position = Position;
            rb.rotation = Rotation;
            rb.velocity = Velocity;
            rb.angularVelocity = AngularVelocity;
        }
        if (GetComponent<NetworkIdentity>().isClient)//if we are a client update our rigidbody with the servers rigidbody info
        {
            rb.position = Position+Velocity*(float)NetworkTime.rtt;//account for the lag and update our varibles
            rb.rotation = Rotation*Quaternion.Euler(AngularVelocity * (float)NetworkTime.rtt);
            rb.velocity = Velocity;
            rb.angularVelocity = AngularVelocity;
        }
    }
    [Command]//function that runs on server when called by a client
    public void CmdResetPose()
    {
        rb.position = new Vector3(0,1,0);
        rb.velocity = new Vector3();
    }
    public void ApplyForce(Vector3 force, ForceMode FMode)//apply force on the client-side to reduce the appearance of lag and then apply it on the server-side
    {
        rb.AddForce(force, FMode);
        CmdApplyForce(force, FMode);

    }
    [Command]//function that runs on server when called by a client
    public void CmdApplyForce(Vector3 force,ForceMode FMode)
    {
        rb.AddForce(force, FMode);//apply the force on the server side
    }
    
}

Add these two scripts to the cube, set the rb variable to the Rigidbody and it should be ready to go. All that’s left is to make the cube a prefab and set it as the Player Prefab in the Network Manager script:

Delete the original instance of the cube prefab so we have a blank slate. And our project is now ready!

To test it you need to build your project so that you can have multiple instances running, once you have it built, open one of your instance running as the server and one running as a client, you could run as host but this makes it harder to debug issues as it runs both server and client code.

So now you should have a working multiplayer game but how do you get your friends to test it? First, you need to send them the build folder of your project, (I used Google Drive. ) And then you need a way for them to connect their computer to yours. If you are both on the same LAN network you won’t have a problem, but if you don’t live in the same house as your friends you will need to do some backflips.

The first option that comes to mind is port forwarding, but as everyone also knows, that’s a terrible idea for security reasons. What I would suggest would be to use Hamachi. Hamachi basically simulates a LAN network between all the connected computers without any security risks. When using it, you will need to use the IP address provided by Hamachi instead of your own.

That should be everything for this one, I hope you can get it working and I wish you the best for your multiplayer games!

Liked it? Take a second to support WireWhiz on Patreon!
Become a patron at Patreon!