Using Unity ThirdPersonController with Coherence simulator as 'Server Side with Client Input'

I’ve configured the unity ThirdPersonController attached to a player gameobject, and made it into a prefab with Coherence. I could login to a RS and all was working. Movement was working, multiple clients connecting, everything was syncing.

Now I’m moving to using a simulator, and it’s almost all working except for a few things.

First issue:
client logging into simulator is not syncing a child objects rotation to the same value. The client is showing a slightly different value for the y rotation value than the simulator.

I have the Coherence sync object configured for the child object to sync rotation like so.

Second issue:
The second client logging into the game cannot move on their client, and no movement shows on the simulator. The first client, and any clients ( third, fourth, etc) logging in after the second are fine. Do I need to put a check in to make sure it’s ready before instantiation? I would expect the first one to not work if that was the case, but that’s not happening.

Blockquote SOLUTION! It appears that moving these lines from Awake() to Start() has solved the movement issue:

	public void Start()
			_cInput = GetComponent<CoherenceInput>();
			_sync = GetComponent<CoherenceSync>();


Third issue:
I don’t see any of the movement sync from other clients. They show movement on themselves & simulator.

Some data is syncing because you can see the rotation change, but no actual position.

Any thoughts or assistance would be greatly appreciate.

1 Like

Hey Martin, welcome!

Hmm, this is odd. I wonder why, and why would it would sync wrong on a Sim and not on other Clients. Actually, quick idea: would you be able to connect 2 clients to that Simulator? Just to see if the issue is the Simulator, or it’s the parameter/setup in itself.

Another thought: what do you have in the Optimisation panel? (it’s the other panel you can open from the CoherenceSync component. I’m asking because in that panel you’re able to configure parameters’ precision, and if you set it low or it’s somehow low by default, updates under that threshold won’t be sent. Could it be? :thinking:

Wait, how did that solve it? :smiley:
In general, just be aware of the lifetime of network entities. I would suggest to read this paragraph and this one.

Aha, a classic! :slight_smile: Could it be that the remote instance of a player (Vitt in your video) is still receiving the input that you intend only the local player (Izzy) to receive? It is very common mistake that a prefab set to be single player, when instantiated over the network, still hooks into input callbacks and tries to move… even if it shouldn’t!

Sometimes fixing this issue fixes all the others, because things start to work the way the are intended to be® :laughing:

Thanks for giving names to the players, it makes so easy to refer to what’s happening! :+1:


I edited my original post that I had moved some of the code in the PlayerHandler script ( responsible for spawning player… like in the example project ). To document the fix here as well.

Fixed this as well by syncing more data. I’m not totally sure if this is the intended way for this to be used for simulator clients. Is the idea that the data being synced is from the perspective of the simulator?
Screenshot 2023-07-17 121931

The players have input authority, and if I understand from the documentation ( and if I’m using Coherence Input correctly ) only the client that has authority should be able to send input. The simulator for all the clients connecting has state authority.

To make a prefab not be ‘single player’ it should only need Coherence Sync attached correct? And then be configured properly.

This issue seems to be tied directly to the Camera rotation code:

 if (_input.look.sqrMagnitude >= _threshold && !LockCameraPosition)
                //Don't multiply mouse input by Time.deltaTime;
                float deltaTimeMultiplier = IsCurrentDeviceMouse ? 1.0f : Time.deltaTime;

                if (!SimulatorUtility.IsSimulator)
                    _cinemachineTargetYaw += _input.look.x * cameraRotationSensitivity * deltaTimeMultiplier;
                    _cinemachineTargetPitch += _input.look.y  * cameraRotationSensitivity * deltaTimeMultiplier;
                if (SimulatorUtility.IsSimulator)
                    _cinemachineTargetYaw += _input.look.x * cameraRotationSensitivity * deltaTimeMultiplier * 1.59765f;
                    _cinemachineTargetPitch += _input.look.y  * cameraRotationSensitivity * deltaTimeMultiplier * 1.59765f;                   




Perhaps it needs to be refactored to account for it being run through the simulator? I introduced the adjustment value ‘1.59765f’ to try and “fix” the issue, and it’s close. I’m curious if anyone else has run into something like this?

Oh yes, what I meant is that it’s quite peculiar that moving calls to GetComponent from Awake to Start would fix movement. If those calls were failing, you would have gotten quite a few errors it in the console.
But it doesn’t matter :slight_smile:

What I meant by ‘a prefab that was meant to be single player’ is that the character controller offered by Unity doesn’t necessarily contemplate multiplayer, it was thought to be used just in a single-player scenario. To make it work in multiplayer, you might have to change the code so that other instances of that prefab (the remote players) don’t take over the input.

Yes, this is correct.

Hmm, no that shouldn’t be the case. I wouldn’t apply an arbitrary adjustment like that to the Simulator.

But one question to ask is: you have a minimum check in there: sqrMagnitude >= _treshold. Maybe you need to remove it? It could be that if the player sends an input that is less than that, then the simulator just ignores it. The Simulator should not ignore it.

But honestly, for something like the camera… why are you even transferring it to the simulator? :slight_smile: The camera is something local to the player, no need to have a server tell Clients where their camera should be. So I presume you can just leave that code in the authoritative player, and disable it for the remote ones and for the Simulator.

Let me know if it helps!

Thank you very much for all the additional information. First I must apologize, as I was implementing it improperly. I was using the input for client movement & for simulator movement at the same time inadvertently. This was causing the simulator and client to not be synced properly.

Yes 100% I had to change the whole input code to implement CoherenceInput. I have done that.

I was making a problem for myself, and then trying to solve it. This was not necessary because Coherence already has it built in.

I would like to tune the movement to be more responsive now that it’s working, and implement more features from the documentation:

and this:

I was trying to implement my own version of client-side prediction & reconciliation by doing this. I’ll see if the build-in will work for me or not.

private void Awake()
    var positionBinding = GetComponent<CoherenceSync>().Bindings.FirstOrDefault(c => c.Name == "position");
    positionBinding.OnNetworkSampleReceived += DetectMisprediction;

private void DetectMisprediction(object sampleData, long simulationFrame)
    const float MispredictionThreshold = 3;

    var networkPosition = (Vector3)sampleData;
    var distance = (networkPosition - transform.position).magnitude;

    if (distance > MispredictionThreshold)
        transform.position = networkPosition;

In the example for misprediction VS Code gives an error for ‘FirstOrDefault’. Do I need to do something to make this available?

Very happy to hear it’s working fine now!

You just need to include C#'s Linq libraries in your script. Like this:

using System.Linq;

at the top of the file.

Btw I have marked my reply as the solution for this thread, so it can be more easily located by others in the future. Feel free to open a new one if you stumble into more roadblocks, but you feel they are unrelated to the topic discussed here.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.