PlayerMove 스크립트
- 경사면을 내려갈때 땅 체크가 간헐적으로 됐다 안됐다 하는 문제 발생.
- isGround 값이 체크가 잘 안되는 문제.
- isGround 상태가 true일때 ySpeed 값을 건드린게 문제.
해결 : 땅에 닿았을때 ySpeed값 설정하는 코드를 없애버리면 자연스럽게 땅 체크가 잘 됨.
isGrounded = characterController.isGrounded;
using Cinemachine;
using System;
using UnityEngine;
using UnityEngine.UI;
public class PlayerMove : MonoBehaviour
{
CharacterController characterController;
Player player;
PlayerController controller;
Animator animator;
public CinemachineVirtualCamera virtualCamera; // 캐릭터 줌 카메라
public Vector2 moveInput { get; private set; }
public float walkSpeed = 4f;
public float runSpeed = 7f;
public float rotationSpeed = 5f;
public float jumpSpeed = 5f;
[Header("Mouse Zoom")]
float zoomValue;
public float zoomSpeed = 3f;
public float minFOV = 16;
public float maxFOV = 60;
[Header("Ground Check")]
public LayerMask groundMask;
public bool isGrounded;
float ySpeed;
Vector3 movementDirection;
float verticalVelocity;
float speed;
float xVelocity;
float yVelocity;
bool isRunning = false;
bool isJumping;
bool isCombat;
// 입력 시간을 저장하는 변수
private float lastTapTimeW = 0f;
private float lastTapTimeA = 0f;
private float lastTapTimeS = 0f;
private float lastTapTimeD = 0f;
// 더블탭을 감지할 시간 간격
public float doubleTapTime = 0.3f;
private void Awake()
{
}
void Start()
{
characterController = GetComponent<CharacterController>();
player = GetComponent<Player>();
controller = player.playerController;
animator = GetComponentInChildren<Animator>();
speed = walkSpeed;
isCombat = false;
controller.Character.Move.performed += context =>
{
moveInput = context.ReadValue<Vector2>();
Run(); // 입력이 발생할 때마다 더블탭 감지
};
controller.Character.Move.canceled += context =>
{
moveInput = Vector2.zero;
isRunning = false;
speed = walkSpeed; // 걷기 속도로 복귀
animator.SetBool("isRunning", isRunning);
};
controller.Character.Jump.performed += context =>
{
Jump();
};
controller.Character.Combat.performed += context =>
{
isCombat = !isCombat;
animator.SetBool("IsCombat", isCombat);
};
controller.Character.Zoom.performed += context =>
{
//zoomValue = Mathf.Sign(context.ReadValue<float>());
zoomValue = context.ReadValue<float>();
};
}
void Update()
{
Move();
CameraZoom();
}
private void CameraZoom()
{
if(zoomValue != 0 && virtualCamera != null)
{
float currentFOV = virtualCamera.m_Lens.FieldOfView;
currentFOV -= zoomValue * 0.01f * zoomSpeed ;
currentFOV = Mathf.Clamp(currentFOV, minFOV, maxFOV);
virtualCamera.m_Lens.FieldOfView = currentFOV;
zoomValue = 0; // 마우스 휠 한번 움직인 뒤, 휠 움직임값 0으로 초기화
}
}
private void Jump()
{
//ySpeed = jumpSpeed;
if (isGrounded) // 착지 상태일 때만 점프 가능
{
isJumping = true; // 다음 프레임에서 점프 요청
}
}
private void Move()
{
ApplyMovement();
}
private void Run()
{
if (moveInput.y > 0 && Mathf.Abs(moveInput.x) == 0) // W 키 입력 감지
{
if (!isRunning && Time.time - lastTapTimeW < doubleTapTime)
{
// 더블탭 감지됨 => 달리기 전환
isRunning = true;
speed = runSpeed;
animator.SetBool("isRunning", true);
print("double click");
}
lastTapTimeW = Time.time; // 마지막 입력 시간 갱신
}
else if (moveInput.y < 0 && Mathf.Abs(moveInput.x) == 0) // S 키 더블탭 감지
{
if (!isRunning && Time.time - lastTapTimeS < doubleTapTime)
{
isRunning = true;
speed = runSpeed;
animator.SetBool("isRunning", true);
}
lastTapTimeS = Time.time;
}
else if (moveInput.x > 0 && Mathf.Abs(moveInput.y) == 0) // D 키 더블탭 감지
{
if (!isRunning && Time.time - lastTapTimeD < doubleTapTime)
{
isRunning = true;
speed = runSpeed;
animator.SetBool("isRunning", true);
}
lastTapTimeD = Time.time;
}
else if (moveInput.x < 0 && Mathf.Abs(moveInput.y) == 0) // A 키 더블탭 감지
{
if (!isRunning && Time.time - lastTapTimeA < doubleTapTime)
{
isRunning = true;
speed = runSpeed;
animator.SetBool("isRunning", true);
}
lastTapTimeA = Time.time;
}
}
private void ApplyMovement()
{
if (TimeManager.instance.gameOver)
return;
// 시네머신 가상카메라의 Transform에서 forward 방향 가져오기
Transform cameraTransform = virtualCamera.VirtualCameraGameObject.transform;
Vector3 cameraForward = cameraTransform.forward;
Vector3 cameraRight = cameraTransform.right;
// 수직 방향은 제외하고 카메라가 바라보는 평면 방향만 가져옴
cameraForward.y = 0;
cameraRight.y = 0;
cameraForward.Normalize();
cameraRight.Normalize();
// 중력 적용
ApplyGravity();
// 입력을 카메라 기준으로 변환
Vector3 moveDirection = cameraForward * moveInput.y + cameraRight * moveInput.x;
//if (moveDirection.magnitude > 0)
//{
// // 캐릭터 회전
// Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
// transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
//}
// 여기서부터 수정된 부분
if (moveDirection.magnitude > 0)
{
RaycastHit hit;
// 현재 위치에서 아래로 레이캐스트를 쏴서 경사면 감지
if (Physics.Raycast(transform.position + Vector3.up * 0.1f, Vector3.down, out hit, 1f, groundMask))
{
// 이동 방향을 경사면의 normal 벡터를 기준으로 투영
moveDirection = Vector3.ProjectOnPlane(moveDirection, hit.normal).normalized;
}
// 캐릭터 회전
Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
}
// 캐릭터 이동
Vector3 velocity = moveDirection * speed;
velocity.y = ySpeed;
characterController.Move(velocity * Time.deltaTime);
animator.SetFloat("move", moveDirection.magnitude);
animator.SetFloat("X", moveInput.x, 0.1f, Time.deltaTime);
animator.SetFloat("Y", moveInput.y, 0.1f, Time.deltaTime);
}
private void ApplyGravity()
{
// 캐릭터가 땅에 닿았는지 확인
isGrounded = characterController.isGrounded;
//isGrounded = Physics.CheckSphere(transform.position, 0.3f, groundMask);
//isGrounded = Physics.Raycast(transform.position + new Vector3(0, 0.02f, 0), Vector3.down, 0.04f, groundMask);
//Debug.DrawRay(transform.position + new Vector3(0 ,0.02f, 0), Vector3.down * 0.04f, isGrounded ? Color.green : Color.red);
if (isGrounded)
{
if(ySpeed < 0)
{
// 착지 시 수직 속도를 리셋
ySpeed = -0.1f; // 아주 작은 음수 값으로 유지
animator.SetBool("isGrounded", true);
animator.SetBool("isJumping", false);
animator.SetBool("isFalling", false);
}
// 점프 기능
if(isJumping)
{
ySpeed = jumpSpeed; // 점프 속도 적용
isJumping = false; // 점프 요청 초기화
animator.SetBool("isGrounded", false);
animator.SetBool("isJumping", true);
animator.SetBool("isFalling", true);
}
}
else
{
// 착지하지 않았을 때 중력 적용
ySpeed -= 9.81f * Time.deltaTime;
}
}
//void OnDrawGizmos()
//{
// // 체크하는 구체를 시각화
// Gizmos.color = isGrounded ? Color.green : Color.red;
// Gizmos.DrawWireSphere(transform.position, 0.32f);
//}