首页 文章

Unity - 如何让我的跳跃动画循环起作用?

提问于
浏览
3

我是团结和C#的新手所以非常感谢任何帮助 .

我已经使我的精灵跳跃工作得很好,但是,唯一会播放的动画是着陆动画 . 起飞动画将不会播放并且精灵停留在空闲位置,同时跳跃直到速度低于0然后着陆动画播放 .

我究竟做错了什么?我希望在玩家跳起来然后直接进入落地动画时播放起飞动画 .

这是我的代码:

using UnityEngine;

public class Player : MonoBehaviour
{
    private Rigidbody2D myRigidbody;

    private Animator myAnimator;

    [SerializeField]
    private float movementSpeed;

    private bool facingRight;

    private bool attack;

    private bool slide;

    [SerializeField]
    private Transform[] groundPoints;

    [SerializeField]
    private float groundRadius;

    [SerializeField]
    private LayerMask whatIsGround;

    private bool isGrounded;

    private bool jump;

    private bool airControl;

    [SerializeField]
    private float jumpForce;

    // Use this for initialization
    void Start()
    {
        facingRight = true;
        myRigidbody = GetComponent<Rigidbody2D>();
        myAnimator = GetComponent<Animator>();
    }

    void Update()
    {
        HandleInput();
    }

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

        HandleMovement(horizontal);

        isGrounded = IsGrounded();

        Flip(horizontal);

        HandleAttacks();

        HandleLayers();

