Search Unity

Calling CharacterController.Move() more than one time per frame.

Discussion in 'Multiplayer' started by TinhNong, Aug 26, 2020.

  1. TinhNong

    TinhNong

    Joined:
    Apr 21, 2020
    Posts:
    2
    Hi folks!
    I'm currently working on multiplayer game and do some client-side prediction. The problem is when client receive acknowledge state from server its rewind to that state and play back. But CharacterController.Move() move faster than it should be, The docs says "It is recommended that you make only one call to Move or SimpleMove per frame.". But I want to call it multiple time!!. What should I do?
     
    Suikale likes this.
  2. KyleOlsen

    KyleOlsen

    Joined:
    Apr 3, 2012
    Posts:
    237
    We had much more success writing a custom character controller built around Raycast, Spherecast and Physics.ComputePenetration
     
    gamecreatorc1 and ch715t0 like this.
  3. ep1s0de

    ep1s0de

    Joined:
    Dec 24, 2015
    Posts:
    168
    Can you share the code? :)
    I have the same problem with CharacterController and GC
     
  4. TinhNong

    TinhNong

    Joined:
    Apr 21, 2020
    Posts:
    2
    Agreed!
     
  5. Strieglitz

    Strieglitz

    Joined:
    May 11, 2017
    Posts:
    13
    Instead of calling the CharacterControllers Move() method directly, you can write your own MoveCharacter() method, where you can collect all the movements you want to do in this update-frame and add the movement combined. Everytime you want to Call the CharacterControllers Move() you instead call the MoveCharacter() method.

    Code (CSharp):
    1.  
    2.     Vector3 _cumulatedMovement;
    3.  
    4.     public void MoveCharacter(Vector3 movement) {
    5.         _cumulatedMovement += movement;
    6.     }
    7.  
    8.     private void Update() {
    9.         _characterController.Move(_cumulatedMovement);
    10.         _cumulatedMovement = Vector3.zero;
    11.     }
     
    smartplay and trombonaut like this.
  6. gamecreatorc1

    gamecreatorc1

    Joined:
    Dec 12, 2019
    Posts:
    64
    Thanks @Strieglitz but unfortunately that doesn't take changes in direction into account (to walk around an obstacle, for example).
     
  7. Punfish

    Punfish

    Joined:
    Dec 7, 2014
    Posts:
    401
    If you call Physics.SyncTransforms() between moves it will move properly.
     
  8. Sycab

    Sycab

    Joined:
    Jul 11, 2020
    Posts:
    10
    Did you ever find an answer to this, or is the only solution to create a custom kinematic character controller?

    This does not seem to work unfortunately.
     
  9. gamecreatorc1

    gamecreatorc1

    Joined:
    Dec 12, 2019
    Posts:
    64
    I never got it to work and will likely do my own. It may be worth looking into the source code for Unity's Netcode for GameObjects to see how they handle it (if they do).
     
  10. Sycab

    Sycab

    Joined:
    Jul 11, 2020
    Posts:
    10
    Hey, so I actually got it working now!

    Instead of Physics.SyncTransforms() like @Punfish suggested I step the physics scene using
    physiscScene.Simulate(deltaTime)
    , but I think syncing the transforms would also work. My problem was I forgot to call sync/simulate after resetting to the state of the message from the server. So the client started re-simulating from his current position and not from the position of the state Message.
    Hope this helps. :)

    My rewinding code looks something like this (simplified):
    Code (CSharp):
    1.  
    2. // Get buffer index for comparison with state buffer
    3. var rewind_tick_number = stateMsg.tick_number;
    4. var bufferIndex = rewind_tick_number % CLIENT_BUFFER_SIZE;
    5.  
    6. // Compare and decide if we need to reconcile
    7. // {...}
    8.  
    9. // Reset to state from state msg
    10. transform.position = stateMsg.position;
    11. transform.rotation = stateMsg.rotation;
    12.  
    13. // This is the line I forgot and which caused my issues
    14. _physicsScene.Simulate(dt);
    15.  
    16. // Re-simulate player from necessary tick to current client tick
    17. while (rewind_tick_number < c_tick_number)
    18. {
    19.                 // Step player with inputs from buffer
    20.                 bufferIndex = rewind_tick_number % CLIENT_BUFFER_SIZE;
    21.                 var rewindInput = c_input_buffer[bufferIndex];
    22.  
    23.                 // Movement is handled in different class, but you would call CharacterController.Move() here;
    24.                  _playerMovement.EarlyProcessAbility(rewindInput);
    25.                  _playerMovement.ProcessAbility();
    26.                  
    27.                  // Step physics scene
    28.                   _physicsScene.Simulate(dt);
    29.              
    30.                 // Save resulting state
    31.                 c_state_buffer[bufferIndex] = new ClientState {
    32.                     position = transform.position,
    33.                     rotation = transform.rotation,
    34.                 };
    35.  
    36.                 ++rewind_tick_number;
    37. }
    38.  
    39.  
     
    Last edited: Nov 4, 2022
    gamecreatorc1 likes this.