Reputation:
I'm trying to learn unity 2D and now iI need to do animations. I did walking animation, falling animation, jumping animation and the idle animation. However, when I jump, the falling animation doesn't play as intended. The falling animation isn't working properly, so it looks like this:
https://www.awesomescreenshot.com/video/8054610?key=208b095723f0bd4d8dfb936c88485e76
So when I'm falling, it doesn't play the animation right.
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private Animator anim;
private SpriteRenderer sprite;
private enum MovementState { idle, running, jumping, falling }
private float dirX = 0f;
[SerializeField] private float moveSpeed = 7f;
[SerializeField] private float jumpForce = 5f;
private void Start()
{
Debug.Log("program started...");
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
}
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
if (Input.GetButtonDown("Jump"))
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
Animation();
}
private void Animation()
{
MovementState state;
if (dirX > 0f)
{
state = MovementState.running;
sprite.flipX = false;
}
else if (dirX < 0f)
{
state = MovementState.running;
sprite.flipX = true;
}
else
{
state = MovementState.idle;
}
if (rb.velocity.y > .1f)
{
state = MovementState.jumping;
}
else if (rb.velocity.y < -.1f)
{
state = MovementState.falling;
}
anim.SetInteger("state", (int)state);
}
}
i don't know how to fix it, i've searched for about a hour how to fix it.
animations setting (my settings) the settings that i want
Upvotes: 0
Views: 1966
Reputation: 6266
As it has been suggested, I would add what is called a ground check to your player, which is exactly what it sounds like. An easy way to do this would be to Raycast
downward from your player's position offset by the sprite's extents
and only detect casts against a specific ground LayerMask
.
Once you have a ground check done, you can set the state of your animation based on 4 criteria.
With your four states, the logic would look as follows
•Idle - Grounded and X Velocity equals 0 (Or default)
•Running - Grounded and X Velocity not equal to 0
•Jumping - Not Grounded and Y Velocity greater than or equal to 0
•Falling - Not Grounded and Y Velocity less than 0
With this in mind, here is how I would edit your existing code to work as intended
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private Rigidbody2D rb;
private Animator anim;
private SpriteRenderer sprite;
private enum MovementState { idle, running, jumping, falling }
private float dirX = 0f;
[SerializeField] private float moveSpeed = 7f;
[SerializeField] private float jumpForce = 5f;
[SerializeField] private LayerMask groundMask;
[SerializeField] private float groundLengthCheck = 0.03f;
private Vector3 spriteGroundOffset = Vector3.zero;
private bool isGrounded = false;
private void Start()
{
Debug.Log("program started...");
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
// offset the raycast check to be half our sprite bounds in the Y (the bottom of your sprite)
spriteGroundOffset = new Vector3(0f, sprite.bounds.extents.y, 0f);
}
private void Update()
{
dirX = Input.GetAxisRaw("Horizontal");
rb.velocity = new Vector2(dirX * moveSpeed, rb.velocity.y);
// determine if our player is grounded by casting down from their feet with our slight offset
isGrounded = Physics2D.Raycast(transform.position - spriteGroundOffset, Vector2.down, groundLengthCheck, groundMask);
if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce);
}
Animation();
}
private void Animation()
{
// default our state to idle - as if we are just standing
MovementState state = MovementState.idle;
// change
if (isGrounded)
{
if (dirX > 0f)
{
state = MovementState.running;
sprite.flipX = false;
}
else if (dirX < 0f)
{
state = MovementState.running;
sprite.flipX = true;
}
}
else
{
if (rb.velocity.y > 0)
{
state = MovementState.jumping;
}
else if (rb.velocity.y < 0f)
{
state = MovementState.falling;
}
}
anim.SetInteger("state", (int)state);
}
}
Keep in mind
groundMask
to check for this new layer.Here is a gif of the current code in action - I am using a public domain sprite as I do not currently have any art for a 2D player.
Upvotes: 1
Reputation: 1455
I suggest you to use two different floats
for your animations, one for Movement and one for Jumping.
Then something like following to handle them.
private void Animation() {
sprite.flipX = dirX > 0f;
//Do some sort of ground check
if (isGrounded) {
anim.SetFloat("Move", dirX);
}
else {
anim.SetFloat("Jump", rb.velocity.y);
}
}
And of course you need to change your condition that you have setup in your Animator
as well from your Animator window
to make it work.
Upvotes: 1
Reputation: 516
There's a couple of things you can try to do to narrow down the issue. See if commenting out this else statement has an effect on the issue you are experiencing between jump and fall.
else
{
state = MovementState.idle;
}
I think this else could possibly to interrupt your Y velocity animation transition when the object reaches the max height of the jump. Try to comment out different part of your if else statements until you find the culprit. Also try to get rid of the overlap on the transition between jump and fall. I think you want to instantly move from jump animation to fall animation.
Upvotes: 1