https://youtu.be/uandR5M30ho [유튜버: 케이디]님의 유니티 3D강좌 영상을 보고 따라하면서 유니티 공부용으로 제작한 게임입니다.
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 isRun = false;
private bool isCrouch = 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;
// Start is called before the first frame update
void Start()
{
capsuleCollider = GetComponent<CapsuleCollider>();
myRigid = GetComponent<Rigidbody>();
applySpeed = walkSpeed;
originPosY = theCamera.transform.localPosition.y;
applyCrouchPosY = originPosY;
}
// Update is called once per frame
void Update()
{
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)
{
count++;
_posY = Mathf.Lerp(_posY, applyCrouchPosY, 0.1f);
theCamera.transform.localPosition = new Vector3(0, _posY, 0);
if (count > 15)
break;
yield return null;
}
theCamera.transform.localPosition = new Vector3(0, applyCrouchPosY, 0f);
}
// 지면 닿아 있는지 확인
private void IsGround()
{
isGround = Physics.Raycast(transform.position, Vector3.down, capsuleCollider.bounds.extents.y + 0.1f);
}
//점프 시도
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))
{
RunningCancel();
}
}
//달리기 시도
private void Running()
{
//앉아 있다가 달리면 일어나기
if (isCrouch)
Crouch();
isRun = true;
applySpeed = runSpeed;
}
//달리기 취소
private void RunningCancel()
{
isRun = false;
applySpeed = walkSpeed;
}
//움직이기
private void Move()
{
//플레이어가 움직는 값을 설정하기
float _moveDirX = Input.GetAxisRaw("Horizontal");
float _moveDriZ = Input.GetAxisRaw("Vertical");
Vector3 _moveHorizontal = transform.right * _moveDirX;
Vector3 _moveVertical = transform.forward * _moveDriZ;
Vector3 _velocity = (_moveHorizontal + _moveVertical).normalized * applySpeed;
myRigid.MovePosition(transform.position + _velocity * Time.deltaTime);
}
//캐릭터를 좌우로 움직이기
private void CharacterRotation()
{
float _yRotation = Input.GetAxisRaw("Mouse X");
Vector3 _characterRotationY = new Vector3(0f, _yRotation, 0f) * lookSensitivity;
myRigid.MoveRotation(myRigid.rotation * Quaternion.Euler(_characterRotationY));
}
//카메라 상하로 움직이기
private void CameraRotation()
{
float _xRotation = Input.GetAxisRaw("Mouse Y");
float _cameraRotationX = _xRotation * lookSensitivity;
currentCameraRotationX -= _cameraRotationX;
currentCameraRotationX = Mathf.Clamp(currentCameraRotationX, -cameraRotationLimit, cameraRotationLimit);
theCamera.transform.localEulerAngles = new Vector3(currentCameraRotationX, 0f, 0f);
}
}
1편에서는 캐릭터가 방향키로 움직이는 방법을 구현하는 방법을 알아봤다.
2편에서는 점프도 하고 앉기도 하고 각 동작을 취할때 뚝 끊겨서 이동하지 않게 자연스럽게 카메라의 시야가 이동하도록 하는 방법을 알아 보는 내용이다.
//속도 조절
[SerializeField]
private float crouchSpeed;
private float applySpeed;
[SerializeField]
private float jumpForce;
//상태변수
private bool isRun = false;
private bool isCrouch = false;
private bool isGround = true;
// 앉았을 때 얼마나 앉을지 결정하기
[SerializeField]
private float crouchPosY;
private float originPosY;
private float applyCrouchPosY;
// 땅 착지 상태
private CapsuleCollider capsuleCollider;
각 동작을 하기 위한 변수들을 SerializeField에 넣어 주고 shift키와 control키를 눌렀다가 땠을 때 달리고 앉는 동작을 바꾸기 위해 참 거짓 으로 처음 설정을 해준다.
//앉기 시도
private void TryCrouch()
{
if(Input.GetKeyDown(KeyCode.LeftControl))
{
Crouch();
}
}
앉기 시도를 하면 Crouch함수를 실행하도록 하고
//앉기 동작
private void Crouch()
{
isCrouch = !isCrouch;
if(isCrouch)
{
applySpeed = crouchSpeed;
applyCrouchPosY = crouchPosY;
}
else
{
applySpeed = walkSpeed;
applyCrouchPosY = originPosY;
}
StartCoroutine(CrouchCoroutine());
}
실행했을 때 참이면 거짓으로 거짓이면 참으로 키를 누를 때마다 앉았다가 서는 상태의 변화를 할 수 있다.
새로 만든 applaySpeed를 이용하여 상태에 따라 속도가 바뀌도록 할 것이다. 그렇지 않다면 밑에 나올 1편에서 만든 속도 값을 상태에 따라 따로 Vector3값을 줘야하기 때문에 이렇게 따로 applySpeed로 만들면 번거롭지 않게 상태에 따른 속도를 바꿀 수 있다.
Vector3 _velocity = (_moveHorizontal + _moveVertical).normalized * applySpeed;
이 앉기 동작이나 다시 서는 동작을 수행할 때 좌표값이 바뀌면서 시야가 한번에 바뀌게 되는데
IEnumerator를 쓰면 속도가 그래프로 곡선을 띄면서 속도가 자연스럽게 사용자에게 보여진다.
유니티는 함수가 병행실행이 안돼서 Crouch를 실행 할 때 IEnumerator도 같이 실행되는 것 처럼 보이지만 컴퓨터가 빠르게 두개의 함수를 번갈아 가며 계산하기 때문에 같이 실행되는 것처럼 보이는 것이라고 한다.
IEnumerator CrouchCoroutine()
{
float _posY = theCamera.transform.localPosition.y;
int count = 0;
while (_posY != applyCrouchPosY)
{
count++;
_posY = Mathf.Lerp(_posY, applyCrouchPosY, 0.1f);
theCamera.transform.localPosition = new Vector3(0, _posY, 0);
if (count > 15)
break;
yield return null;
}
theCamera.transform.localPosition = new Vector3(0, applyCrouchPosY, 0f);
}
다음은 점프를 실행하는 함수이다. 점프를 하려면 일단 플레이어가 땅에 맞닿아 있어야 점프를 할 수 있어야 한다. 그렇기에 플레이어가 땅에 닿아 잇는지 검사하는 함수가 필요하다.
position을 쓰지 않고 Vector3.down를 쓴 이유는 점프의 기준이 플레이어가 아닌 무조건 땅으로 인식을 하게끔 만든 것이다.
// 지면 닿아 있는지 확인
private void IsGround()
{
isGround = Physics.Raycast(transform.position, Vector3.down, capsuleCollider.bounds.extents.y + 0.1f);
}
점프를 할 때 스페이스바키를 누름과(and) 땅에 닿아 있다면 jump를 실행하고
//점프 시도
private void TryJump()
{
if (Input.GetKeyDown(KeyCode.Space) && isGround)
{
Jump();
}
}
점프를 실행한다. 이때 속도롤 velocity를 이용하여 순간적으로 뛰게끔 만들었다.
//점프 실행
private void Jump()
{
//앉아 있을때 점프하면 일어나기
if (isCrouch)
Crouch();
myRigid.velocity = transform.up * jumpForce;
}
그리고 유니티에서 만들어볼 맵을 위한 지형만들기로 맵에 산지역과 평지를 만들고 영상에서 제공하는 3D 나무, 풀, 돌을 시험삼아 1개씩 배치를 해 보았다.