-
[Unity 3D URP] 캐릭터 컨트롤러를 알아보자 2Unity 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