01 CharacterController 컴포넌트
- 3차원 게임에서 사람 형태의 캐릭터 움직임과 관련된 제어를 위해 사용
- 캡슐 (Capsule) 형태의 충돌 범위가 포함되어 있다
- Slope Limit : 올라갈 수 있는 경사 한계 각
- Step Offset : 설정 값보다 낮은 높이의 계단(그 외 오브젝트) 을 오를 수 있다
- Center : Capsule 충돌 범위의 중심점
- Radius : Capsule 충돌 범위의 반지름 (x, z)
- Height : Capsule 충돌 범위의 높이 (y)
02 게임 월드 구성, 플레이어 캐릭터 제작
게임 월드 구성의 경우, 강사님께서 미리 만들어주신 유니티 패키지 파일을 다운로드 받고 이를 프로젝트 뷰로 이동하여 추가하는 방식으로 사용하였다.
이후, 프리팹 폴더에 있는 게임 월드를 Hierarchy View로 드래그 해서 생성해주면 다음과 같은 화면을 볼 수 있다.
게임 월드를 한눈에 볼 수 있도록 Main Camera 게임 오브젝트의 위치를 다음과 같이 설정해준다. 그럼 아래와 같이 게임 월드 전체를 확인할 수 있다.
다음으로 플레이어 역할을 할 오브젝트를 생성해보자.
생성한 플레이어 오브젝트에 대해 색상을 변경하려 하는데 3D 오브젝트의 경우 material assets을 만들어 material에 색상과 이미지를 설정하고 이를 오브젝트에 등록해야 한다.
따라서, material 이미지를 만들어주고 이름을 Player로 설정한 후, 다음과 같이 색상을 지정해줄 수 있다.
다음으로 플레이어 오브젝트의 Mesh Renderer 부분에 방금 설정한 material을 추가해주면 플레이어 오브젝트가 빨간색으로 변한 것을 확인할 수 있다.
플레이어 오브젝트 중 Character Controller는 추가하고 Capsule Collider는 제거하여야 한다.
이후 확인해보면 다음과 같이 캡슐 모양의 충돌 범위가 생성된 것을 확인할 수 있다.
03 플레이어 캐릭터 이동
이동을 제어하는 스크립트를 생성하고 작성해줄 수 있다.
[ Movement3D.cs ]
다음으로 플레이어를 제어하는 스크립트를 생성하고 작성해줄 수 있다.
[ PlayerController.cs ]
위와 같이 작성한 코드를 저장하고 게임을 실행시켜보면, 아래 영상과 같이 우리가 방향키로 움직이는 대로, 오브젝트가 움직이는 것을 볼 수 있다.
04 중력 적용
플레이어의 높이를 위로 올리고 확인해보면 오브젝트가 떨어지지 않는 것을 확인할 수 있는데, 이는 Character Controller 컴포넌트가 충돌 처리는 가능하지만, 중력은 적용되지 않고 있다는 것을 알 수 있다. 따라서, 중력은 코드로 직접 입력해주어야 한다.
플레이어 캐릭터의 중력 설정을 위해 Movement3D 스크립트 수정
📌 Movement3D.cs 수정 코드
using UnityEngine; public class Movement3D : MonoBehaviour { [SerializeField] private float moveSpeed = 5.0f; // 이동 속도 private float gravity = -9.81f; // 중력 계수 private Vector3 moveDirection; // 이동 방향 private CharacterController characterController; private void Awake() { characterController = GetComponent<CharacterController>(); } private void Update() { if (characterController.isGrounded == false) { moveDirection.y += gravity * Time.deltaTime; } characterController.Move(moveDirection * moveSpeed * Time.deltaTime); } public void MoveTo(Vector3 direction) { moveDirection = new Vector3(direction.x, moveDirection.y, direction.z); } }
▶ 수정된 코드를 바탕으로 게임을 실행하면 게임 오브젝트를 아무리 위로 올려도 게임을 실행하면 다시 중력에 의해 밑으로 떨어지는 것을 확인할 수 있다.
05 플레이어 캐릭터 점프
다음은 점프를 구현하기 위해 Movement3D 스크립트를 수정해보자.
📌 Movement3D 코드 수정
using UnityEngine; public class Movement3D : MonoBehaviour { [SerializeField] private float moveSpeed = 5.0f; // 이동 속도 [SerializeField] private float jumpForce = 3.0f; // 뛰는 힘 private float gravity = -9.81f; // 중력 계수 private Vector3 moveDirection; // 이동 방향 private CharacterController characterController; private void Awake() { characterController = GetComponent<CharacterController>(); } private void Update() { if (characterController.isGrounded == false) { moveDirection.y += gravity * Time.deltaTime; } characterController.Move(moveDirection * moveSpeed * Time.deltaTime); } public void MoveTo(Vector3 direction) { moveDirection = new Vector3(direction.x, moveDirection.y, direction.z); } public void JumpTo() { if (characterController.isGrounded == true) { moveDirection.y = jumpForce; } } }
플레이어 캐릭터가 점프할 수 있도록 아래와 같이 코드를 수정해줄 수 있다.
📌 PlayerController.cs 코드 수정
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private KeyCode jumpKeyCode = KeyCode.Space; private Movement3D movement3D; private void Awake() { movement3D = GetComponent<Movement3D>(); } private void Update() { float x = Input.GetAxisRaw("Horizontal"); // 방향키 좌/우 움직임 float z = Input.GetAxisRaw("Vertical"); // 방향키 위/아래 움직임 movement3D.MoveTo(new Vector3(x, 0, z)); if (Input.GetKeyDown(jumpKeyCode)) { movement3D.JumpTo(); } } }
수정한 코드를 바탕으로 게임을 실행해보면, 아래 영상과 같이 게임 오브젝트가 자유롭게 뛰고 이동하는 것을 확인할 수 있다.
06 경사면 이동과 계단 이동
경사면 이동의 경우 Character Controller에 Slope Limit이라는 변수가 경사 한계 각도임을 알 수 있다.
현재는 45로 설정이 되어있기 때문에 45도까지의 경사만 올라갈 수 있는 것이다. 따라서, 경사각이 45도 보다 높을 경우 올라갈 수 없음을 확인할 수 있다
계단 이동의 경우 Character Controller 컴포넌트의 Step Offset 변수와 연관이 있다.
서로 인접한 두 계단 사이의 높이가 step offset 이하일 때, 계단을 오를 수 있는 것이다.
07 1인칭 카메라 제어
카메라가 플레이어를 쫓아다니도록 설정을 해보자. 우선 플레이어를 쫓아다녀야 하므로 플레이어 오브젝트의 자식 오브젝트로 넣어주어야 한다.
이후 카메라의 transform 정보를 reset으로 초기화하고, y축 위치만 0.6 정도로 변경해준다. 변경한 내용을 바탕으로 Scene을 확인해보면, 다음과 같이 플레이어 오브젝트의 눈 위치 즉, 위쪽에서 있는 것을 확인할 수 있다.
다음으로 카메라 회전 설정을 위해 CameraController.cs 스크립트를 작성해보자.
📌 CameraController.cs 코드 작성
using UnityEngine; public class CameraController : MonoBehaviour { private float rotateSpeedX = 3; private float rotateSpeedY = 5; private float limitMinX = -80; private float limitMaxX = 50; private float eulerAngleX; private float eulerAngleY; public void RotateTo(float mouseX, float mouseY) { // 마우스를 좌/우로 움직이는 mouseX 값을 y축에 대입하는 이유는 // 마우스를 좌/우로 움직일 때 카메라도 좌/우를 보려면 카메라 오브젝트의 // y축이 회전되어야 하기 때문 eulerAngleY += mouseX * rotateSpeedX; // 같은 개념으로 카메라가 위/아래를 보려면 카메라 오브젝트의 x축이 회전 eulerAngleX -= mouseY * rotateSpeedY; // x축 회전 값의 경우 아래, 위를 볼 수 있는 제한 각도가 설정되어 있다 eulerAngleX = ClampAngle(eulerAngleX, limitMinX, limitMaxX); // 실제 오브젝트의 쿼터니온 회전에 적용 transform.rotation = Quaternion.Euler(eulerAngleX, eulerAngleY, 0); } private float ClampAngle(float angle, float min, float max) { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; // Mathf.Clamp()를 이용해 angle이 min <= angle <= max을 유지하도록 함 return Mathf.Clamp(angle, min, max); } }
PlayerController.cs 코드 또한, 마우스를 움직였을 때 카메라에 값을 전달할 수 있도록 코드를 작성할 수 있다.
📌 PlayerController.cs 코드 수정
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private KeyCode jumpKeyCode = KeyCode.Space; [SerializeField] private CameraController cameraController; private Movement3D movement3D; private void Awake() { movement3D = GetComponent<Movement3D>(); } private void Update() { float x = Input.GetAxisRaw("Horizontal"); // 방향키 좌/우 움직임 float z = Input.GetAxisRaw("Vertical"); // 방향키 위/아래 움직임 movement3D.MoveTo(new Vector3(x, 0, z)); if (Input.GetKeyDown(jumpKeyCode)) { movement3D.JumpTo(); } float mouseX = Input.GetAxis("Mouse X"); // 마우스 좌/우 움직임 float mouseY = Input.GetAxis("Mouse Y"); // 마우스 위/아래 움직임 cameraController.RotateTo(mouseX, mouseY); } }
다음으로 방향키를 눌러 이동할 때, 카메라가 보고있는 전방 방향을 기준으로 이동할 수 있도록 아래와 같이 Movement3D.cs 코드를 수정해줄 수 있다.
📌 Movement3D.cs 코드 수정
using UnityEngine; public class Movement3D : MonoBehaviour { [SerializeField] private float moveSpeed = 5.0f; // 이동 속도 [SerializeField] private float jumpForce = 3.0f; // 뛰는 힘 private float gravity = -9.81f; // 중력 계수 private Vector3 moveDirection; // 이동 방향 [SerializeField] private Transform cameraTransform; private CharacterController characterController; private void Awake() { characterController = GetComponent<CharacterController>(); } private void Update() { if (characterController.isGrounded == false) { moveDirection.y += gravity * Time.deltaTime; } characterController.Move(moveDirection * moveSpeed * Time.deltaTime); } public void MoveTo(Vector3 direction) { Vector3 movedis = cameraTransform.rotation * direction; moveDirection = new Vector3(direction.x, moveDirection.y, direction.z); } public void JumpTo() { if (characterController.isGrounded == true) { moveDirection.y = jumpForce; } } }
이렇게 수정한 코드들을 실행시키기 위해 유니티로 이동하여 main camera 오브젝트에 CamerController 컴포넌트를 적용시켜 주고, 플레이어를 보게 되면 각각 다음과 같이 main camera 오브젝트를 넣을 수 있는 카메라 컨트롤러와 트랜스폼이 생성되어 해당 부분에 main camera 오브젝트를 넣어주었다.
모든 설정이 완료되었다면, 게임을 실행시켜보자.
그럼 아래 영상과 같이 마우스의 움직임에 따라 카메라가 돌아가고 케릭터가 움직이는 것을 확인할 수 있다.
출처 : 따라하면서 배우는 고박사의 유니티 기초(https://inf.run/sgcy)
'Study > Unity' 카테고리의 다른 글
[Unity 3D Basic] Navigation Mesh 응용 (0) | 2024.05.15 |
---|---|
[Unity 3D Basic] Navigation Mesh 기초 (0) | 2024.05.15 |
[Unity 2D Basic] 2D Tilemap - Extras (0) | 2024.05.08 |
[Unity 2D Basic] 2D Tilemap Editor (0) | 2024.05.08 |
[Unity 2D Basic] 2D Animation 실습 (0) | 2024.05.01 |