Search Unity

2D Multiplayer Animation Problem

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

  1. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Hey friendly Unity-Community,

    I have problems with my Player animations, hope you can help me, I don't know what's wrong here in my code but my Player moves just straight down, it doesn't matter which key i press. I've read my code about 50 times and now I'll hope you can help me:
    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 enum PlayerState
    9.         {
    10.             Idle,
    11.             Walk
    12.         }
    13.  
    14.  
    15.         //public Rigidbody2D rb2D;
    16.         public CharacterController characterController;
    17.         public Animator myAnim;
    18.         private float speed = 5f;
    19.        
    20.         //client caches positions
    21.         private Vector3 change;
    22.  
    23.         //Network Variables
    24.         private NetworkVariable<Vector3> networkPositionDirection = new NetworkVariable<Vector3>();
    25.         private NetworkVariable<PlayerState> networkPlayerState = new NetworkVariable<PlayerState>();
    26.  
    27.         void Awake()
    28.         {
    29.             characterController = GetComponent<CharacterController>();
    30.             myAnim = GetComponent<Animator>();
    31.         }
    32.  
    33.         void Start()
    34.         {
    35.             //Set the Spawnpoint from the player (for example the city)
    36.         }
    37.  
    38.         void Update()
    39.         {
    40.             if(IsClient && IsOwner)
    41.             {
    42.                 ClientInput();
    43.             }
    44.  
    45.             ClientMove();
    46.             ClientVisuals();
    47.         }
    48.  
    49.         void ClientInput()
    50.         {
    51.             Vector3 direction = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"), 0);
    52.             Vector3 inputPosition = direction.normalized * speed * Time.fixedDeltaTime;
    53.  
    54.             if(change != inputPosition)
    55.             {
    56.                 change = inputPosition;
    57.                 UpdateClientPositionServerRpc(inputPosition);
    58.             }
    59.             /*change = Vector3.zero;
    60.             change.x = Input.GetAxisRaw("Horizontal");
    61.             change.y = Input.GetAxisRaw("Vertical");*/
    62.  
    63.             //Player state enum changes
    64.             if(direction != Vector3.zero)
    65.             {
    66.                 UpdatePlayerStateServerRpc(PlayerState.Walk);
    67.             } else
    68.             {
    69.                 UpdatePlayerStateServerRpc(PlayerState.Idle);
    70.             }
    71.         }
    72.  
    73.         void ClientMove()
    74.         {
    75.             if(networkPositionDirection.Value != Vector3.zero)
    76.             {
    77.                 characterController.SimpleMove(networkPositionDirection.Value);
    78.                 //rb2D.MovePosition(transform.position + change.normalized * speed * Time.fixedDeltaTime);
    79.                 //transform.position + change.normalized * speed * Time.fixedDeltaTime
    80.             }
    81.         }
    82.  
    83.         void ClientVisuals()
    84.         {
    85.             if(networkPlayerState.Value == PlayerState.Walk)
    86.             {
    87.                 myAnim.SetFloat("Walk", 1);
    88.                 /*myAnim.SetFloat("moveX", change.x);
    89.                 myAnim.SetFloat("moveY", change.y);
    90.                 myAnim.SetBool("moving", true);*/
    91.             } else
    92.             {
    93.                 myAnim.SetFloat("Walk", 0);
    94.                 //myAnim.SetBool("moving", false);
    95.             }
    96.         }
    97.  
    98.         /*void UpdateAnimationAndMove()
    99.         {
    100.             if(change != Vector3.zero)
    101.                 {
    102.                     Move();
    103.                     myAnim.SetFloat("moveX", change.x);
    104.                     myAnim.SetFloat("moveY", change.y);
    105.                     myAnim.SetBool("moving", true);
    106.                 } else
    107.                 {
    108.                     myAnim.SetBool("moving", false);
    109.                 }
    110.         }*/
    111.  
    112.         [ServerRpc]
    113.         public void UpdateClientPositionServerRpc(Vector3 newPositionDirection)
    114.         {
    115.             networkPositionDirection.Value = newPositionDirection;
    116.         }
    117.  
    118.         [ServerRpc]
    119.         public void UpdatePlayerStateServerRpc(PlayerState newState)
    120.         {
    121.             networkPlayerState.Value = newState;
    122.         }
    123.     }
    Thank you for every answer :)
     
    Quae likes this.
  2. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    Do you ever initialize the Vector3 "change" (presumably to Vector3.zero)? If not, that might explain your result. Secondly, note that SimpleMove ignores the y component, so you may have intended to set "direction" such that the vertical axis sets its z component, rather than y.
     
  3. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Ok thank you, I forgot to initialize the change Vector3, now i did it here:
    Code (CSharp):
    1. if(change != inputPosition)
    2.             {
    3.                 change = Vector3.zero;
    4.                 change = inputPosition;
    5.                 UpdateClientPositionServerRpc(inputPosition);
    6.             }
    But my Player doens't move left, right or up, just down when i press a key.

    What do you mean with: note that SimpleMove ignores the y component, so you may have intended to set "direction" such that the vertical axis sets its z component, rather than y.

    Greats,
    One
     
  4. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    No, you should initialize it *before* you compare to it, and only once. So just change
    private Vector3 change;
    to
    private Vector3 change=Vector3.zero;
    . If you don't, then
    change
    doesn't have any set value (which is not the same as it being zero), and comparisons to it can therefore yield unexpected results.

    What I mean with the SimpleMove comment is that
    characterController.SimpleMove(new Vector3(x,y,z))
    is functionally the same as
    characterController.SimpleMove(new Vector3(x,0,z))
    , and that you therefore probably meant to set
    Vector3 direction = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
    . See the documentation of SimpleMove.
     
    Last edited: Feb 10, 2022
  5. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Ah ok thank you, now I initialized the change Vector3:
    Code (CSharp):
    1. private Vector3 change = Vector3.zero;
    And I read the Docs about SimpleMove and Move, and I change 2 lines:
    Code (CSharp):
    1. Vector3 direction = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical") , 0);
    Because it's a 2D game, I changed it like this and I can now move left, right, up and down.
    And this from SimpleMove to Move:
    Code (CSharp):
    1. void ClientMove()
    2.         {
    3.             if(networkPositionDirection.Value != Vector3.zero)
    4.             {
    5.                 characterController.Move(networkPositionDirection.Value);
    6.             }
    7.         }
    But now my Animation just works for walking down and not for left, right and up^^

    We are getting close to the resolution but now there is another issue, you might help aswell?^^

    Greets,
    One
     
  6. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    In the code you posted you don't let the animator know which direction you're walking in. You seem to have parameters for that (moveX and moveY) but you commented them out.
     
  7. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Yeah... I forgot to uncomment these part... now I can walk, but when I start Host and Client, my host and my client player jumps back to my down_idle after I walked and if I walk on my host, my client sees just the idle sprite moving and no animation... weird stuff is happening...
     
  8. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    So... I cleaned up my code, change Vector3 to Vector2, because it's not necessary to have Vector3 in my code and I tried to change a few variables but nothing worked, 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.         public enum PlayerState
    9.         {
    10.             Idle,
    11.             Walk
    12.         }
    13.  
    14.  
    15.         //Controller and Declaration
    16.         public CharacterController characterController;
    17.         public Animator myAnim;
    18.        
    19.        
    20.         //client caches positions and variables
    21.         private Vector2 change = Vector2.zero;
    22.         private float speed = 5f;
    23.  
    24.         //Network Variables
    25.         private NetworkVariable<Vector2> networkPositionDirection = new NetworkVariable<Vector2>();
    26.         private NetworkVariable<PlayerState> networkPlayerState = new NetworkVariable<PlayerState>();
    27.  
    28.         void Awake()
    29.         {
    30.             characterController = GetComponent<CharacterController>();
    31.             myAnim = GetComponent<Animator>();
    32.         }
    33.  
    34.         void Start()
    35.         {
    36.             //Set the Spawnpoint from the player (for example the city)
    37.         }
    38.  
    39.         void Update()
    40.         {
    41.             if(IsClient && IsOwner)
    42.             {
    43.                 ClientInput();
    44.             }
    45.             ClientMove();
    46.             ClientVisuals();
    47.         }
    48.  
    49.         void ClientInput()
    50.         {
    51.            
    52.             Vector2 direction = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
    53.             Vector2 inputPosition = direction.normalized * speed * Time.deltaTime;
    54.  
    55.             if(change != inputPosition)
    56.             {
    57.                 change = inputPosition;
    58.                 UpdateClientPositionServerRpc(inputPosition);
    59.             }
    60.  
    61.             //Player state enum changes
    62.             if(direction != Vector2.zero)
    63.             {
    64.                 UpdatePlayerStateServerRpc(PlayerState.Walk);
    65.             } else
    66.             {
    67.                 UpdatePlayerStateServerRpc(PlayerState.Idle);
    68.             }
    69.         }
    70.  
    71.         void ClientMove()
    72.         {
    73.             if(networkPositionDirection.Value != Vector2.zero)
    74.             {
    75.                 characterController.Move(networkPositionDirection.Value);
    76.             }
    77.         }
    78.  
    79.         void ClientVisuals()
    80.         {
    81.             if(networkPlayerState.Value == PlayerState.Walk)
    82.             {
    83.                 myAnim.SetFloat("Walk", 1);
    84.                 myAnim.SetFloat("moveX", change.x);
    85.                 myAnim.SetFloat("moveY", change.y);
    86.                 myAnim.SetBool("moving", true);
    87.             } else
    88.             {
    89.                 myAnim.SetFloat("Walk", 0);
    90.                 myAnim.SetBool("moving", false);
    91.             }
    92.         }
    93.  
    94.         [ServerRpc]
    95.         public void UpdateClientPositionServerRpc(Vector2 newPositionDirection)
    96.         {
    97.             networkPositionDirection.Value = newPositionDirection;
    98.         }
    99.  
    100.         [ServerRpc]
    101.         public void UpdatePlayerStateServerRpc(PlayerState newState)
    102.         {
    103.             networkPlayerState.Value = newState;
    104.         }
    105.     }
    The problem is that my host works perfectly, but when I connect with the client the animation stays in idle_down sprite and my client is moving faster than my host (for no reason...).
    Do I have issues with my ServerRpc or something else?

    Greets,
    One
     
  9. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    As far as I'm aware Move does not take a Vector2, but since it works for your host that's apparently not a problem.

    When you say the animation does not update and the movement is faster, are you talking about that of the client player object as seen from the client, that of the client player object as seen from the host, or that of the host player object as seen from the client? And are you using a NetworkAnimator component?

    About the speed of the movement, note that Time.deltaTime is not fixed, so you might be sending a change in speed every frame. Additionally, the server will likely have different frame timings than the client, and so the effective speed will be different. What will likely solve your speed inconsistency issue and ensure that the serverRpc is not called every frame is to either (a) move the * Time.deltaTime multiplication out of inputPosition and do it in Move(...) (in ClientMove) instead; or (b) read input in Update but only calculate and send inputPosition in FixedUpdate, using time.fixedDeltaTime instead.
     
    OneUnity3D likes this.
  10. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    I changed the Vector2 to Vector3 to prevent future issues.

    I changed the Time.deltaTime from inputPosition to the Move function and now the player moves at the same speed on the host and the client, which is perfect! THANK YOU!

    To answer your question:
    When I move the host with my keyboard on the host window everything works perfectly, the movement, the animation, everything is ok.
    When I move the host and I look at my client window the client sees the host moving, but the animation stays in idle_down.
    When I move the client and I look at my host window I see the client moving, but also with the idle_down animation.
    When I move the client and look at my client window, the movement and animation works, but after pressing a keyboard key he jumps back to the idle_down animation.

    I hope it's understandable what I'm trying to say^^

    Thank you for your help, we are getting close to the solution! :D

    here is my Inspector from the player prefab:


     
  11. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    I think the problem is that you have a NetworkAnimator, but you're setting the animation parameters for all clients (in ClientVisuals). Either remove the NetworkAnimator component or make sure that ClientVisuals only executes if IsServer.
     
  12. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    hmm.. I tried both: first I changed:
    Code (CSharp):
    1. if(IsServer)
    2.             {
    3.                
    4.                 ClientVisuals();
    5.             }
    and it's the same issue and then I deleted the NetworkAnimator, but then there is no animation at all.

    After I changed the isServer line, my client sees the animation from my host perfectly when I press the buttons "slowly" and not pressed fast in a row. but when I press my arrow keys fast, the player animation stays in the last sprite where the player was.
     
  13. CosmoM

    CosmoM

    Joined:
    Oct 31, 2015
    Posts:
    204
    To be clear: you have to either keep the NetworkAnimator and let only the server set the animation parameters or delete the NetworkAnimator and have all clients (including the server) set the animation parameters.

    The reason you're seeing this is because your animation parameters depend on two things: a networkvariable
    networkPlayerState
    , which is set with a significant delay because the moving client has to communicate with the server and back, and
    change
    , which is set locally and is changed immediately upon button presses. The two are therefore not in sync.

    What I would do is have each client set the animation parameters of all player objects (so no networkAnimators), but make sure that the owner of the player does so immediately, without waiting for communication from the server. This way your own movement feels more responsive. This will require some rewriting of your current setup, but you should have more than enough information now to make it work.
     
  14. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    Thank you for help but I don't get it... I tried 5 different scripts, 20 different versions of it and my network animation won't work.

    Thank you for your help, but I try something else... When a simple animation takes me 2 weeks of work and still not working, that's sad...
     
  15. OneUnity3D

    OneUnity3D

    Joined:
    Aug 7, 2019
    Posts:
    28
    I did it!!! :) Thank you CosmoM, now I wrote a complete new script with many new things I've learned and the movement and animation works on host and client!

    #closed thread