Background
I am working on animations for 8-way movement for my character and have done the 8 directional running animations using a blend tree.
Now I need to do the same for the idle animations.
When the player stops moving, the correct idle animation should play.
For example, if they move right and let go of the 'A' button, then the idle animation of the player facing right is played.
Problem
I have done 4/8 directions (Up, Left, Right & Down), my problem is with the diagonals.
e.g if the player lets go of the 'W' and 'D' keys then the idle animation of the player facing the Upper Right corner should be played.
I can't get this to work though.
My blend tree for the animations:
I realize that it's because the two keys (Whether W+D, S+D, A+S, or W+A) are let go at different times, and with the system I have currently, the player has to let go of any of those two key combinations at exactly the same time to successfully play the right diagonal animation.
Tl;Dr:
How do I accomplish these post-movement idle animations when my current system relies on the player releasing the correct keys simultaneously?
Is there a system for this, add a tiny bit of input lag or map keys to something?
Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[SerializeField] private Transform pfDashEffect;
public Animator animator;
public LayerMask layerMask;
Vector2 movement;
public float speed = 10f;
public float dashDistance = 100f;
private Vector3 lastMoveDir;
private float MoveX;
private float MoveY;
private void Update()
{
// Input
movement.x = Input.GetAxisRaw("Horizontal");
movement.y = Input.GetAxisRaw("Vertical");
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
animator.SetFloat("Speed", movement.sqrMagnitude);
Move();
Dash();
}
private void FixedUpdate()
{
PlayerAbilities.Dash();
}
private void Move()
{
MoveX = 0f;
MoveY = 0f;
if (Input.GetKey(KeyCode.W))
{
MoveY = +1f;
}
if (Input.GetKey(KeyCode.A))
{
MoveX = -1f;
}
if (Input.GetKey(KeyCode.S))
{
MoveY = -1f;
}
if (Input.GetKey(KeyCode.D))
{
MoveX = +1f;
}
bool isIdle = MoveX == 0 && MoveY == 0;
if (isIdle)
{
// Play idle animation with the lastMoveDir so that the idle animation plays in the right rotation (left, right, up, down)
}
else
{
Vector3 moveDir = new Vector3(MoveX, MoveY).normalized;
if (TryMove(moveDir, speed * Time.deltaTime))
{
// Play walking
animator.SetFloat("Horizontal", movement.x);
animator.SetFloat("Vertical", movement.y);
animator.SetFloat("Speed", movement.sqrMagnitude);
}
else
{
// Play idle
animator.SetFloat("Speed", 0);
}
}
}
private bool CanMove(Vector3 dir, float distance)
{
return Physics2D.Raycast(transform.position, dir, distance, layerMask).collider == null;
}
private bool TryMove(Vector3 baseMoveDir, float distance)
{
Vector3 moveDir = baseMoveDir;
bool canMove = CanMove(moveDir, distance);
if (!canMove)
{
// Cannot move diagonally
moveDir = new Vector3(baseMoveDir.x, 0f).normalized;
canMove = moveDir.x != 0f && CanMove(moveDir, distance);
if (!canMove)
{
// Cannot move horizontally
moveDir = new Vector3(0f, baseMoveDir.y).normalized;
canMove = moveDir.y != 0f && CanMove(moveDir, distance);
}
}
if (canMove)
{
lastMoveDir = moveDir;
// Play walking animation with the moveDir so that the idle animation plays in the right rotation (left, right, up, down)
// storeLastDirection(new Vector3(MoveX, MoveY));
animator.SetFloat("LastMoveVertical", lastMoveDir.y);
animator.SetFloat("LastMoveHorizontal", lastMoveDir.x);
transform.position += moveDir * distance;
return true;
}
else
{
return fase;
}
}
private void Dash()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Vector3 beforeDashPosition = transform.position;
if(TryMove(lastMoveDir, dashDistance))
{
Transform dashEffectTransform = Instantiate(pfDashEffect, beforeDashPosition, Quaternion.identity);
dashEffectTransform.eulerAngles = new Vector3(0, 0, GetAngleFromVectorFloat(lastMoveDir));
float dashEffectWidth = 5;
dashEffectTransform.localScale = new Vector3(dashDistance / dashEffectWidth, 1f, 1f);
}
}
}
public static float GetAngleFromVectorFloat(Vector3 dir)
{
dir = dir.normalized;
float n = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
if (n < 0) n += 360;
return n;
}
}
At the moment I'm just storing the two x and y coordinates for my blend tree like this:
animator.SetFloat("LastMoveVertical", lastMoveDir.y);
animator.SetFloat("LastMoveHorizontal", lastMoveDir.x);
Which works well for the simple directions (North, East, South, West), just not the diagonal ones as the X/Y values change when each button is released. So if the player lets go of the W and 5 milliseconds later lets go of the D key, that's enough for the value to change which then in turn doesn't show the correct animation.
"LastMoveVertical"
and"LastMoveHorizontal"
values instead of themoveDir
values? The moveDir values are not smooth and with the GetAxis you could get smooth values that are not immediately 0, 1, -1. \$\endgroup\$