본문 바로가기
유니티 개발

3D 캐릭터 움직임 심화

by L3m0n S0ju 2021. 8. 17.

점프, 앉기, 달리기 추가

 

 

 

 

 

 

 

 

 

 


 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    // 스피드 조정 변수
    [SerializeField]
    private float walkSpeed; // 걸음속도
    [SerializeField]
    private float runSpeed; // 달리기속도
    [SerializeField]
    private float crouchSpeed; // 앉기상태에서의 속도
    private float applySpeed; // 임시속도 저장변수

    [SerializeField]
    private float jumpForce; // 점프

    // 상태 변수
    private bool isCrouch = false; // 앉아있는가?
    private bool isRun = false; // 달리고 있는가?
    private bool isGround = true; // 땅에 붙어있는가?

    // 앉기상태, 서있는상태 높이변수
    [SerializeField]
    private float crouchPosY; //앉기상태 시선
    private float originPosY; //서있는상태 시선
    private float applycrouchPosY;

    // 땅 착지 여부
    private CapsuleCollider capsuleCollider;

    // 민감도
    [SerializeField]
    private float lookSensitivity; // 마우스 민감도

    // 카메라 한계
    [SerializeField]
    private float cameraRotationLimit; // 위아래 시선 각도 제한
    private float currentCameraRotationX = 0;

    // 필요한 컴포넌트
    [SerializeField]
    private Camera theCamera;
    private Rigidbody myRigid; 

    void Start() // 시작
    {
        capsuleCollider = GetComponent<CapsuleCollider>(); // 캡슐 로드
        myRigid = GetComponent<Rigidbody>(); // Rigidbody 속성 가지고 있는 컴포넌트 로드
        applySpeed = walkSpeed;

        // 초기화
        originPosY = theCamera.transform.localPosition.y; //서있는상태 카메라 상대위치 저장
        applycrouchPosY = originPosY;
    }

    void Update() // 대략 1초에 60프레임 반복
    {
        IsGround();
        TryJump();
        TryRun(); // 달리기
        TryCrouch(); // 앉기
        Move(); // 캐릭터 움직임
        CameraRotation(); // 카메라 상하각도
        CharacterRotation(); // 캐릭터 좌우각도
    }

    // 앉기 시도
    private void TryCrouch()
    {
        if(Input.GetKeyDown(KeyCode.LeftControl))
        {
            Crouch();
        }
    }

    // 앉기 동작
    private void Crouch()
    {
        isCrouch = !isCrouch; // 앉기상태 변환

        if(isCrouch)
        {
            applySpeed = crouchSpeed;
            applycrouchPosY = crouchPosY;
        }
        else
        {
            applySpeed = walkSpeed;
            applycrouchPosY = originPosY;
        }

        StartCoroutine(CrouchCoroutine()); //카메라 위치변경
    }

    // 부드러운 앉기 동작
    IEnumerator CrouchCoroutine() // 카메라 위치변경
    {
        float _posY=theCamera.transform.localPosition.y;
        int count = 0; // 무한히 실행되므로 카운터를 통해 종료

        while(_posY!=applycrouchPosY) //_posY가 applycrouchPosY와 같을 때 까지 반복
        {
            count++;
            _posY = Mathf.Lerp(_posY, applycrouchPosY, 0.3f); // 0.3 속도로 서서히 변경
            theCamera.transform.localPosition = new Vector3(0, _posY, 0); // 좌표 수정
            if (count > 15) // 15번 반복 후 종료
                break;
            yield return null;
        }
        theCamera.transform.localPosition = new Vector3(0, applycrouchPosY, 0); // 좌표 완전히 수정
    }

    // 지면 체크
    private void IsGround()
    {
        isGround = Physics.Raycast(transform.position, Vector3.down, capsuleCollider.bounds.extents.y+0.1f); // 캡슐 중간 높이에서 아래로 레이저를 쏴서 땅에 닿으면 true
    }

    // 점프 시도
    private void TryJump()
    {
        if(Input.GetKeyDown(KeyCode.Space) && isGround) // 스페이스 키를 누르고 땅에 붙어있는 경우
        {
            Jump();
        }
    }

    // 점프 동작
    private void Jump()
    {
        if (isCrouch) // 앉기상태에서 점프할 경우 앉기상태 해제
            Crouch();
        myRigid.velocity = transform.up * jumpForce;
    }

    // 달리기 시도
    private void TryRun()
    {
        if(Input.GetKey(KeyCode.LeftShift)) // 왼쪽 쉬프트를 누를때
        {
            Running(); // 달리기
        }
        if(Input.GetKeyUp(KeyCode.LeftShift)) // 왼쪽 쉬프트를 안누를때
        {
            RunningCancle(); // 달리기 취소
        }
    }

    // 달리기 동작
    private void Running()
    {
        if (isCrouch) // 앉은상태에서 달리기 누르면 일어서서 달림
            Crouch();
        isRun = true; 
        applySpeed = runSpeed; // 달리기 속도 적용
    }

    private void RunningCancle()
    {
        isRun = false;
        applySpeed = walkSpeed; // 걷기 속도 적용
    }

    // 움직임 실행
    private void Move()
    {
        float _moveDirX = Input.GetAxisRaw("Horizontal"); // 좌우 키
        float _moveDirZ = Input.GetAxisRaw("Vertical"); // 상하 키

        Vector3 _moveHorizontal = transform.right * _moveDirX; // 좌우 좌표
        Vector3 _moveVertical = transform.forward * _moveDirZ; // 상하 좌표

        Vector3 _velocity = (_moveHorizontal + _moveVertical).normalized * applySpeed; // 속도

        myRigid.MovePosition(transform.position + _velocity * Time.deltaTime); // 속도에 따른 위치이동
    }

    // 좌우 캐릭터 회전
    private void CharacterRotation() 
    {
        float _yRotation = Input.GetAxisRaw("Mouse X"); // 마우스는 좌우가 X축이고 캐릭터는 좌우가 Y축이다.
        Vector3 _characterRotationY = new Vector3(0f, _yRotation, 0f)*lookSensitivity;
        myRigid.MoveRotation(myRigid.rotation * Quaternion.Euler(_characterRotationY));
    }

    // 상하 카메라 회전
    private void CameraRotation() 
    {
        float _xRotation = Input.GetAxisRaw("Mouse Y"); // 마우스는 상하가 Y축이고 카메라는 상하가 X축이다.
        float _cameraRotationX = _xRotation * lookSensitivity; // 상하 민감도
        currentCameraRotationX -= _cameraRotationX; // 상하각도 값
        currentCameraRotationX = Mathf.Clamp(currentCameraRotationX, -cameraRotationLimit, cameraRotationLimit); // 각도 제한
     
        theCamera.transform.localEulerAngles = new Vector3(currentCameraRotationX, 0f, 0f); // 상하각도 업데이트
    }
}

'유니티 개발' 카테고리의 다른 글

3D 캐릭터 팔, 주먹 동작 구현  (0) 2021.08.17
지형제작 (나무, 잔디, 돌)  (0) 2021.08.17
3D 캐릭터 움직임 기초  (0) 2021.08.16
2D 상하좌우 이동  (0) 2021.08.16

댓글