Unity-如何使跳跃动画周期发挥作用?

时间:2018-08-14 15:41:32

标签: c# unity3d animation

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

1 个答案:

答案 0 :(得分:0)

我认为您设置动画的方式使这项工作变得更具挑战性。让我们进行一些更改,希望可以使角色动画变得更加轻松。

首先,我认为在编写跳跃动画脚本时,使用动画n是不可靠的。更好的方法是在动画师中创建一个trigger,我将其称为 velocityY ,它代表玩家的float。我还创建了一个名为 isGrounded 的新Rigidbody2D.velocity.y,因为我认为这更清楚,更适用于许多“跳跃”场景。

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

  1. 将默认动画设置为“空闲”。
  2. 从“空闲”过渡到“跳跃”,条件是:

    • bool
    • velocityY > 0
  3. 在以下条件下从“跳跃”过渡到“土地”:

    • isGrounded = false
    • velocityY < 0
  4. isGrounded = false时从“土地”过渡到“空闲”。

  5. 最后,要为角色掉落时的动画(不先跳动),您可以选择从“空闲”过渡到“降落”,其中:

    • isGrounded = true
    • velocityY < 0

现在输入代码。这是我在一个项目中测试过的有效示例,可以达到您想要的结果。请注意,我并未在脚本中包含所有内容,仅包含了使角色移动并使其跳跃动画正确的部分。尝试使用此脚本并使用玩家的isGrounded = false组件上的运动值以及重力倍数;默认值和3.5的重力倍数对我来说很有趣!

Rigidbody2D

我还花了一些时间来重组您的代码。您不一定现在就不必担心太多的组织,但是我认为这可能会使您感兴趣,因为您仍在学习。

如您所见,脚本中的每个方法都处理一个具体任务。例如,有一种方法专门用于处理动画,另一种方法可以让播放器跳转。以此方式设置代码是一个好主意,这样,如果以后必须更改一个方面(例如玩家移动),那么所有相关代码都放在同一位置。我认为这对于创建处理球员“动作”(例如跳跃或进攻)的方法尤为正确。如果您有足够的它们,您甚至可以创建整个动作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; } } }

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

class

我发现这是确定播放器何时接地的更简便方法。为此,我向播放器添加了一个孩子isGrounded = Physics2D.OverlapCircle(groundPoint.position, groundRadius, whatIsGround); ,并添加了一个彩色图标以方便放置(您可以通过点击GameObject名称旁边的彩色框来完成此操作),然后将其放置在在球员的脚之间。