Search Unity

problems with backward waliking animation in blend tree

Discussion in 'Animation' started by Shags, Aug 10, 2016.

  1. Shags

    Shags

    Joined:
    Apr 28, 2016
    Posts:
    24
    Is there a way to have the animations play in a blend tree based on the direction the player is facing, and not on which controller input you press, w,s,a,d, on a keyboard for example. diagram attached.
     

    Attached Files:

  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Yes, you need to write a script in c# that convert your input into local coordinate for your character.

    I don't how your input is setup right now but how I do it normally is with atan2 and then I scale the result to match my setup.
    axistodirection.png
    atan2(0, x) = 0
    atan2(1,0) = pi/2
    atan2(-1,0) = - pi/2
    atan2(0, -x) = pi

    Code (CSharp):
    1.  
    2. float h = Input.GetAxis("Horizontal");
    3. float v = Input.GetAxis("Vertical");
    4.  
    5. // scaling output of atan2 to match blend tree parametrization that goes from -100 to 100
    6. float direction = Mathf.Atan2(h, v) * 180.0f / 3.14159f;
    7. animator.SetFloat("Speed", h*h+v*v);
    8. animator.SetFloat("Direction", direction, 0.25f, Time.deltaTime);
    9.  
    Here the blend tree that goes with this, it pretty big and have a lot of motion to increase animation fidelity, but the goal is to show you the mapping between the direction parameter and all the clip that are going to be played depending on the direction.
    axistodirectionblendtree.png
     
    Last edited: Aug 10, 2016
  3. Shags

    Shags

    Joined:
    Apr 28, 2016
    Posts:
    24
    Hi,
    Thanks for replying.I have two questions (1) is the c# script best used as part of the StateMachineBehaviour or as a separate MonoBehaviours script. (2) I know you said "Here the blend tree that goes with this" But is there any chance of altering my current setup.If the setup you have shown is the best remedy for my problem then I will gladly use it. I just thought I would try and save myself some time and extra work if I could.Thanks for your help. Blend tree set up.png
     
  4. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    I would say in this case use a MonoBehaviour, it will be called at each frame independantly of the current state playing.
    A StateMachineBehaviour(aka SMB) is only called while your statemachine is currently playing the state on which you added the SMB, so to handle input correctly you would need to add the same script on all state that handle your character movement, which is not really convenient and need more maintenance if you need to change something.

    Yes that could work with your current setup but in this case since you already have the whole range of motion express as a 2d cartesian, you do need to use atan2 since you are mapping the horizontal and vertical axis directly into your blend tree.
    when you press w it should yield h=0 and v=1 which is mapped to your walk animation so your character should start to walk forward independantly of the position of the character in the camera space.

    Does it work?
     
  5. Shags

    Shags

    Joined:
    Apr 28, 2016
    Posts:
    24
    No it doesn't work, But this is most definitely my fault. Due to my limited knowledge of programing. I do the tutorials and then use them as a guide. So my code tends to resemble a patch work quilt, with borrowed bits and pieces. I received a Parameter 'Direction' does not exist alert. I have commented in the section of the code that I think might be the problem. Thank you for your patience.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerMovement : MonoBehaviour
    4. {
    5.     public float speed = 6f;            // The speed that the player will move at.
    6.  
    7.     Vector3 movement;                   // The vector to store the direction of the player's movement.
    8.     Animator anim;                      // Reference to the animator component.
    9.     Rigidbody playerRigidbody;          // Reference to the player's rigidbody.
    10.     int floorMask;                      // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
    11.     float camRayLength = 100f;          // The length of the ray from the camera into the scene.
    12.  
    13.     void Awake ()
    14.     {
    15.         // Create a layer mask for the floor layer.
    16.         floorMask = LayerMask.GetMask ("Floor");
    17.  
    18.         // Set up references.
    19.         anim = GetComponent <Animator> ();
    20.         playerRigidbody = GetComponent <Rigidbody> ();
    21.     }
    22.  
    23.  
    24.     void FixedUpdate ()
    25.     {
    26.         // Store the input axes.
    27.         float h = Input.GetAxisRaw ("Horizontal");
    28.         float v = Input.GetAxisRaw ("Vertical");
    29.  
    30.         // scaling output of atan2 to match blend tree parametrization that goes from -100 to 100
    31.         float direction = Mathf.Atan2(h, v) * 180.0f / 3.14159f;
    32.         anim.SetFloat("Speed", h*h+v*v);
    33.         anim.SetFloat("Direction", direction, 0.25f, Time.deltaTime);
    34.  
    35.  
    36.         // Move the player around the scene.
    37.         Move (h, v);
    38.  
    39.         // Turn the player to face the mouse cursor.
    40.         Turning ();
    41.  
    42.         // Animate the player.
    43.         Animating (h, v);
    44.     }
    45.  
    46.     void Move (float h, float v)
    47.     {
    48.         // Set the movement vector based on the axis input.
    49.         movement.Set (h, 0f, v);
    50.  
    51.         // Normalise the movement vector and make it proportional to the speed per second.
    52.         movement = movement.normalized * speed * Time.deltaTime;
    53.  
    54.         // Move the player to it's current position plus the movement.
    55.         playerRigidbody.MovePosition (transform.position + movement);
    56.  
    57.     }
    58.  
    59.     void Turning ()
    60.     {
    61.         // Create a ray from the mouse cursor on screen in the direction of the camera.
    62.         Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
    63.  
    64.         // Create a RaycastHit variable to store information about what was hit by the ray.
    65.         RaycastHit floorHit;
    66.  
    67.         // Perform the raycast and if it hits something on the floor layer...
    68.         if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
    69.         {
    70.             // Create a vector from the player to the point on the floor the raycast from the mouse hit.
    71.             Vector3 playerToMouse = floorHit.point - transform.position;
    72.  
    73.             // Ensure the vector is entirely along the floor plane.
    74.             playerToMouse.y = 0f;
    75.  
    76.             // Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
    77.             Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
    78.  
    79.             // Set the player's rotation to this new rotation.
    80.             playerRigidbody.MoveRotation (newRotation);
    81.         }
    82.     }
    83.  
    84.     void Animating (float h, float v)//this might be where the problem is, I replaced a transition from a idle animation to a walking animation with a blend tree
    85.     {
    86.         // Create a boolean that is true if either of the input axes is non-zero.
    87.         bool walking = h != 0f || v != 0f;
    88.  
    89.         // Tell the animator whether or not the player is walking.
    90.         anim.SetBool ("IsWalking", walking);
    91.     }
    92. }
    93.  
    94.  
     

    Attached Files:

  6. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    This is expected, in my example blend tree's parameter were called Direction and Speed but in your you did call them Horizontal and Vertical so you will need to change

    anim.SetFloat("Vertical", h*h+v*v);
    anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);

    also in my example my blend tree was setup with 1d blend tree with a range going from -180 to 180 but in your case you did use a 2d blend tree with a range -1 to 1 for horizontal and -1 to 1 for Vertical so you will need to adjust

    // Atan2 output a range of -pi/2 to pi/2, scale it to -1 to 1 to match your blend tree
    float direction = Mathf.Atan2(h, v) / ( Mathf.PI/2.0f );
    anim.SetFloat("Vertical", h*h+v*v);
    anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);

    Your move function need to be review, not sure that mapping vertical and horizontal axis directly on the GO transform position will give you the expected result.
     
  7. Shags

    Shags

    Joined:
    Apr 28, 2016
    Posts:
    24
    "Your move function need to be review, not sure that mapping vertical and horizontal axis directly on the GO transform position will give you the expected result."

    I'm not sure what you are telling me here, I'm assuming you wanted me to try it and see, and tell you what happened. So I have, and I received an error message in console (see screen grab) I have also included the code with the changes made. Console error.png
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerMovement : MonoBehaviour
    4. {
    5.     public float speed = 6f;            // The speed that the player will move at.
    6.  
    7.     Vector3 movement;                   // The vector to store the direction of the player's movement.
    8.     Animator anim;                      // Reference to the animator component.
    9.     Rigidbody playerRigidbody;          // Reference to the player's rigidbody.
    10.     int floorMask;                      // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
    11.     float camRayLength = 100f;          // The length of the ray from the camera into the scene.
    12.  
    13.     void Awake ()
    14.     {
    15.         // Create a layer mask for the floor layer.
    16.         floorMask = LayerMask.GetMask ("Floor");
    17.  
    18.         // Set up references.
    19.         anim = GetComponent <Animator> ();
    20.         playerRigidbody = GetComponent <Rigidbody> ();
    21.     }
    22.  
    23.  
    24.     void FixedUpdate ()
    25.     {
    26.         // Store the input axes.
    27.         anim.SetFloat("Vertical", h*h+v*v);
    28.         anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);
    29.  
    30.  
    31.         // Atan2 output a range of -pi/2 to pi/2, scale it to -1 to 1 to match your blend tree
    32.         float direction = Mathf.Atan2(h, v) / ( Mathf.PI/2.0f );
    33.         anim.SetFloat("Vertical", h*h+v*v);
    34.         anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);
    35.  
    36.  
    37.         // Move the player around the scene.
    38.         Move (h, v);
    39.  
    40.         // Turn the player to face the mouse cursor.
    41.         Turning ();
    42.  
    43.         // Animate the player.
    44.         Animating (h, v);
    45.     }
    46.  
    47.     void Move (float h, float v)
    48.     {
    49.         // Set the movement vector based on the axis input.
    50.         movement.Set (h, 0f, v);
    51.  
    52.         // Normalise the movement vector and make it proportional to the speed per second.
    53.         movement = movement.normalized * speed * Time.deltaTime;
    54.  
    55.         // Move the player to it's current position plus the movement.
    56.         playerRigidbody.MovePosition (transform.position + movement);
    57.  
    58.     }
    59.  
    60.     void Turning ()
    61.     {
    62.         // Create a ray from the mouse cursor on screen in the direction of the camera.
    63.         Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
    64.  
    65.         // Create a RaycastHit variable to store information about what was hit by the ray.
    66.         RaycastHit floorHit;
    67.  
    68.         // Perform the raycast and if it hits something on the floor layer...
    69.         if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
    70.         {
    71.             // Create a vector from the player to the point on the floor the raycast from the mouse hit.
    72.             Vector3 playerToMouse = floorHit.point - transform.position;
    73.  
    74.             // Ensure the vector is entirely along the floor plane.
    75.             playerToMouse.y = 0f;
    76.  
    77.             // Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
    78.             Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
    79.  
    80.             // Set the player's rotation to this new rotation.
    81.             playerRigidbody.MoveRotation (newRotation);
    82.         }
    83.     }
    84.  
    85.     void Animating (float h, float v)//this might be where the problem is, I replaced a transition from a idle animation to a walking animation with a blend tree
    86.     {
    87.         // Create a boolean that is true if either of the input axes is non-zero.
    88.         bool walking = h != 0f || v != 0f;
    89.  
    90.         // Tell the animator whether or not the player is walking.
    91.         anim.SetBool ("IsWalking", walking);
    92.     }
    93. }
    94.  
    95.  
     
  8. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    why did you removed these two line they are needed to read user input?
    1. // Store the input axes.
    2. float h = Input.GetAxisRaw ("Horizontal");
    3. float v = Input.GetAxisRaw ("Vertical");
    Other wise you will get a script error at line 27, h and v doesn't exist
     
  9. Shags

    Shags

    Joined:
    Apr 28, 2016
    Posts:
    24
    I put it back in, now all the animations play out of sequence. And they are very jerky.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerMovement : MonoBehaviour
    4. {
    5.     public float speed = 6f;            // The speed that the player will move at.
    6.  
    7.     Vector3 movement;                   // The vector to store the direction of the player's movement.
    8.     Animator anim;                      // Reference to the animator component.
    9.     Rigidbody playerRigidbody;          // Reference to the player's rigidbody.
    10.     int floorMask;                      // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
    11.     float camRayLength = 100f;          // The length of the ray from the camera into the scene.
    12.  
    13.     void Awake ()
    14.     {
    15.         // Create a layer mask for the floor layer.
    16.         floorMask = LayerMask.GetMask ("Floor");
    17.  
    18.         // Set up references.
    19.         anim = GetComponent <Animator> ();
    20.         playerRigidbody = GetComponent <Rigidbody> ();
    21.     }
    22.  
    23.  
    24.     void FixedUpdate ()
    25.     {
    26.         // Store the input axes.
    27.         float h = Input.GetAxisRaw ("Horizontal");
    28.         float v = Input.GetAxisRaw ("Vertical");
    29.  
    30.         // scaling output of atan2 to match blend tree parametrization that goes from -100 to 100
    31.         //float direction = Mathf.Atan2(h, v) * 180.0f / 3.14159f;
    32.         //anim.SetFloat("Speed", h*h+v*v);
    33.         //anim.SetFloat("Direction", direction, 0.25f, Time.deltaTime);
    34.  
    35.         // Atan2 output a range of -pi/2 to pi/2, scale it to -1 to 1 to match your blend tree
    36.         float direction = Mathf.Atan2(h, v) / ( Mathf.PI/2.0f );
    37.         anim.SetFloat("Vertical", h*h+v*v);
    38.         anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);
    39.  
    40.         anim.SetFloat("Vertical", h*h+v*v);
    41.         anim.SetFloat("Horizontal", direction, 0.25f, Time.deltaTime);
    42.  
    43.         // Move the player around the scene.
    44.         Move (h, v);
    45.  
    46.         // Turn the player to face the mouse cursor.
    47.         Turning ();
    48.  
    49.         // Animate the player.
    50.         Animating (h, v);
    51.     }
    52.  
    53.     void Move (float h, float v)
    54.     {
    55.         // Set the movement vector based on the axis input.
    56.         movement.Set (h, 0f, v);
    57.  
    58.         // Normalise the movement vector and make it proportional to the speed per second.
    59.         movement = movement.normalized * speed * Time.deltaTime;
    60.  
    61.         // Move the player to it's current position plus the movement.
    62.         playerRigidbody.MovePosition (transform.position + movement);
    63.  
    64.     }
    65.  
    66.     void Turning ()
    67.     {
    68.         // Create a ray from the mouse cursor on screen in the direction of the camera.
    69.         Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
    70.  
    71.         // Create a RaycastHit variable to store information about what was hit by the ray.
    72.         RaycastHit floorHit;
    73.  
    74.         // Perform the raycast and if it hits something on the floor layer...
    75.         if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
    76.         {
    77.             // Create a vector from the player to the point on the floor the raycast from the mouse hit.
    78.             Vector3 playerToMouse = floorHit.point - transform.position;
    79.  
    80.             // Ensure the vector is entirely along the floor plane.
    81.             playerToMouse.y = 0f;
    82.  
    83.             // Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
    84.             Quaternion newRotation = Quaternion.LookRotation (playerToMouse);
    85.  
    86.             // Set the player's rotation to this new rotation.
    87.             playerRigidbody.MoveRotation (newRotation);
    88.         }
    89.     }
    90.  
    91.     void Animating (float h, float v)//this might be where the problem is, I replaced a transition from a idle animation to a walking animationwith a blend tree
    92.     {
    93.         // Create a boolean that is true if either of the input axes is non-zero.
    94.         bool walking = h != 0f || v != 0f;
    95.  
    96.         // Tell the animator whether or not the player is walking.
    97.         anim.SetBool ("IsWalking", walking);
    98.     }
    99. }
    100.  
    101.