Unity3D

Unity) [유니티 3D] 점프 애니메이션 제어

HSH12345 2023. 7. 4. 11:33
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 애니메이션 상태
    const string PLAYER_MOVEMENT = "Movement";
    const string PLAYER_JUMP = "Jump";

    private CharacterController controller;
    private Vector3 playerVelocity;
    private bool isGrounded;
    private float playerWalkSpeed = 2.0f;
    private float playerRunSpeed = 5.0f;
    private bool isMove;
    private bool isRun;
    private Vector3 dirMove;
    private float verAxis;
    private float horAxis;
    private float currentSpeed; 
    private Vector3 jumpDirection;
    private float jumpSpeed;
    private float jumpHeight = 1.0f;
    private float gravityValue = -20.0f;
    private float groundDist = 0.18f;
    private LayerMask groundMask;

    private Camera camMain;    
    [SerializeField]
    private float smoothness = 2f;

    private Animator anim;
    private string currentState;

    public void Init()
    {
        this.camMain = Camera.main;
        this.controller = this.gameObject.GetComponent<CharacterController>();
        this.anim = this.gameObject.GetComponent<Animator>();
        // 각각의 layer 이름으로 LayerMask를 받아온 뒤 OR 연산을 통해 합칩니다.
        this.groundMask = (1 << 6) | (1 << 7);
        this.ChangeAnimState(PLAYER_MOVEMENT);
    }

    private void Update()
    {
        // Raycasting을 이용한 grounded 판별
        this.isGrounded = Physics.Raycast(this.transform.position, Vector3.down, this.groundDist, groundMask);

        if (this.isGrounded && this.playerVelocity.y < 0)
        {
            this.playerVelocity.y = 0f;
            if (this.currentState == PLAYER_JUMP)
            {
                if (this.anim.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !this.anim.IsInTransition(0))
                {
                    this.ChangeAnimState(PLAYER_MOVEMENT);
                }
            }
        }

        if (Input.GetButtonDown("Jump") && this.isGrounded && this.currentState == PLAYER_MOVEMENT)
        {
            // 제곱근을 사용하여 점프의 높이를 조절합니다.
            this.playerVelocity.y = Mathf.Sqrt(this.jumpHeight * -2.0f * this.gravityValue);
            this.ChangeAnimState(PLAYER_JUMP);
            this.jumpDirection = this.dirMove.normalized;
            this.jumpSpeed = this.currentSpeed;
        }

        // 시간을 사용하여 중력 가속도를 구현합니다.
        this.playerVelocity.y += this.gravityValue * Time.deltaTime;

        // 이동
        if (this.currentState == PLAYER_MOVEMENT || this.currentState == PLAYER_JUMP)
        {
            this.MovePlayer();
        }

        // 점프
        this.controller.Move(this.playerVelocity * Time.deltaTime);

        // 달리기
        if (Input.GetKey(KeyCode.LeftShift)) this.isRun = true;
        else this.isRun = false;

    }

    private void MovePlayer()
    {
        this.currentSpeed = this.isRun ? this.playerRunSpeed : this.playerWalkSpeed;

        // 카메라의 정면 방향 벡터의 y 값에 0을 곱합니다.
        Vector3 currentVertical = Vector3.Scale(this.camMain.transform.forward, new Vector3(1, 0, 1));
        // 카메라의 좌우 방향 벡터를 월드좌표로 변환합니다.
        Vector3 currentHorizontal = this.camMain.transform.TransformDirection(Vector3.right);

        // 카메라의 방향과 키 입력값을 방향벡터 값으로 사용합니다.
        if (this.currentState == PLAYER_MOVEMENT)
        {
            this.verAxis = Input.GetAxisRaw("Vertical");
            this.horAxis = Input.GetAxisRaw("Horizontal");
            this.dirMove = currentVertical * this.verAxis + currentHorizontal * this.horAxis;
        }
        else if (this.currentState == PLAYER_JUMP)
        {
            // 점프 중을 때 방향과 속도입니다.
            this.dirMove = this.jumpDirection;
            this.currentSpeed = this.jumpSpeed;
        }
       
        // 캐릭터가 이동 중인지 감자힙나다.
        if (this.dirMove.sqrMagnitude > 0.01f)
        {
            // 이동 방향을 바라보도록 합니다.
            this.gameObject.transform.rotation = Quaternion.Slerp(this.gameObject.transform.rotation,
                Quaternion.LookRotation(this.dirMove), Time.deltaTime * this.smoothness);
            this.isMove = true;
        }
        else
        {
            this.isMove = false;
        } 

        this.controller.Move(this.dirMove.normalized * this.currentSpeed * Time.deltaTime);

        // 애니메이션 Blend를 제어합니다.
        float movementPer = (this.isRun ? 0.8f : 0.4f) * this.dirMove.normalized.magnitude;
        this.anim.SetFloat("Movemnet_Blend", movementPer, 0.2f, Time.deltaTime);
        float jumpPer = (this.isMove ? 1f : 0f);
        this.anim.SetFloat("Jump_Blend", jumpPer, 0f, Time.deltaTime);
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawRay(this.transform.position, Vector3.down * this.groundDist);
    }

    private void ChangeAnimState(string newState)
    {
        if (this.currentState == newState) return;

        anim.Play(newState);
        this.currentState = newState;
    }
}