Search code examples
c#unity-game-engineanimationraycast

Character is floating in the air when death animation is played in unity


I'm new to Unity and I've been trying to make this little short runner game. Everything has been going smoothly except the death animation, which is supposed to be played when either the timer reaches zero or the character's health ends. The animation plays, but my character is floating in the air as the animations plays. See below: Character standing normally death animation plays

I have tried changing the size of the character controller since I suspected that to be the source of my problem:

ctrl.height= 0.16;
ctrl.radius= 0.14;

I've also used raycasting as I searched the Internet and found out it may be useful:

    RaycastHit hit;
    
    // ارسال Ray به سمت پایین از موقعیت فعلی پلیر
    if (Physics.Raycast(transform.position, Vector3.down, out hit, Mathf.Infinity))
    {
        print("Raycast");
        // تنظیم موقعیت Y کاراکتر بر اساس موقعیت برخورد با زمین
        transform.position = new Vector3(transform.position.x, hit.point.y, transform.position.z);

    }

Alas, none of them worked. I also checked and the if statement is working just fine. I honestly don't know what else I can do.

my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class Movement : MonoBehaviour
{

    [SerializeField]private GameObject endtext;
    [SerializeField]private GameObject restart;
    [SerializeField]private GameObject dust;
    [SerializeField]private GameObject Timertext;
    [SerializeField]private GameObject[] health;
    private CharacterController ctrl;
    private float jump;
    private Animator playerAnimator;
    private float gravity;
    private int counter;
    private float timer;
    public bool win;
    public bool gameover;
    // Start is called before the first frame update
    void Start()
    {

        gravity= 15f;
        counter= 0;
        timer= 25;
        gameover= false;
        win= false;
        endtext.SetActive(false);
        restart.SetActive(false);
        ctrl = GetComponent<CharacterController>();
        playerAnimator = GetComponent<Animator>();
        playerAnimator.SetBool("IsRun", false);
        playerAnimator.SetBool("IsJump", false);
        playerAnimator.SetBool("IsDead", false);
        playerAnimator.SetBool("FirstState", true);
    }
    
    // Update is called once per frame
    void Update()
    {
        if (!(win) && !(gameover))
        {

        jump -=  gravity * Time.deltaTime;
        Vector3 direction= new Vector3(Input.GetAxis("Horizontal") * Time.deltaTime * 5, jump * Time.deltaTime, 0);
        ctrl.Move(direction);
        if(Input.GetAxis("Horizontal") > 0)
        {
            transform.rotation= Quaternion.Euler(0, 90, 0);
        }
        if(Input.GetAxis("Horizontal") < 0)
        {
            transform.rotation= Quaternion.Euler(0, 270, 0);
        }
        if(Input.GetKeyDown(KeyCode.Space) && (ctrl.isGrounded)) 
        
        {
            jump= 8;
            playerAnimator.SetBool("IsJump", true);
        }
        else
        {
            playerAnimator.SetBool("IsJump", false);
        }   

        if(direction.x != 0)
        {
            playerAnimator.SetBool("IsRun", true);
        }
        else
        {
            playerAnimator.SetBool("IsRun", false);
        }

        timer -= Time.deltaTime;
        Timertext.GetComponent<Text>().text= ((int)timer).ToString() + " Seconds";

        if(Vector3.Distance(transform.position,GameObject.Find("EndPoint").transform.position) <= 1)
        {
            win= true;
            
        }
        // print(Vector3.Distance(transform.position,GameObject.Find("EndPoint").transform.position));

        if(timer <= 0)
        {
            gameover= true;
        }
        }
        else
        {
            playerAnimator.SetBool("IsRun", false);
            playerAnimator.SetBool("IsJump", false);
                  
            if(gameover)
            {
                Die();
            }
            if(win)
            {
                endtext.GetComponent<Text>().text= "GOOD JOB!";
            }   
            EndScreen();
        }
    }

    void OnTriggerEnter(Collider other)
    {   
        if (!(win) && !(gameover))
        {
        if(other.tag=="Enemy")
        {    

            health[counter].GetComponent<Image>().enabled= false;
            counter ++;
            if (counter > health.Length -1)
            {
                counter= 0;
                gameover= true;
            }
            GameObject clones=Instantiate(dust, other.transform.position, Quaternion.identity);
            Destroy(clones, 1);
        }
        else if(other.tag=="Coin")
        {
            timer += 2;
            Destroy(other.gameObject);
        }
        else if(other.tag=="Chest")
        {
            timer += 5;
            Destroy(other.gameObject);
        }
        }

        
    }

    void Die()
    {

    // // متغیری برای ذخیره اطلاعات Raycast
    RaycastHit hit;
    
    // ارسال Ray به سمت پایین از موقعیت فعلی پلیر
    if (Physics.Raycast(transform.position, Vector3.down, out hit, Mathf.Infinity))
    {
        print("Raycast");
        // تنظیم موقعیت Y کاراکتر بر اساس موقعیت برخورد با زمین
        transform.position = new Vector3(transform.position.x, hit.point.y, transform.position.z);

    }

    playerAnimator.SetBool("FirstState", false);
    // اجرای انیمیشن مرگ
    playerAnimator.SetBool("IsDead",true);

    // سایر کدهای مربوط به مرگ
    endtext.GetComponent<Text>().text= "GAME OVER!";
    endtext.GetComponent<Text>().color= Color.red;
    }

    void EndScreen()
    {
        endtext.SetActive(true);
        restart.SetActive(true);
        // Timertext.transform.position= new Vector3(0, 0, 0);
    }

    public void Restart()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
    }

}

Solution

  • If the animation is not bought and created by someone in your team, ask whoever gave it to you to fix it.

    Otherwise:

    Most likely you are using the animation clip as is from the model's asset straight away. This prevents you from editing the animation, so duplicate the animation clip to make it editable.

    Then add the animation clip in your animator controller and double click on the animation clip asset to open the animation window. Then select your model in the scene with that animator controller and make sure you can preview the animation on that model in the scene from the animation window. Lock your animation window so you won't lose connection to that model by clicking around. Then look in the animation window for the bone that contains the offset. In Humanoid Rigs it will likely be the "Root T", whereas in Generic rigs, the root bone of the skeleton hierarchy. You want to change the position animation for that bone and specifically the Y axis. Switch to curve editing mode and select that bone's position.Y property. This will show you the curve for just the Y values of that property for the length of the animation clip. Press F to frame all keyframes of that property in that viewport. Usually when you click on a property, you see flat lines because it's zoomed out too much. Pressing F will zoom in as much as needed to show you the animation's keyframes in an understandable manner. Then Ctrl+A to select all of them and zoom out enough to be able to drag and drop all those keyframes downwards until that offset from the ground is eliminated. Then after you're done, save and it's done.

    There might be other ways to do this, but this should give you the result you need.