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

5

我对Unity和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);
        }
    }
}

你能否从窗口->动画师中打开动画师窗口,然后在游戏中点击你的玩家并按下空格键,观察你的动画发生了什么。它可能会以非常快的速度播放动画,然后回到空闲状态,或者它可能会开始播放,然后突然回到空闲状态。检查是什么原因导致它返回到空闲状态,也许你应该调整你的动画的退出时间属性。 - Çağatay IŞIK
1个回答

7
我认为您设置动画的方式比必要的更具挑战性。让我们改变一些事情,希望这样能使角色动画制作变得更容易。
首先,我认为在编写跳跃动画时使用动画触发器是不可靠的。更好的方法是在您的动画器中创建一个浮点数,我称之为velocityY,它表示玩家的Rigidbody2D.velocity.y。我还创建了一个新的布尔值叫做isGrounded,因为我认为这更清晰,并且适用于许多“跳跃”场景。
一旦创建了这些变量,您可以按照以下方式链接三个动画 - 空闲、跳跃和着陆:
1. 将默认动画设置为“idle”。 2. 从“idle”到“jump”进行转换,其中条件为: - velocityY > 0 - isGrounded = false 3. 从“jump”到“land”进行转换,其中条件为: - velocityY < 0 - isGrounded = false 4. 当isGrounded = true时,从“land”到“idle”进行转换。 5. 最后,如果角色直接落下(而不是先跳跃),您可以选择从“idle”到“land”进行转换,其中: - velocityY < 0 - isGrounded = false 现在看代码。这是我在一个项目中测试过的实例,可以实现您想要的结果。请注意,我没有包含您脚本中的所有内容,只包括了让角色移动和正确跳跃动画的部分。尝试使用此脚本并玩弄移动值以及玩家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类。
最后要提到的一件事是这行代码:
isGrounded = Physics2D.OverlapCircle(groundPoint.position, groundRadius, whatIsGround);

我发现这是一种更容易确定玩家是否着陆的方法。我通过向玩家添加一个子GameObject,添加一个彩色图标以便于放置(您可以通过点击GameObject名称旁边的彩色方框来实现),并将其放置在玩家脚之间来实现这一点。


感谢您详细的解释。那个答案应该被标记为正确的。 - rkyr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接