Can you force a 2D sprite to change to a different animation & Flip direction on the same frame

I’m trying to get a 2d wall jump animation to work smoothly, the only problem I have is that the animations seem to transition 1/2 frames after the sprite flips, causing this stuttery animation look which just doesn’t look nice to the eyes.

This is my first time working with animations and 2D Sprites so I might not be explaining myself to the best that I can, so apologies for that

Here are the following 4 images that occour during a walljump cycle

In the image number 3. happens right after you press the Jump key, which I think causes the player to gain velocity in the left direction. The Flip() method picks up on this during the next game cycle and causes the sprite to flip. However the AnimationController() picks up on the new status of the player jumping and changes to the jumping animation a bit later on.

It would look a lot better if I could remove number 3. from the sequence (or hide it somehow)

It’s probably my lack of knowledge on this but here’s some ideas I have tried but not gotten them to work as of yet
A) Delay the sprite flip until the animation changes
B) Force the Animation to change before the Wall Jump force is applied

Here is my code for the player movement

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{
    private bool isIdle = false;


    private float horizontal;
    private float speed = 7f;
    private float jumpingPower = 14f;
    private bool isFacingRight = true;

    private bool isWallSliding = true;
    private float wallSlidingSpeed = 2f;

    private bool isWallJumpingAnimationFlipped = false;

    private bool isWallJumping;
    private float wallJumpingDirection;
    private float wallJumpingTime = 0.2f;
    private float wallJumpingCounter;
    private float wallJumpingDuration = 0.8f;
    private Vector2 wallJumpingPower = new Vector2(7f, 14f);

    [SerializeField] Animator anim;
    [SerializeField] private Rigidbody2D rb;
    [SerializeField] private Transform wallCheck;
    [SerializeField] private LayerMask wallLayer;
    [SerializeField] private float wallCheckDistance;

    // Update is called once per frame
    void Update()
    {
        horizontal = Input.GetAxis("Horizontal");
        PlayerMovements();
    }

    private void FixedUpdate()
    {
        AnimationController();
    }

    private void PlayerMovements()
    {
        WallSlide();
        WallJump();

        // Temp code for moving sprite round
        if (!isWallJumping)
        {
            rb.velocity = new Vector2(horizontal * speed, rb.velocity.y);
        }
    }

    private bool IsWalled()
    {
        return Physics2D.OverlapCircle(wallCheck.position, 0.09f, wallLayer);
    }

    private void WallSlide()
    {
        if (IsWalled())
        {
            isWallSliding = true;
            rb.velocity = new Vector2(rb.velocity.x, Mathf.Clamp(rb.velocity.y, -wallSlidingSpeed, float.MaxValue));
            //Debug.Log("Player is walled, y velocity should be 2f");
        }
        else
        {
            isWallSliding = false;
        }
    }    
    
    private void WallJump()
    {
        if (isWallSliding)
        {
            isWallJumping = false;
            wallJumpingDirection = -transform.localScale.x;
            wallJumpingCounter = wallJumpingTime;

            CancelInvoke(nameof(StopWallJumping));
        }
        else
        {
            wallJumpingCounter -= Time.deltaTime;
        }
        if (Input.GetButtonDown("Jump") && wallJumpingCounter > 0f)
        {
            isWallJumping = true;
            rb.velocity = new Vector2(wallJumpingDirection * wallJumpingPower.x, wallJumpingPower.y);
            wallJumpingCounter = 0f;

            Invoke(nameof(StopWallJumping), wallJumpingDuration);
            if (transform.localScale.x != wallJumpingDirection)
            {
                Debug.Log("wall jumping flip");
                isFacingRight = !isFacingRight;
                Vector3 localScale = transform.localScale;
                localScale.x *= -1;
                transform.localScale = localScale;
                wallCheckDistance *= -1f;
            }
        }
    }

    private void StopWallJumping()
    {
        isWallJumping = false;
    }

    private void Flip()
    {
        if (isFacingRight && horizontal < 0f || !isFacingRight && horizontal > 0f)
        {
            Debug.Log("Flipping");
            isFacingRight = !isFacingRight;
            Vector2 localScale = transform.localScale;
            localScale.x *= -1f;
            transform.localScale = localScale;
            wallCheckDistance *= -1f;
        }
    }

    private void AnimationController()
    {
        anim.SetBool("isIdle", isIdle);
        anim.SetBool("isWallSliding", isWallSliding);
        anim.SetBool("isWallJumping", isWallJumping);
    }

}

Any help is appreciated :slight_smile:

Always do animation-related stuff in Update. FixedUpdate can be called multiple times or not at all in a single frame and lead to your animation code running unnecessarily or be skipped when it shouldn’t.

FixedUpdate is also called before Update, which leads me to believe your animation might still be lagging one frame. The Animator will first be updated with the values from the last frame in FixedUpdate, and only afterwards will Update run and update the values, which in turn will only be reflected in the frame after the current one.

On the other hand, physics-related code should generally go into FixedUpdate. I don’t think it makes a big difference with your current code, but due to the varying number of FixedUpdate per frame, your code can introduce subtle inconsistencies because of that.

Input and impulse-physics can still remain in Update but especially constant forces need to be in FixedUpdate.

I managed to find the issue. It was that I had Transition Duration set to 0.15 on the transition from ‘AnyState’ to the wall sliding animation seen in image 2.0.
Changing this to 0 has resolved the issue
image