Search Unity

2D Player Movement (Multiplayer, Netcode for Gameobjects)

Discussion in 'Netcode for GameObjects' started by OneUnity3D, Feb 8, 2022.

  1. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Hey Unity-Members,

    it seems like a stupid questions and maybe there is a resolve for this, but I don't get it...
    I want my 2D Player Prefab to move when i press W,A,S or D, but for multiplayer and I'm not able to get it...

    For now I got my example code from the unity docs:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Netcode;
    5.  
    6. public class PlayerMovement : NetworkBehaviour
    7.     {
    8.         public NetworkVariable<Vector3> Position = new NetworkVariable<Vector3>();
    9.  
    10.         public override void OnNetworkSpawn()
    11.         {
    12.             if (IsOwner)
    13.             {
    14.                 Move();
    15.             }
    16.         }
    17.  
    18.         public void Move()
    19.         {
    20.             if (NetworkManager.Singleton.IsServer)
    21.             {
    22.                 var randomPosition = GetRandomPositionOnPlane();
    23.                 transform.position = randomPosition;
    24.                 Position.Value = randomPosition;
    25.             }
    26.             else
    27.             {
    28.                 SubmitPositionRequestServerRpc();
    29.             }
    30.         }
    31.  
    32.         [ServerRpc]
    33.         void SubmitPositionRequestServerRpc(ServerRpcParams rpcParams = default)
    34.         {
    35.             Position.Value = GetRandomPositionOnPlane();
    36.         }
    37.  
    38.         static Vector3 GetRandomPositionOnPlane()
    39.         {
    40.             return new Vector3(Random.Range(-3f, 3f), 1f, Random.Range(-3f, 3f));
    41.         }
    42.  
    43.         void Update()
    44.         {
    45.             transform.position = Position.Value;
    46.         }
    47.     }
    And it works, but I don't want a button and a random position, I want the player to move via keyboard inputs.

    Can anyone help me?
     
  2. MiTschMR

    MiTschMR

    Joined:
    Aug 28, 2018
    Posts:
    489
    There is a ClientNetworkTransform example package that you can use from the package manager's Netcode package. Simply attach the component to the player prefab and it should work.
     
  3. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Ok thank you but I already have a NetworkTransform attached to my Player and it doesn't matter which of these I use, I don't know which code I have to write and where? Usually I write something like this:

    Code (CSharp):
    1. public Rigidbody rb;
    2.  
    3. public float SpeedX = 0.1f;
    4. public float SpeedY = 0.5f;
    5.  
    6. rb.AddForce(new Vector2(SpeedX, SpeedY));
    But for multiplayer I don't know what to do... And the tutorials on youtube or anywhere else are not explaining my issue...
     
  4. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Implementing controls is exactly the same on singleplayer as on multiplayer. All you have to do is to make sure that only the player object that is owned by the player is checking for input. For example, if you have something like this in singleplayer:
    Code (CSharp):
    1. void Update() {
    2.     HandleInput(); //checks for WASD etc and moves player accordingly
    3. }
    in multiplayer becomes:
    Code (CSharp):
    1. void Update() {
    2.     if (IsOwner) { //alternatively: IsLocalPlayer
    3.         HandleInput(); //checks for WASD etc and moves player accordingly
    4.     }
    5. }
     
  5. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Ok thank you, that is good to know, but my Player doesn't move at all... Sorry for being annyoing but here is my code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Netcode;
    5.  
    6. public class PlayerMovement : NetworkBehaviour
    7.     {
    8.  
    9.         public Rigidbody2D rb2D;
    10.         public float speed = 5f;
    11.  
    12.         public NetworkVariable<Vector3> Position = new NetworkVariable<Vector3>();
    13.  
    14.         public override void OnNetworkSpawn()
    15.         {
    16.             if (IsOwner)
    17.             {
    18.                 Move();
    19.             }
    20.         }
    21.  
    22.         public void Move()
    23.         {
    24.             /*if (NetworkManager.Singleton.IsServer)
    25.             {
    26.                 var randomPosition = GetRandomPositionOnPlane();
    27.                 transform.position = randomPosition;
    28.                 Position.Value = randomPosition;
    29.             }
    30.             else
    31.             {
    32.                 SubmitPositionRequestServerRpc();
    33.             }*/
    34.         }
    35.  
    36.         [ServerRpc]
    37.         void SubmitPositionRequestServerRpc(ServerRpcParams rpcParams = default)
    38.         {
    39.             Position.Value = GetRandomPositionOnPlane();
    40.         }
    41.  
    42.         static Vector3 GetRandomPositionOnPlane()
    43.         {
    44.             return new Vector3(Random.Range(-3f, 3f), 1f, Random.Range(-3f, 3f));
    45.         }
    46.  
    47.         void Update()
    48.         {
    49.             transform.position = Position.Value;
    50.         }
    51.  
    52.         void FixedUpdate()
    53.         {
    54.             if(IsOwner)
    55.             {
    56.                 float x = Input.GetAxis("Horizontal");
    57.                 float y = Input.GetAxis("Vertical");
    58.                 rb2D.velocity = new Vector2(x * speed, y * speed).normalized * Time.deltaTime;
    59.             }
    60.         }
    61.     }
    What's wrong?
     
  6. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    There are three things. First, you should always check for input in Update, not FixedUpdate, even if you only apply the result of that input in FixedUpdate. Second, you are setting the rigidbody velocity on the client, but you have a NetworkTransform on the player which means that the client is not allowed to change the transform, whether through rigidbody or not. Third, and I suspect this is most relevant here: your Update function is resetting the transform position every frame.
     
    OneUnity3D likes this.
  7. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Thank you it worked! But two more questions:
    Now my Player is really slowly moving and has a kind of "delay" when i press a keyboard button, is it because of the NetworkBehaviour?

    And the more important thing is, now when I start the Host and the Client, the player can't see the other player moving...
     
  8. MiTschMR

    MiTschMR

    Joined:
    Aug 28, 2018
    Posts:
    489
    This is a different component, not a NetworkTransform. See below:
    upload_2022-2-8_14-43-4.png
    upload_2022-2-8_14-43-47.png
     
    OneUnity3D likes this.
  9. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    That depends on how you fixed it. From the sound of it, you removed the NetworkTransform I'm guessing? If you want the client to have authority over setting position/rotation but the result to be synced to the rest, your player objects should have ClientNetworkTransforms instead. If you instead use NetworkTransforms and have the client send its position/rotation/velocity changes to the server via Rpc to be applied, you will see a delay when moving your own player.

    I would also recommend you read up on setting player movement in singleplayer, there are some important subtleties related to using Update/FixedUpdate/Time.deltaTime/Time.fixedDeltaTime you should be aware of, as well as the difference between setting rigidbody velocity directly versus using rigidbody.AddForce with ForceMode.VelocityChange.
     
    OneUnity3D likes this.
  10. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Ok now my players are synced but the delay is still going: I read alot about Update/FixedUpdate and Time.deltatime, when I use Time.deltatime my player doesn't move at all or very very slow.

    My code is:
    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using Unity.Netcode;
    6.  
    7. public class PlayerMovement : NetworkBehaviour
    8.     {
    9.         public Rigidbody2D rb2D;
    10.         public float speed = 5f;
    11.  
    12.         void Update()
    13.         {
    14.             if(IsOwner)
    15.             {
    16.                 Move();
    17.             }
    18.         }
    19.  
    20.         public void Move()
    21.         {
    22.                 float x = Input.GetAxis("Horizontal");
    23.                 float y = Input.GetAxis("Vertical");
    24.                 rb2D.velocity = new Vector2(x * speed, y * speed).normalized;
    25.         }
    26.     }
    27.  
    And I attched the Client Network Transform to my player prefab, now my players see each others movement, but as I said very slow and with a delay...
     
  11. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    I now notice that you also normalize the velocity vector, so speed does nothing. I'm guessing you want to instead do:
    Code (CSharp):
    1. rb2D.velocity = new Vector2(x, y).normalized * speed;
    The delay for non-owners is normal. It will be a lot smaller if you disable interpolation on the (Client)NetworkTransform but then it will be more choppy.
     
  12. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Wow thanks, now it works perfectly. Thank you all for your help!
     
  13. ksc_3899

    ksc_3899

    Joined:
    Jan 1, 2019
    Posts:
    30
    I attached the ClientNetworkTransform from the Boss Game sample project (it is removed from Package Manager). But the Transform is not being updated.

    Help me, please.
    Code (CSharp):
    1. using Unity.Netcode.Components;
    2. using Unity.Netcode;
    3. using UnityEngine;
    4.  
    5. namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
    6. {
    7.     // TODO inherit from `NetworkBehaviour` instead of `NetworkTransform` to cut direct relationship between two
    8.     // TODO change to owner netvar instead of RPC based
    9.     /// <summary>
    10.     /// Used for syncing a transform with client side changes. This includes host. Pure server as owner isn't supported by this. Please use NetworkTransform
    11.     /// for transforms that'll always be owned by the server.
    12.     /// </summary>
    13.     [DisallowMultipleComponent]
    14.     public class ClientNetworkTransform : NetworkTransform
    15.     {
    16.         /// <summary>
    17.         /// Used to determine who can write to this transform. Owner client only.
    18.         /// Changing this value alone will not allow you to create a NetworkTransform which can be written to by clients.
    19.         /// We're using RPCs to send updated values from client to server. Netcode doesn't support client side network variable writing.
    20.         /// This imposes state to the server. This is putting trust on your clients. Make sure no security-sensitive features use this transform.
    21.         /// </summary>
    22.         // This is public to make sure that users don't depend on this IsClient && IsOwner check in their code. If this logic changes in the future, we can make it invisible here
    23.  
    24.         public override void OnNetworkSpawn()
    25.         {
    26.             base.OnNetworkSpawn();
    27.             CanCommitToTransform = IsOwner;
    28.         }
    29.  
    30.         protected override void Update()
    31.         {
    32.             CanCommitToTransform = IsOwner;
    33.             base.Update();
    34.             if (NetworkManager.Singleton != null && (NetworkManager.Singleton.IsConnectedClient || NetworkManager.Singleton.IsListening))
    35.             {
    36.                 if (CanCommitToTransform)
    37.                 {
    38.                     TryCommitTransformToServer(transform, NetworkManager.LocalTime.Time);
    39.                 }
    40.             }
    41.         }
    42.  
    43.         protected override bool OnIsServerAuthoritative()
    44.         {
    45.             return false;
    46.         }
    47.     }
    48. }
     
  14. PedroPaulo95

    PedroPaulo95

    Joined:
    Apr 22, 2020
    Posts:
    6
    Basically, you solved the problem by adding a client-side authority transform, trusting the client's response.
    But if we don't want to use ClientNetworkTransform, and we want to move using server-side authority only, what would the code look like?

    I haven't figured out how to do this yet with Netcode for gameobjects.
     
  15. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    The client listens for input, then sends the input to the server via ServerRpc. The server then validates the input (e.g. if you mouse click every frame then you're probably cheating), applies the input to the client's player object (e.g. move it forward), then the result is replicated to the client and everyone else by the NetworkTransform or through ClientRpc.

    However, note that doing it this way will always incur a delay, which is why I prefer to trust the client and apply input directly.

    Another option would be to apply the movement on the client, and then have the server check it afterwards. The server doesn't have authority to change the ClientNetworkTransform used in that setup, but can always kick the player if it determines they're cheating.
     
    PedroPaulo95 likes this.
  16. PedroPaulo95

    PedroPaulo95

    Joined:
    Apr 22, 2020
    Posts:
    6
    @CosmoM

    I liked your way of thinking, sharing the processing with the client but validating if you're not cheating on the server side, so we share the workload and avoid latency issues.

    Could you give a practical example (code) in this scenario that the colleague posted in the question? I really liked your approach, but I would like to see a way to implement this.

    Thank you very much for the reply!
     
  17. ireakhavok

    ireakhavok

    Joined:
    Feb 9, 2023
    Posts:
    19
    In a case like this, would you simply just add all of the previous inputs together? I'm curious as to how often the update method would be called and how to compound them properly, as I am using physics in my code, and should be using the fixed update for that, but don't know how to accomplish that.