실제 게임을 개발할 때 exe, apk 파일로 뽑아내는 빌드 작업을 하면 우리가 사용하지 않는 파일은 제외되기 때문에 Project View 를 일일이 정리할 필요는 없다. 다만, 현재와 같이 겹치는 클래스 이름이 있거나 유니티 프로젝트의 로딩이 너무 느리다면 정리를 하는 것이 좋다.
01 게임월드 구성과 플레이어 캐릭터
게임에 필요한 에셋과 파일들을 불러오고 위와 같이 Player 오브젝트도 추가하여 게임 환경을 구상할 수 있다.
이후 카메라 제어를 위해 스크립트 코드를 다음과 같이 작성해줄 수 있다.
📌 CamerController.cs 스크립트
using UnityEngine; public class CameraController : MonoBehaviour { [SerializeField] private Transform target; [SerializeField] private float minDistance = 3; [SerializeField] private float maxDistance = 30; [SerializeField] private float wheelSpeed = 500; [SerializeField] private float xMoveSpeed = 500; [SerializeField] private float yMoveSpeed = 250; private float yMinLimit = 5; private float yMaxLimit = 80; private float x, y; private float distance; private void Awake() { // 최초 설정된 target과 카메라의 위치를 바탕으로 distance 값 초기화 distance = Vector3.Distance(transform.position, target.position); // 최초 카메라의 회전 값을 x, y 변수에 저장 Vector3 angles = transform.eulerAngles; x = angles.y; y = angles.x; } private void Update() { // target이 존재하지 않으면 실행 하지 않는다 if (target == null) return; // 마우스를 x, y축 움직임 방향 정보 x += Input.GetAxis("Mouse X") * xMoveSpeed * Time.deltaTime; y -= Input.GetAxis("Mouse Y") * yMoveSpeed * Time.deltaTime; // 오브젝트의 위/아래(x축) 한계 범위 설정 y = ClampAngle(y, yMinLimit, yMaxLimit); // 카메라의 회전(Rotation) 정보 갱신 transform.rotation = Quaternion.Euler(y, x, 0); // 마우스 휠 스크롤을 이용해 target과 카메라의 거리 값(distance) 조절 distance -= Input.GetAxis("Mouse ScrollWheel") * wheelSpeed * Time.deltaTime; // 거리는 최소, 최대 거리를 설정해서 그 값을 벗어나지 않도록 한다 distance = Mathf.Clamp(distance, minDistance, maxDistance); } private void LateUpdate() { // target이 존재하지 않으면 실행 하지 않는다 if (target == null) return; // 카메라의 위치(Position) 정보 갱신 // target의 위치를 기준으로 distacne만큼 떨어져서 쫓아간다 transform.position = transform.rotation * new Vector3(0, 0, -distance) + target.position; } private float ClampAngle(float angle, float min, float max) { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; return Mathf.Clamp(angle, min, max); } }
작성한 스크립트 코드를 메인 카메라에 넣어주고 플레이어 오브젝트의 자식 오브젝트로 빈 'CameraTarget'을 하나 설정해주고 메인 카메라에 넣어주었던 CameraController 스크립트 코드 부분의 Target으로 넣어줄 수 있다.
02 플레이어 캐릭터 이동
이동 제어에 사용되는 Movement3D.cs 스크립트 파일을 하나 생성해서 아래와 같이 스크립트 코드를 작성해보자.
📌 Movement3D.cs 스크립트 코드
using UnityEngine; public class Movement3D : MonoBehaviour { [SerializeField] private float moveSpeed = 5; // 이동 속도 private Vector3 moveDirection; // 이동 방향 private CharacterController characterController; public float MoveSpeed { // 이동속도는 2~5 사이의 값만 설정 가능 set => moveSpeed = Mathf.Clamp(value, 2.0f, 5.0f); } private void Awake() { characterController = GetComponent<CharacterController>(); } private void Update() { // 이동 설정. CharacterController의 Move() 함수를 이용한 이동 characterController.Move(moveDirection * moveSpeed * Time.deltaTime); } public void MoveTo(Vector3 direction) { moveDirection = new Vector3(direction.x, moveDirection.y, direction.z); } }
이후, 플레이어를 제어할 수 있는 PlayerController.cs 코드를 작성해보자.
📌 PlayerController.cs
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private Transform cameraTransform; private Movement3D movement3D; private void Awake() { Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; movement3D = GetComponent<Movement3D>(); } private void Update() { // 방향키를 눌러 이동 float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); // 이동 속도 설정 (앞으로 이동할때만 5, 나머지는 2) movement3D.MoveSpeed = z > 0 ? 5.0f : 2.0f; // 이동 함수 호출 (카메라가 보고있는 방향을 기준으로 방향키에 따라 이동) movement3D.MoveTo(cameraTransform.rotation * new Vector3(x, 0, z)); } }
이렇게 작성한 Movement3D.cs, PlayerController.cs에 해당하는 코드를 플레이어 오브젝트에 넣어준다.
위와 같이 CharacterController까지 추가해줄 수 있다.
플레이어 오브젝트에 대해 각각 설정을 위와 같이 마친 후, 게임을 실행하면 카메라가 보는 방향을 기준으로 방향키 방향대로 캐릭터가 이동하는 것을 확인할 수 있다.
📌 PlayerController.cs 코드 수정
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private Transform cameraTransform; private Movement3D movement3D; private void Awake() { Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; movement3D = GetComponent<Movement3D>(); } private void Update() { // 방향키를 눌러 이동 float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); // 이동 속도 설정 (앞으로 이동할때만 5, 나머지는 2) movement3D.MoveSpeed = z > 0 ? 5.0f : 2.0f; // 이동 함수 호출 (카메라가 보고있는 방향을 기준으로 방향키에 따라 이동) movement3D.MoveTo(cameraTransform.rotation * new Vector3(x, 0, z)); // 회전 설정 (항상 앞만 보도록 캐릭터의 회전은 카메라와 같은 회전 값으로 설정) transform.rotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y, 0); } }
플레이어 캐릭터가 카메라가 바라보는 전방방향으로 계속 바라볼 수 있도록 PlayerController.cs 코드를 위와 같이 수정해보자.
코드 수정 후, 게임을 실행하면 플레이어 캐릭터의 Y축 회전값과 카메라의 Y축 회전값이 같기 때문에 항상 캐릭터의 뒷모습만 보게 된다.
03 플레이어 캐릭터 물리와 점프
플레이어 물리와 점프를 위해 Movement3D.cs 코드를 수정해보자.
📌 Movement3D.cs 코드 수정
using UnityEngine; public class Movement3D : MonoBehaviour { [SerializeField] private float moveSpeed = 5; // 이동 속도 [SerializeField] private float gravity = -9.81f; // 중력 계수 [SerializeField] private float jumpForce = 3.0f; // 점프 힘 private Vector3 moveDirection; private CharacterController characterController; public float MoveSpeed { set => moveSpeed = Mathf.Clamp(value, 2.0f, 5.0f); } private void Awake() { characterController = GetComponent<CharacterController>(); } private void Update() { // 중력 설정, 플레이어가 땅을 밟고 있지 않다면 // y축 이동방향에 gravity * Time.deltaTime을 더해준다. if (characterController.isGrounded == false) { moveDirection.y += gravity * Time.deltaTime; } // 이동 설정. CharacterController의 Move() 함수를 이용한 이동 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 코드도 아래와 같이 수정해줄 수 있다.
📌 수정된 PlayerController.cs 코드
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private KeyCode jumpKeyCode = KeyCode.Space; [SerializeField] private Transform cameraTransform; private Movement3D movement3D; private void Awake() { Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; movement3D = GetComponent<Movement3D>(); } private void Update() { // 방향키를 눌러 이동 float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); // 이동 속도 설정 (앞으로 이동할때만 5, 나머지는 2) movement3D.MoveSpeed = z > 0 ? 5.0f : 2.0f; // 이동 함수 호출 (카메라가 보고있는 방향을 기준으로 방향키에 따라 이동) movement3D.MoveTo(cameraTransform.rotation * new Vector3(x, 0, z)); // 회전 설정 (항상 앞만 보도록 캐릭터의 회전은 카메라와 같은 회전 값으로 설정) transform.rotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y, 0); if (Input.GetKeyDown(jumpKeyCode)) { movement3D.JumpTo(); } } }
이후 게임을 실행해보면 아래와 같이 플레이어가 움직이고 점프하는 것을 확인할 수 있다.
04 플레이어 캐릭터 대기/이동, 점프 애니메이션
무기가 플레이어의 오른쪽 손에 있어야 하므로 오른쪽 손 부분에 해당하는 오브젝트의 자식 오브젝트로 무기를 넣어준다. 이후 위와 같이 무기의 Transform 설정을 해줄 수 있다.
이후 에니메이터 컨트롤러를 하나 만들고 'Toko_sum' 오브젝트의 에니메이터 Controller 부분에 Player을 적용할 수 있다. 그리고 apply root motion 체크를 해제해준다.
Animator 뷰로 넘어가서 Blend Tree를 하나 생성해주고, 이름을 Movement로 변경한다.
그리고 위와 같이 BlendTree에 대한 설정을 진행해줄 수 있다.
BaseLayer로 돌아와서 점프 애니메이션을 삽입하고 Movement와 Transition을 형성해준다. 이후, Trigger 형태의 onJump를 만들어줄 수 있다.
Movement에서 2Hand-Sword-Jump로의 상태전이를 클릭해주고 Has Exit Time을 해제해준다. Conditions 부분에도 하나를 추가하고 이를 onJump로 변경해주어야 한다.
각 에니메이션에 대해 기존에 설정된 애니메이션으로 인해 이후 설정한 애니메이션이 원하는 대로 작동하지 않을 수 있으니 이를 제거해주는 과정이 필요하다.
다음으로 애니메이션 관리와 제어를 위해 PlayerAnimator.cs 코드를 작성해보자.
📌 PlayerAnimator.cs
using UnityEngine; public class PlayerAnimaor : MonoBehaviour { [SerializeField] private GameObject attackCollision; private Animator animator; private void Awake() { animator = GetComponent<Animator>(); } public void OnMovement(float horizontal, float vertical) { animator.SetFloat("horizontal", horizontal); animator.SetFloat("vertical", vertical); } public void OnJump() { animator.SetTrigger("onjump"); } }
코드 작성 이후, 대기/이동/점프에 맞춰 애니메이션을 재생하기 위해 PlayerController 스크립트 코드의 수정이 필요하다.
📌 PlayerController.cs 스크립트 수정 및 암기방법
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private KeyCode jumpKeyCode = KeyCode.Space; [SerializeField] private Transform cameraTransform; private Movement3D movement3D; private PlayerAnimaor playerAnimaor; private void Awake() { Cursor.visible = false; Cursor.lockState = CursorLockMode.Locked; movement3D = GetComponent<Movement3D>(); playerAnimaor = GetComponentInChildren<PlayerAnimaor>(); } private void Update() { // 방향키를 눌러 이동 float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); // 애니메이션 파라미터 설정 (horizontal, verticla) playerAnimaor.OnMovement(x, z); // 이동 속도 설정 (앞으로 이동할때만 5, 나머지는 2) movement3D.MoveSpeed = z > 0 ? 5.0f : 2.0f; // 이동 함수 호출 (카메라가 보고있는 방향을 기준으로 방향키에 따라 이동) movement3D.MoveTo(cameraTransform.rotation * new Vector3(x, 0, z)); // 회전 설정 (항상 앞만 보도록 캐릭터의 회전은 카메라와 같은 회전 값으로 설정) transform.rotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y, 0); if (Input.GetKeyDown(jumpKeyCode)) { playerAnimaor.OnJump(); movement3D.JumpTo(); } } }
작성한 코드를 바탕으로 게임을 실행하면, 대기, 이동, 점프를 했을 때 각각의 애니메이션이 재생되는 것을 확인할 수 있다.
05 플레이어 캐릭터 공격 애니메이션
우선 본 실습에서 진행되는 발차기 애니메이션의 경우 동일한 에셋과 파일을 임포트했지만 업데이트 등의 사유로인지 해당 애니메이션 기능이 없어 우선 이는 잠시 넘어가고 다음으로 무기를 이용한 애니메이션 설정을 진행해보고자 한다.
BaseLayer에 대해 해당 애니메이션도 마찬가지로 다음과 같이 설정해줄 수 있다.
OnWeaponAttack을 제어하는 부분을 추가하기 위해 PlayerAnimator.cs, PlayerController 각각의 코드를 수정해주었다.
📌 PlayerAnimator.cs 코드 수정
using UnityEngine; public class PlayerAnimaor : MonoBehaviour { [SerializeField] private GameObject attackCollision; private Animator animator; private void Awake() { animator = GetComponent<Animator>(); } public void OnMovement(float horizontal, float vertical) { animator.SetFloat("horizontal", horizontal); animator.SetFloat("vertical", vertical); } public void OnJump() { animator.SetTrigger("onjump"); } public void OnAttack() { animator.SetTrigger("onattack"); } public void OnWeaponAttack() { animator.SetTrigger("OnWeaponAttack"); } public void OnAttackCollision() { attackCollision.SetActive(true); } }
📌 PlayerController.cs 코드 수정
using UnityEngine; public class PlayerController : MonoBehaviour { [SerializeField] private KeyCode jumpKeyCode = KeyCode.Space; [SerializeField] private Transform cameraTransform; private Movement3D movement3D; private PlayerAnimaor playerAnimaor; private void Awake() { Cursor.visible = false; // 마우스 커서를 보이지 않게 Cursor.lockState = CursorLockMode.Locked; // 마우스 커서 위치 고정 movement3D = GetComponent<Movement3D>(); playerAnimaor = GetComponentInChildren<PlayerAnimaor>(); } private void Update() { // 방향키를 눌러 이동 float x = Input.GetAxis("Horizontal"); float z = Input.GetAxis("Vertical"); // 애니메이션 파라미터 설정 (horizontal, verticla) playerAnimaor.OnMovement(x,z); // 이동 속도 설정 (앞으로 이동할때만 5, 나머지는 2) movement3D.MoveSpeed = z > 0? 5.0f: 2.0f; // 이동 함수 호출 (카메라가 보고있는 방향을 기준으로 방향키에 따라 이동) movement3D.MoveTo(cameraTransform.rotation * new Vector3(x, 0, z)); // 회전 설정 (항상 앞만 보도록 캐릭터의 회전은 카메라와 같은 회전 값으로 설정) transform.rotation = Quaternion.Euler(0, cameraTransform.eulerAngles.y,0); if (Input.GetKeyDown(jumpKeyCode)) { playerAnimaor.OnJump(); movement3D.JumpTo(); } // 마우스 좌클릭 어택 if (Input.GetMouseButtonDown(0)) { playerAnimaor.OnAttack(); } // 우클릭 연계공격 if (Input.GetMouseButtonDown(1)) { playerAnimaor.OnWeaponAttack(); } } }
'Study > Unity' 카테고리의 다른 글
[Unity 3D Basic] Terrain Map (0) | 2024.06.05 |
---|---|
[Unity 3D Basic] Animation Layer, Blend Tree 실습 (0) | 2024.05.22 |
[Unity 3D Basic] Animation Layer, Blend Tree (0) | 2024.05.22 |
[Unity 3D Basic] 3D Model / Animations (0) | 2024.05.15 |
[Unity 3D Basic] Navigation Mesh 응용 (0) | 2024.05.15 |