using System.Collections;
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 once before the first execution of Update after the MonoBehaviour is created
void Start()
{
capsuleCollider = GetComponent<CapsuleCollider>();
myRigid = GetComponent<Rigidbody>();//Player에 있는 리지드바디 장착
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)
// isCrouch = false;
//else
// isCrouch = true;위 한줄짜리 코드 축약임
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)
{
_posY = Mathf.Lerp(_posY, applyCrouchPosY, 0.5f);
theCamera.transform.localPosition = new Vector3(0, _posY, 0);
if (count > 15)
break;
yield return new WaitForSeconds(0.1f);
}
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.linearVelocity = 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");//좌우 방향키를 입력시 +1 ~ -1이 반환된다
float _moveDirZ = Input.GetAxisRaw("Vertical");//상하 움직임 입력시 +1 ~ -1이 반환된다
Vector3 _moveHorizontal = transform.right * _moveDirX;
Vector3 _moveVertical = transform.forward * _moveDirZ;//실제 입력과 같이 이동을 처리함
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));
//Debug.Log(myRigid.rotation);
//Debug.Log(myRigid.rotation.eulerAngles);
}
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);
}
}
이번시간에는 걷기,뛰기,점프, 앉기, 일어서기를 공부할 것이다
일단 걷기 뛰기부터 구현해보자
걷기 뛰기에는 빨간줄 친 3개의 변수가 이용된다
void Start()에는 빨간줄친 변수를 작성해서 이동 스피드를 걷기로 초기화한다
void Move()함수에서는 빨간줄 친 부분이 walkSpeed 로 작성되었었다
applySpeed로 수정해주자
applySpeed안에 walkSpeed와 runSpeed를 번갈아 넣어주면서 이동속도를 조절해줄 것이다
void Update에서 TryRun()함수를 작성해보자
TryRun()함수이다
138번째 줄은 왼쪽 Shift버튼을 누르는 동안
Running()함수가 호출되는 것이고
142번째 줄은 버튼을 눌렀다가 땟을때
RunningCancel()함수가 호출되는 것이다
일단 호출된 Running()함수를 보자
150번째 줄은 isCrouch라면 Crouch()함수가 호출되는 것이다 이건 앉기이므로 미리 작성해주자
152번째 줄에 bool값인 Run을 true로 만들어주고
applySpeed에 runSpeed를 넣어서 달리기를 구현해 주었다
다음은 RunningCancel() 함수를 보자
isRun을 false로 바꿔주고 applySpeed에 walkSpeed수치를 넣어서 걷기를 구현해주었다
인스펙터에 WalkSpeed와 RunSpeed가 생겼다 필자가 넣은 수치를 참고해서 값을 넣어주고 테스트해보자
다음은 점프를 구현해보자
위에 밑줄친 변수를 작성해주자
jumpForce는 우리가 점프할 힘을 입력할 것이고
isGround는 지면과 접촉했는지 bool값으로 확인해줄 것 이다
그리고 변수 목록에 캡슐 콜라이더도 넣어주자
우리는 캡슐 콜라이더를 이용해서 점프를 구현할 것이기 때문에 필요하다
void Start()에 캡슐콜라이더를 초기화했다
변수에 GetComponent<CapsuleCollider>(); 이라고 작성되어 있으면 현제 스크립트가 붙어 있는 오브젝트의 인스펙터에 있는
CapsuleCollider을 불러온다는 뜻 이다
이제 TryJump()를 작성해주자
작성된 TryJump()이다
if문으로 조건을 달고 스페이스바 버튼을 누르면서 isGround가 true이면 실행되게 했다
그 뒤 Jump()함수를 호출한다
이제 Jump()함수를 보자
131번째 줄은 isCrouch가 true일때 Crouch()함수가 실행되게 했다
그리고 점프가 실행되는 것은 133번 줄인데...
필자가 분홍빛으로 밑 줄을 그어 놓은 함수는 유니티6에 등장한 linearVelocity 이다
기존의 Velocity는 이제 사용되지 않아서 Velocity 라고 오류가 난다
이제 133번째 줄을 설명하자면
myRigid.linearVelocity는 transform.up * jumpForce와 같다라고 작성되어 있다
즉 transform의 위로 jumpForce만큼 힘을 넣어서 점프를 구현한다는 뜻이다
이제 void Update()에서 IsGround()함수를 작성해주자
IsGround()함수이다
일단 isGround변수를 선언해주고 안의 값에
Physice.Raycast함수를 이용해서 레이져를 쏴준다
그리고 그 안에 기준이 되는 transform.position을 넣고 다음으로 레이져를 쏴줄 방향 Vector3.down을 넣어주고
길이는 capsuleCollider.bounds.extents.y + 0.1f 로 캡슐 콜라이더의 길이에 0.1f를 더한 값을 넣어준 것이다
두번째 인자 Vector3.down을 설명하자면...
우리가 Vector3를 이용한 이유는 Transform기준으로 힘을 가하면 캡슐의 방향이 기울어져 있으면 기준값이 같이 변화가 생겨서
점프가 이상해진다
하지만 Vector3로 방향을 지정해주면 Transform에 영향을 안받고 백터방향에 영향을 받기 때문에 일정한 방향이 구현된다
그리고 3번째 인자 capsuleCollider.bounds.extents.y + 0.1f를 설명하자면
위 스크린샷을 참고해서 보자면 캡슐콜라이더의 전체 길이기 bounds이다
그리고 extents.y는 y값의 절반분을 나타내는 것이고 그러면 쉽게 말해
캡슐콜라이더의 절반분의 레이져를 아래쪽으로 쏴주고 신호가 있으면 true를 반환해주는 것이다
그런데 0.1f를 왜 더 넣었는지 질문했을 수 있는데
이건 우리가 계단을 가던지 지형이 고르지 않을때를 대비해서 여유분을 더해준 것이다
이제 인스펙터에 JumpForce의 수치를 입력하고 테스트하자
이제 왼쪽에 있는 Ctrl버튼을 누르면 앉게되는 함수를 구현하자
하늘빛으로 표시한 변수를 작성해주자
crouchSpeed는 우리가 앉는 속도이고
isCrouch는 bool 값으로 앉기를 관리하는 변수이다
25번부터 28번줄은 우리가 얼마나 앉았다 일어날 것인지를 넣을 변수이다
void Start()를 보자
originPosY변수에 theCamera.transform.localPosition.y라고 값을 입력했다
우리는 카메라를 아래로 내려가게해서 앉기를 구현할 것이기 때문에 카메라를 넣었다
그리고 localPosition.y라고 입력해 넣었는데
이건 카메라 오브젝트가 Player오브젝트 안에 있기 때문에 Player오브젝트의 기준으로 만들어 준 것이다
56번 줄은 applyCrouchPosY = originPosY라고 값을 초기화해 넣었다
이제 TryCrouch()함수를 작성해주자
TryCrouch()함수이다
왼쪽Ctrl버튼을 누르면 Crouch()를 호출하도록 구현되어 있다
호출된 Crouch()함수이다
isCrouch = !isCrouch 라고 한줄만 표기했는데
이걸 풀어서 정리하면 82번부터 85번줄로 쓸 수 있다
우리는 간단하게 압축한 81번줄처럼 작성해주자
86번 줄을 보자
if문으로 isCrouch가 true일때
applySpeed가 crouchSpeed의 값이 되고
applyCrouchPosY가 crouchPosY로 값이 변하게 된다
else 그 외의 경우라면
당연히 isCrouch가 false일 경우
applySpeed가 walkSpeed가 되고
applyCrouchPosY가 originPosY가 되게 한다
이제 96번 줄의 코루틴 CrouchCoroutine()를 보자
임시 변수를 선언해서 _posY를 만들어주고 카메라 좌표를 넣어주자
그리고 int count = 0을 선언해주고
while문으로 조건을 _posY가 applyCrouchPosY가 아닐때로 잡고
_posY에 Mathf.Lerp() 함수를 적용 즉 서서히 실행되게 한다
이걸 풀어서 설명하면 _posY에서 applyCrouchPosY까지 0.5f 만큼의 속도로 이동한다고 생각하면 된다
그리고 이 값을 108번 줄에 new Vector3를 넣어서 적용해준다
그런데 이렇게 하면 Mathf.Lerp 즉 보간법으로는 적확한 숫자가 맞아 떨어지지 않는다
우린 그걸 해결하기 위해 103번에 int count = 0이라고 선언을 해주고
107번에 count++로 1씩 증감해주는 로직을 넣자
그리고 110번 줄에 if문으로 count는 15보다 크다고 해주고
숫자를 넘기면 break를 걸게 한다
이렇게 해주면 15까지 반복하다가 정지하게 된다
그리고 112번은 케이디님의 경우 yield reutrn null로 프레임마다 정지하는 코루틴을 넣으셨는데
필자는 yield reutrn new WaitforSeconds(0.1f)를 선언해서 0.1초마다 고르게 작동하도록 했다
그리고 114번줄에 카메라가 수정된 카메라 위치가 적용되게 했다
이제 테스트해보자
그리고 케이디님의 경우 Crouch()함수를 여기저기 넣으셨는데 우리는 이미 넣으면서 진행했기 때문에 추가 작성은 필요 없다
오늘 이번 글의 구현은 여기까지 한다
'FPS서바이벌 디펜스' 카테고리의 다른 글
4.팔 구현 (0) | 2025.04.14 |
---|---|
3.지형 제작 (0) | 2025.04.14 |
1.기본 캐릭터 움직임 (1) | 2025.04.01 |