Authoritative Server: Click to Move Using Navmesh Agent

relicright
edited June 2017 in Photon Bolt
Has anyone tried to do a click-to-move authoritative server using a Navmesh agent? I've used the script below to control the playerMotor, but it only works on the client:

 public override void SimulateController () {

        if (playerCam == null && entity.hasControl)
        {
            GameObject playCam = (GameObject)Instantiate(Resources.Load("PlayerCamera3D"));
            playCam.GetComponent<PlayerCameraMove3D>().target = transform;

            playerCam = playCam;
        }

        if (_motor == null)
        {
            if (BoltNetwork.isServer)
            {
                PlayerObject pObject = PlayerObjectRegistry.GetClientPlayer(entity.controller);
                _motor = pObject.character.GetComponent<PlayerMotor>();

                state.TrySetDynamic("pMotor", _motor);
            } else if (BoltNetwork.isClient)
            {
                PlayerObject pObject = PlayerObjectRegistry.GetClientPlayer(entity.controller);
                 _motor = pObject.character.GetComponent<PlayerMotor>();
            }<del class="Delete"></del>                                             
        }        

        IClickToMoveCommandInput input = ClickToMoveCommand.Create();

        if (Input.GetMouseButton(1) && !isClickWaiting)
        {           
            isClickWaiting = true;
            Vector3 mousePosFar = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.farClipPlane);
            Vector3 mousePosNear = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane);

            Vector3 mousePosF = Camera.main.ScreenToWorldPoint(mousePosFar);
            Vector3 mousePosN = Camera.main.ScreenToWorldPoint(mousePosNear);
            Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(mousePosN, mousePosF - mousePosN, out hit))
            {
                if (!hit.collider.isTrigger)
                {
                    testLabel = "Clicked by client";
                    Debug.Log("hit a collider");
                    target = hit.point;
                }
            }

            StartCoroutine(mouseClickTimer());    
        }

        input.click = target;
        entity.QueueInput(input);
    }

Comments

  • Missing your execute command ?

    The problem with Navmesh is you need to "fast forward" the simulation to catch up to the server. I haven't looked into whether that's possible with Navmesh.

    Details on commands here https://docs.google.com/document/d/1yjPE69MdT3BhY-HjARFdqkIpqXujORLsKiX6B8GWoLI/edit#heading=h.cf8bgjjxjtu5
  • Sorry stanchion, I was planning to post the execute command as a comment to this post so I didn't bog everyone down combining them into a single post, but the original post had to be approved first. I can get the client and the server following the navmesh path, but the controller begins stuttering noticeably, which is normal since i don't know the speed algorithm of a navmesh to simulate the movement.

    What I'm trying now is having a cube with no mesh render act as the navmesh agent. Then, making the ship follow that using MoveTowards. I'd love to know if there may be a better way.

    Here is the execute script:
    public override void ExecuteCommand(Command c, bool resetState)
    {
    ClickToMoveCommand cmd = (ClickToMoveCommand)c;

    agent.SetDestination(cmd.Input.click);

    if (BoltNetwork.isClient)
    Debug.Log("is client");

    if (resetState)
    {
    _motor.SetState(cmd.Result.position, cmd.Result.velocity);
    }
    else
    {
    PlayerMotor.State motorState = _motor.Move(transform.position, transform.rotation, true);

    cmd.Result.position = motorState.position;
    cmd.Result.velocity = motorState.velocity;
    }
    }
  • Example with no client side prediction, if you are playing on the client with latency there will be input lag.
    https://hastebin.com/meqesisomi.cpp
  • relicright
    edited June 2017
    Thanks for the help. I modified the script you attached and now I'm getting a pretty smooth transition on the client. I also used the Temporal Anti Aliasing tutorial to help with some of the jitter. I've attached the code in the event that another use may be interested in achieving the same effect.

    https://hastebin.com/nadameloya.cs
  • I've figured out how to completely smooth the movement while allowing the server to correct position in resetState. You have to change the agentState Transform to replicate to 'Everyone Except Controller'. This fixed all of the jitter. Below is the refined script.

    https://hastebin.com/uqohoyirok.cpp
  • Hey Relicright,

    First thank you for the script :)
    Second, when using your previous script, I can't get the client and server to move at the same speed.
    And the client doesn't end up at the same position (stop when the server stops).

    I might not be important for your game, but do you have the same problem?

    Using the agent in both client and server doesn't create the issue obviously.
  • That's because his script does not reset state position if the distance between resetstate position and current position is 1 or less. If you read the doc that goes into detail on commands you'll see that movement should be on server and client, and identical, for client side prediction. However since you are not "fast forwarding" the agent each time each time you setdestination and instead are just consecutively calling setdestination (all but the last call will do nothing) the client side prediction will not work. For Rigidbodies for example, there is now manual stepping in the new Unity beta which lets you "fast forward" the rigidbody, so that even though you are just applying forces consecutively, it will fast forward to catch up with the server properly. https://www.youtube.com/watch?v=ChfllThl8eQ
  • So,

    If I understand correctly, Either I use the agent, but skip the client side prediction (like in your script), or I don't use the agent at all (and the navmesh probably) and move the character with the motor script and character controller you have in example?

    Stanchion, you might think I don't read the docs or don't try :) but I do. I'm not that into network yet like you guys :(
  • Yes you're correct. You can test with simulated latency in Bolt setting to see if its playable on client with latency and no prediction. Also if Unity navmesh does not have "fast forward simulation" I'm sure other ones like A* or writing your own can do that.
  • laurel
    laurel
    edited June 2017
    I found this:
    That might help, but at least I can focus my energy on something that is feasible.
    Thanks.

    Here is the link :
    A Multiplayer RPG Mouse Controller w/ Bolt
    https://www.youtube.com/watch?v=VTA804OQyXo&amp;list=PLKVb1Q1qkNtHc2QIvVCR31ZKpqXzSby9g
  • The reason why i needed the navmeshagent compared to the bolt 'Click-to-Move' tutorial was, was because of the lack of path finding included in the example. When you click to move with that method you will go until you run into a wall. I'm seeing that A* may be a good way to go about path finding, but i don't have any experience with it , which was why i chose navmesh. It's true there is no 'true' client prediction in my code, but the state is reset when the objects are out of sync. With my testing I find little discrepancy between the clients/server yet. I'll keep you updated on anything else i find.