        ResetValues();
    }

    private void HandleMovement(float horizontal)
    {
        if (myRigidbody.velocity.y < 0)
        {
            myAnimator.SetBool("land", true);
        }

        if (!myAnimator.GetBool("slide") && !this.myAnimator.GetCurrentAnimatorStateInfo(0).IsTag("Attack")&&(isGrounded || airControl))
        {
            myRigidbody.velocity = new Vector2(horizontal * movementSpeed, myRigidbody.velocity.y);

        }
        if (isGrounded && jump)
        {
            isGrounded = false;
            myRigidbody.AddForce(new Vector2(0, jumpForce));
            myAnimator.SetTrigger("jump");

        }
        if (slide && !this.myAnimator.GetCurrentAnimatorStateInfo(0).IsName("Slide"))
        {
            myAnimator.SetBool("slide", true);
        }
        else if (!this.myAnimator.GetCurrentAnimatorStateInfo(0).IsName("Slide"))
        {
            myAnimator.SetBool("slide", false);
        }
        myAnimator.SetFloat("speed", Mathf.Abs(horizontal));
    }

    private void HandleAttacks()
    {
        if (attack && !this.myAnimator.GetCurrentAnimatorStateInfo(0).IsTag("Attack"))
        {
            myAnimator.SetTrigger("attack");
            myRigidbody.velocity = Vector2.zero;
        }

    }

    private void HandleInput()
    {
        if(Input.GetKeyDown(KeyCode.Space))
        {
            jump = true;
        }
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            attack = true;
        }

        if (Input.GetKeyDown(KeyCode.LeftControl))
        {
            slide = true;
        }
    }

    private void Flip(float horizontal)
    {
        if (horizontal > 0 && !facingRight || horizontal < 0 && facingRight)
        {
            facingRight = !facingRight;

            Vector3 theScale = transform.localScale;

            theScale.x *= -1;

            transform.localScale = theScale;
        }
    }

    private void ResetValues()
    {
        attack = false;
        slide = false;
        jump = false;
    }

    private bool IsGrounded()
    {
        if (myRigidbody.velocity.y <= 0)
        {
            foreach (Transform point in groundPoints)
            {
                Collider2D[] colliders = Physics2D.OverlapCircleAll(point.position, groundRadius, whatIsGround);

                for (int i = 0; i < colliders.Length; i++)
                {
                    if (colliders[i].gameObject != gameObject)
                    {
                        myAnimator.ResetTrigger("jump");
                        myAnimator.SetBool("land", false);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void HandleLayers()
    {
        if (!isGrounded)
        {
            myAnimator.SetLayerWeight(1, 1);
        }
        else 
        {
            myAnimator.SetLayerWeight(1, 0);
        }
    }
}

1 回答

  • 0

    我认为你设置动画的方式使得这比必要的更具挑战性 . 让我们改变一些有希望使你的角色更容易动画的东西 .

    首先,我认为使用动画 trigger 在脚本跳跃动画方面是不可靠的 . 更好的方法是在动画师中创建 float ,我称之为 velocityY ,代表玩家的 Rigidbody2D.velocity.y . 我还创建了一个名为 isGrounded 的新 bool ,因为我认为这更清晰,更适用于许多场景 .

    创建这些变量后,您可以通过以下方式链接三个动画 - 空闲,跳跃和着陆 -

    • 将默认动画设置为"idle" .

    • 从“空闲”转换为“跳转”,条件是:

    • velocityY > 0

    • isGrounded = false

    • 使用以下条件从“跳转”转换为“土地”:

    • velocityY < 0

    • isGrounded = false

    • isGrounded = true 时从"land"过渡到"idle" .

    • 最后,要在角色落下时为其设置动画(不先跳跃),您可以选择从“空闲”转换为“陆地”,其中:

    • velocityY < 0

    • isGrounded = false

    现在到代码 . 这里's a working example that I tested in a project to achieve your desired results. Note that I did not include everything in your script, just the parts that let the character move and animate its jump correctly. Try using this script and playing around with the movement values, as well as the gravity multiplier on your player' s Rigidbody2D 组件;默认值和重力乘数3.5对我来说很有趣!

    using UnityEngine;
    
    public class Player : MonoBehaviour
    {
        //Components on Player GameObject
        private Rigidbody2D myRigidbody;
        private Animator myAnimator;
    
        //Movement variables
        [SerializeField]
        private float movementSpeed = 9; //Set default values so you don't always
        [SerializeField]                //have to remember to set them in the inspector
        private float jumpForce = 15;
    
        //Ground checking
        [SerializeField]
        private Transform groundPoint;
        [SerializeField]
        private float groundRadius = 0.1f;
        [SerializeField]
        private LayerMask whatIsGround;
    
        private float velocityX;
        private bool isGrounded;
        private bool facingRight;
    
        // Use this for initialization
        private void Start()
        {
            facingRight = true;
            myRigidbody = GetComponent<Rigidbody2D>();
            myAnimator = GetComponent<Animator>();
        }
    
        private void Update()
        {
            Flip();
            HandleInput();
            HandleAnimations();
        }
    
        private void FixedUpdate()
        {                       
            HandleMovement();  //It's generally considered good practice to 
                               //call physics-related methods in FixedUpdate
        }
    
        private void HandleAnimations()
        {
            if (!isGrounded)
            {
                myAnimator.SetBool("isGrounded", false);
    
                //Set the animator velocity equal to 1 * the vertical direction in which the player is moving 
                myAnimator.SetFloat("velocityY", 1 * Mathf.Sign(myRigidbody.velocity.y));
            }
    
            if (isGrounded)
            {
                myAnimator.SetBool("isGrounded", true);
                myAnimator.SetFloat("velocityY", 0);
            }
        }
    
        private void HandleMovement()
        {
            isGrounded = Physics2D.OverlapCircle(groundPoint.position, groundRadius, whatIsGround);
    
            velocityX = Input.GetAxis("Horizontal");
    
            myRigidbody.velocity = new Vector2(velocityX * movementSpeed , myRigidbody.velocity.y);
        }
    
        private void HandleInput()
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
        }
    
        private void Jump()
        {
            if (isGrounded)
            {   //ForceMode2D.Impulse is useful if Jump() is called using GetKeyDown
                myRigidbody.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
            }
    
            else 
            {
                return;
            }       
        }
    
        private void Flip()
        {
            if (velocityX > 0 && !facingRight || velocityX < 0 && facingRight)
            {
                facingRight = !facingRight;
    
                Vector3 theScale = transform.localScale;
                theScale.x *= -1;
    
                transform.localScale = theScale;
            }
        }
    }
    

    我还花了一些时间来重新组织你的代码 . 你现在不一定需要关注太多组织,但我认为你可能会感兴趣,因为你还在学习 .

    如您所见,脚本中的每个方法都处理一个具体的任务 . 例如,以这种方式设置代码是个好主意,这样如果你以后必须改变一个方面,比如玩家移动,那么所有相关的代码都在同一个地方 . 我认为这对于创建处理玩家"actions"的方法尤其如此,例如跳跃或攻击;如果你有足够的,你甚至可以创建一个完整的动作 class .

    最后要提到的是这行代码:

    isGrounded = Physics2D.OverlapCircle(groundPoint.position, groundRadius, whatIsGround);
    

    我发现这是一种更容易确定玩家何时接地的方法 . 我通过向玩家添加一个孩子 GameObject 来实现这个目的,添加一个彩色图标以便于放置(你可以通过点击 GameObject 附近的彩色框来实现这一点),然后将它放在玩家的脚之间 .

相关问题