Click to move tutorial issues

Options

I was working through the click to move tutorial and trying to figure out why things weren't working correctly. I found a few problems, but the biggest one is that I think the tutorial's implementation is just wrong... Sorry for the length of the post, but this doesn't seem to be a simple fix set of problems...

Problem 1: although I set up the Mecanim animator controller I'm using in my state, it isn't getting replicated to clients; with the setup from the tutorial, the host and controlling client both animate, but other clients don't. The animator properties are set to replicate to "Everyone Except Controller" and I've tried using both bolt properties and animator methods with the same results.

Here's how I have the animator properties I need set up:

What am I doing wrong? And should the animator properties propagate from the controller or the server? (Ideally I'd prefer each client to handle animation locally as a server only (non-host) instance doesn't need to animate anything.

Problem 2: the way the tutorial is written, once a click occurs it gets captured, then sent as a command on every subsequent frame. This is clearly wrong; one click should send one command. It's easily fixed by clearing destination after sending the command in SimulateController(), but that reveals another problem:

Problem 3: when ExecuteCommand() runs on the server, the returned result is the current position of the player on the server. ExecuteCommand() also runs once on the controlling client with the original input and resetState=false (which I believe is intended, so the controller can begin prediction?). The problem is, it's then called repeatedly with resetState=true and the result from the server. This constantly mis-corrects the player position back to its starting location.

Problem 3 is masked by problem 2, as each time the command gets resent the server responds with an up-to-date position, but once problem 2 is fixed the client just keeps getting the same input to ExecuteCommand(). Which I think is another bug:

Problem 4: ExecuteCommand() gets called once on the server (correct) but on the controlling client it gets called once with the original input (intended?) and then on every subsequent frame forever with the result calculated by the server. This is, again, clearly wrong; there should be either only one call, with the authoritative response from the server, or two at most if the call with the original input is intentional.

It seems the tutorial is adapted from a keyboard based movement scenario, where it would be correct to send input commands for every frame an input key was being pressed, and the server calculated response to that command would correctly be the server calculated position that results. But for click to move, a single input causes movement over time. The result of that input isn't position change directly; position change over time is a result of giving that input to the nav mesh agent.

I think the correct way to do click to move (ignoring client prediction and correction/interpolation) would be to only perform navigation on the server and sync the player transform to the controller as well as all other clients. And if I put `if (!BoltNetwork.IsServer()) return;` at the start of ExecuteCommand(), things indeed work with the exception of animation (see Problem 1).

Am I completely misunderstanding how this is intended to work? Is the way ExecuteCommand() is being called on the client correct? (And if so, what's the reason for it being called every frame even if no new commands are being sent?) And why aren't my animations syncing?

Comments

  • laurie
    Options

    Update: (don't seem to be able to edit the original post)

    I should mention that my setup isn't exactly the same as the tutorial; I'm using my own character controller that's a much simplified version of the Standard Assets one used in the tutorial. Hence only the two animator properties being replicated.

    I've figured out that the animation should be replicating from the controller, therefore the controller client does need to drive the nav agent in order to drive the animation, even though it's transform is being controlled by replication from the server.

    The server (host) probably therefore shouldn't be animating anything but it's own controlled player. It may be that the animator getting driven on the host for other players is what's causing animator replication to fail?

  • laurie
    Options

    After reading through the documentation on commands, I now get how it's intended to work and why ExecuteCommand() is being called every frame on the client. The problem is the tutorial's use of `cmd.Result.position = transform.position;`, as that's not the state that's being effected (directly) by the input.

    I now have `cmd.Result.position = cmd.Input.click;`, which is somewhat more correct but not perfect. NavMeshAgent can update its target destination as a result of path finding (e.g. because the location passed in was off the nav mesh / within an obstacle / etc). Getting the destination from the agent would only partially fix this, as pathing may take more than one frame, so the destination could change over time.

    Ultimately, the player's position is driven by transform replication from the server, though (confirmed by skipping the movement/animation update code if not the server). So, I think this solution is 'good enough' if I can solve the animation replication problem.

    Given the length of the initial post, I'll start a separate thread to address just that problem.

  • stanchion
    Options

    The Click to move tutorial uses Navmesh which isn't compatible with client side prediction as far as I know, so it is a bit different to the other examples. This is a full server auth sample, there are different ways to implement this.

  • ramonmelo
    Options

    Hello @laurie ,


    Our samples are totally open source, and you can find them here: https://github.com/BoltEngine/Bolt-Sample

    If you are interested in improving them we are glad to receive your pull-request :)

    We made them available this way in order to get this kind of feedback from our community. Thanks for the detailed post.