Unity – cum pot face să funcționeze ciclul de animație de salt? (Programare, C#, Unity3D, Animație)

Abby a intrebat.

Sunt nou la unitate și C#, așa că aș aprecia orice ajutor.

Mi-am făcut sprite-ul meu să sară care funcționează bine, totuși, singura animație care va juca este animația de aterizare. Animația de decolare nu se va juca și sprite rămâne într-o poziție inactivă în timp ce sare până când viteza scade sub 0, apoi se joacă animația de aterizare.

Ce fac greșit? Sper să obțin o animație de decolare care să se joace atunci când jucătorul sare și apoi să treacă direct la o animație de aterizare atunci când cade.

Iată codul meu:

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

Comentarii

  • Puteți deschide ferestrele animatorului de la ferestre -> animator și apoi să jucați jocul, faceți clic pe jucător și apăsați spațiu și vedeți ce se întâmplă cu animația dvs. S-ar putea ca animația să fie redată foarte repede și apoi să treacă din nou la inactivitate sau să înceapă să joace și apoi să revină brusc la inactivitate. Verificați care este cauza revenirii la inactivitate, poate că ar trebui să vă jucați cu proprietatea de timp de ieșire a animației dvs. –  > Por Çağatay IȘIK.
1 răspunsuri
Ghimbir și levănțică

Cred că modul în care ai configurat animațiile tale face ca acest lucru să fie mai dificil decât este necesar. Hai să schimbăm câteva lucruri care sperăm că vor face animația personajului tău un pic mai ușoară.

În primul rând, părerea mea este că utilizarea unei animații trigger nu este fiabilă atunci când vine vorba de scriptul animațiilor de sărituri. O abordare mai bună este de a crea un float în animatorul tău, pe care eu l-am numit velocityY, care reprezintă Rigidbody2D.velocity.y. Am creat, de asemenea, un nou element bool numit isGrounded, deoarece cred că este mai clar și mai aplicabil la multe scenarii de „sărituri”.

După ce aceste variabile au fost create, puteți lega cele trei animații – idle, jump și land – în felul următor:

  1. Setați animația implicită la „idle”.
  2. Faceți o tranziție de la „idle” la „jump” în care condițiile sunt:

    • velocityY > 0
    • isGrounded = false
  3. Faceți o tranziție de la „salt” la „aterizare” în care condițiile să fie: – „salt” la „aterizare”:

    • velocityY < 0
    • isGrounded = false
  4. Efectuați o tranziție de la „aterizare” la „repaus” atunci când isGrounded = true.

  5. În cele din urmă, pentru a vă anima personajul atunci când cade (fără a sări mai întâi), puteți face opțional o tranziție de la „idle” la „land” în care: :

    • velocityY < 0
    • isGrounded = false

Acum trecem la cod. Iată un exemplu funcțional pe care l-am testat într-un proiect pentru a obține rezultatele dorite. Rețineți că nu am inclus totul în scriptul dvs., ci doar părțile care permit personajului să se miște și să își anime corect saltul. Încercați să folosiți acest script și să vă jucați cu valorile de mișcare, precum și cu multiplicatorul de gravitație de pe jucătorul dvs. Rigidbody2D componentă; valorile implicite și un multiplicator de gravitație de 3,5 mi s-au părut amuzante!

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;
        }
    }
}

De asemenea, mi-am luat ceva timp pentru a vă reorganiza codul. Nu trebuie neapărat să te preocupi de prea multă organizare acum, dar m-am gândit că te-ar putea interesa, deoarece încă înveți.

După cum puteți vedea, fiecare metodă din script se ocupă de o sarcină concretă. De exemplu, există o metodă care se ocupă exclusiv de animație și o alta care permite jucătorului să sară. Este o idee bună să vă configurați codul în acest fel, astfel încât, dacă mai târziu trebuie să schimbați un aspect, cum ar fi mișcarea jucătorului, atunci tot codul aferent se află în același loc. Cred că acest lucru este valabil mai ales pentru crearea de metode care se ocupă de „acțiuni” ale jucătorului, cum ar fi săriturile sau atacurile; dacă aveți destule, puteți crea chiar o întreagă secțiune Actions class.

Un ultim lucru care trebuie menționat este această linie de cod:

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

Am descoperit că aceasta era o modalitate mai ușoară de a determina când jucătorul este la sol. Am realizat acest lucru prin adăugarea unui copil GameObject la jucător, adăugând o pictogramă colorată pentru a facilita plasarea (puteți face acest lucru făcând clic pe caseta colorată de lângă numele unui GameObject), și plasându-l între picioarele jucătorului.

Comentarii

  • mulțumesc pentru o explicație detaliată. acest răspuns ar trebui să fie marcat ca fiind cel corect –  > Por rkyr.