ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity 3D URP] 캐릭터 컨트롤러를 알아보자 2
    Unity 3D 2023. 3. 27. 20:17

    ★ 1인칭 캐릭터 만들기

      시야 이동 구현 (1인칭) 

      캐릭터 이동 구현 → 높은 곳에서 떨어질 때의 중력까지 구현하기

      점프 구현 ( rigidbody 기능 구현 )

     

     

     

     

    w,s,a,d 키를 이용해서 캐릭터가 움직이는 걸 구현해보자.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
        
        // 이동속도
        [SerializeField] float moveSpeed = 5;
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
        	if(Input.GetKey(KeyCode.A))
            { 
            	transform.Translate(Vector3.left * moveSpeed * Time.deltaTime);
            }
            if(Input.GetKey(KeyCode.D))
            { 
            	transform.Translate(Vector3.right * moveSpeed * Time.deltaTime);
            }
            if(Input.GetKey(KeyCode.W))
            { 
            	transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
            }
            if(Input.GetKey(KeyCode.S))
            { 
            	transform.Translate(Vector3.back * moveSpeed * Time.deltaTime);
            }
        }
    }

    이게 내가 처음에 짰던 코드다.

    Space.World (월드좌표)를 넣으면 움직임이 이상해진다.. Player가 능동적으로 움직일 수 있도록

    그냥 뺐더니 (로컬좌표) 원하는대로 움직임이 구현됐다.

     

    이 코드로 물론 움직이긴 하지만 화살표 키 값까지 넣으려면 코드가 너무 복잡해진다.

     

     

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
    
        // 이동하기
        [SerializeField] float moveSpeed = 5;
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
            // 이동 구현
            float moveX = Input.GetAxis("Horizontal");
            float moveY = Input.GetAxis("Vertical");
    
            Vector3 move = transform.right * moveX + transform.forward * moveY;
            controller.Move(move * Time.deltaTime * moveSpeed);
        }

    Input.GetAxis("Horizontal"); 를 변수 moveX, Input.GetAxis("Vertical"); 를 변수 moveY에 저장한다.

    ( Horizontal , Vertical 키를 사용하면 a,s,d,w 키 말고도 화살표 키도 이동 구현 가능함 )

    그리고 움직이는 값을 변수들로부터 받고 controller.move 로 컨트롤러를 이용해 이동한다.

     

    이때 Vector3 move = transform.right * moveX + transform.forward * moveY; 는

    Vector3 move = new Vector3(moveX, 0f, moveY);  로도 대체 가능하다.

     

    아주 부~드럽게 잘 움직인다.

     

    한 가지 아쉬운 점. . ..  높은 건물로 올라가 이 플레이어가 떨어지면 그대로 떨어지는 게 아니고

    공중에 그 자리 그대로 멈춰있다. 중력이 구현되어있지 않기 때문이다.

     

    중력값을 넣어보겠다.

    중력값을 줬을 때 떨어지는 높이값 h를 구하는 공식

    h (높이) = ½ * gravity * t ²  

    h (높이) = 0.5f * gravity * ( t * t )

     gravity = 중력 이다. 중력은 기본적으로 9.81f 의 수치를 디폴트로 가지고 있다.

    여기서 t 는 떨어지는 시간이다.

     

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
    
        // 이동하기
        [SerializeField] float moveSpeed = 5;
    
        // 중력
        private float gravity = -9.81f;
        private Vector3 fall; // 기본 값은 (0,0,0)이다
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
            // 이동 구현
            float moveX = Input.GetAxis("Horizontal");
            float moveY = Input.GetAxis("Vertical");
    
            Vector3 move = transform.right * moveX + transform.forward * moveY;
            controller.Move(move * Time.deltaTime * moveSpeed);
            
            // 중력 구현
            fall.y += 0.5f * gravity * Time.deltaTime;
            controller.Move(fall * Time.deltaTime);  
            // -9.8을 곱했기 때문에 마이너스 좌표로 (아래로)떨어진다
        }
    }

    중력 구현은 곧 떨어지는 것을 구현하는 것이니까 y좌표만 움직인다.

    Vector3 fall 변수의 기본값은 Vector3의 기본 값인 (0,0,0) 이다. 

    벡터 기본 값을 가진 fall 의 떨어질 값을 += 0.5f * gravity * Time.deltaTime; 으로 구하고

    controller에 적용시켜준다.

    중력이 애초에 -9.81f 로, 마이너스기 때문에 y좌표대로 밑으로 떨어지게 된다.

     

    위에서 떨어져보니 잘 떨어진다.

     

     

     

    ★ 1인칭 캐릭터 만들기

      시야 이동 구현 (1인칭) 

      캐릭터 이동 구현 

      점프 구현 ( rigidbody 기능 구현 ) → Ground 콜라이더 체크 해서 땅에서만 점프 구현

     

     

     

    중력도 넣었으니 짬푸를 구현해보자.

    점프는 스페이스키를 눌렀을 때 실행되도록 할 것이다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
    
        // 이동하기
        [SerializeField] float moveSpeed = 5;
    
        // 중력
        private float gravity = -9.81f;
        private Vector3 fall; // 기본 값은 (0,0,0)이다
    
        // 점프할 높이
        [SerializeField] float jumpHeight = 10f;
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
            // 이동 구현
            float moveX = Input.GetAxis("Horizontal");
            float moveY = Input.GetAxis("Vertical");
    
            Vector3 move = transform.right * moveX + transform.forward * moveY;
            controller.Move(move * Time.deltaTime * moveSpeed);
    
            // 점프 구현
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
    
            // 중력 구현
            fall.y += 0.5f * gravity * Time.deltaTime;
            controller.Move(fall * Time.deltaTime);  
        }
        void Jump()
        {
            fall.y = Mathf.Sqrt(-2f * gravity * jumpHeight);
        }
    }

    jump 메서드를 따로 작성했다.

    점프할 높이는 10f 로 지정해놓고 플레이 하면서 조정할 수 있도록 인스펙터 창으로 뺀다.

    중력의 힘을 거스르고 +y 좌표(위 방향)로 올라가야 하기 때문에 jumpHeight 만큼 (점프 구현할 높이만큼) 곱해준다.

     

    일단 점프력을 구하는 공식이 Mathf.Sqrt(-2f * gravity * jumpHeight); 라고 한다...

    여기서 -2 는 중력이 -9.81f 값으로 들어갔기 때문에 곱했을 때 + 가 되어 위로 올라가기 위해서 마이너스를 넣은 것이다.

    Mathf.Sqrt 함수 관련은 이 글을 참고하면 된다...

    Mathf.Sqrt( ) 함수 :: 내가 보려고 정리하는 공부 기록장 (tistory.com)

     

    근데 솔직히 왜 이 공식을 써야 하는지 아직 모르겠음... 

    너무 수학적이라 내 머리로는 이해가 안 됩니다 ,,.

    왜 2가 들어가는지 아시는 분?... 나중에 알게 되면 글에 추가해야지.

    일단 저 -2 에 -0.5f 를 넣어도 구현은 잘 되던데...

     

    점프했다가 내려왔을 때 좀 더 자연스럽게 착지하고 싶으면 중력값을 아래로 더 (무겁게) 낮춰주면 된다.

    gravity 를 -30 값을 줬더니 자연스러워졌다!

     

     

    그라운드 체크를 해서 땅에 있을 때만 점프 키가 먹힐 수 있게 해보자.

    (공중에서 점프하는 건 좀 아니지 않냐구)

     

     

     

     

    빈 오브젝트 하나 생성해서 플레이어 발 밑에 위치한다.

    (땅을 체크해줄 오브젝트)

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
    
        // 이동하기
        [SerializeField] float moveSpeed = 5;
    
        // 중력
        private float gravity = -30f;
        private Vector3 fall; // 기본 값은 (0,0,0)이다
    
        // 점프할 높이
        [SerializeField] float jumpHeight = 10f;
    
        // 그라운드 체크
        public Transform groundCheck;
        [SerializeField] private float checkRange = 0.5f;
        [SerializeField] private LayerMask groundMask;
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
            // 그라운드 체크 구현 (땅에 있을 때만 짬푸 구현)
            bool isGround = IsGroundCheck();
            if(isGround == true && Input.GetKeyDown(KeyCode.Space))
            {
                // fall.y 가 마이너스 값을 계속 누적해서 받는 것을 방지
                fall.y = -3;
            }
    
            // 이동 구현
            float moveX = Input.GetAxis("Horizontal");
            float moveY = Input.GetAxis("Vertical");
    
            Vector3 move = transform.right * moveX + transform.forward * moveY;
            controller.Move(move * Time.deltaTime * moveSpeed);
    
            // 점프 구현
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
    
            // 중력 구현
            fall.y += 0.5f * gravity * Time.deltaTime;
            controller.Move(fall * Time.deltaTime);  
        }
    
        void Jump()
        {
            fall.y = Mathf.Sqrt(-2f * gravity * jumpHeight);
        }
    
        bool IsGroundCheck()
        {
            return Physics.CheckSphere(groundCheck.position, checkRange, groundMask);
        }
    }

     

    필드 선언부에서 선언하고 인스펙터 창에 해당 오브젝트를 드래그한다.

    플레이어가 땅에 있고 fall.y 가 0보다 작으면 임의로 fall.y = -3; 으로 초기화한다.

    점프시 계속 -값이 누적되기 때문이다.

    bool 형을 사용해서 땅에 위치해있으면 메서드를 실행한다.

    Physics.CheckSphere(레이어를 찾을 위치 , 반경 범위 , 찾을 레이어); 함수가 있다.

    반경 안에 있는 레이어를 전부 찾아주는 함수이다.

    그 전에 ground레이어를 추가해서 ground에 해당되는 곳에 레이어를 모두 설정한다.

    그리고 인스펙트 창에 있는 ground Mask에 해당 레이어를 할당한다.

    Range 는 0.5f로 설정한다. 점프한 후에 그라운드 체크 (플레이어 발)이 땅과 닿기 전 반경 0.5부터 속도가 1로 변하는 것이다.

     

     

     


    전체 코드

    더보기

    PlayMove.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerMove : MonoBehaviour
    {
    
        // 캐릭터 컨트롤러 가져오기
        private CharacterController controller;
    
        // 이동하기
        [SerializeField] float moveSpeed = 5;
    
        // 중력
        private float gravity = -30f;
        private Vector3 fall; // 기본 값은 (0,0,0)이다
    
        // 점프할 높이
        [SerializeField] float jumpHeight = 10f;
    
        // 그라운드 체크
        public Transform groundCheck;
        [SerializeField] private float checkRange = 0.5f;
        [SerializeField] private LayerMask groundMask;
    
        private void Start()
        {
           // 캐릭터 컨트롤러 컴포넌트 저장하기
           controller = GetComponent<CharacterController>();
        }
        void Update()
        {
            // 그라운드 체크 구현 (땅에 있을 때만 점프 구현)
            bool isGround = IsGroundCheck();
            if(isGround == true && Input.GetKeyDown(KeyCode.Space))
            {
                // fall.y 가 마이너스 값을 계속 누적해서 받는 것을 방지
                fall.y = -3;
            }
    
            // 이동 구현
            float moveX = Input.GetAxis("Horizontal");
            float moveY = Input.GetAxis("Vertical");
    
            Vector3 move = transform.right * moveX + transform.forward * moveY;
            controller.Move(move * Time.deltaTime * moveSpeed);
    
            // 점프 구현
            if (Input.GetKeyDown(KeyCode.Space))
            {
                Jump();
            }
    
            // 중력 구현
            fall.y += 0.5f * gravity * Time.deltaTime;
            controller.Move(fall * Time.deltaTime);  
        }
    
        void Jump()
        {
            fall.y = Mathf.Sqrt(-2f * gravity * jumpHeight);
        }
    
       // 그라운드 체크
        bool IsGroundCheck()
        {
            return Physics.CheckSphere(groundCheck.position, checkRange, groundMask);
        }
    }

     

     

    'Unity 3D' 카테고리의 다른 글

    [Unity 3D URP] 캐릭터 컨트롤러를 알아보자 1  (0) 2023.03.24
Designed by Tistory.