CombatPlayer 움직임 로직 보완 중
This commit is contained in:
parent
da3c952d78
commit
9dd465a8ed
@ -3,6 +3,7 @@ using System.Collections;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace BlueWaterProject
|
||||
@ -46,12 +47,18 @@ namespace BlueWaterProject
|
||||
[SerializeField] private Transform visualLook;
|
||||
[field: SerializeField] public Animator Animator { get; set; }
|
||||
[SerializeField] private Transform groundCheck;
|
||||
|
||||
[Title("Extensions Data")]
|
||||
[SerializeField] private float nextFrameCoefficient = 3f;
|
||||
|
||||
private Vector2 movementInput;
|
||||
private Vector3 currentDirection = Vector3.back;
|
||||
public Vector3 PreviousDirection { get; set; } = Vector3.back;
|
||||
[field: SerializeField] public Collider[] HitColliders { get; set; }
|
||||
private RaycastHit slopeHit;
|
||||
private int groundLayer;
|
||||
private bool isOnSlope;
|
||||
private bool isGrounded;
|
||||
|
||||
private const float RAY_DISTANCE = 3f;
|
||||
|
||||
@ -107,23 +114,26 @@ namespace BlueWaterProject
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var isMoving = Rb.velocity.magnitude > 0.1f;
|
||||
var isMoving = currentDirection.magnitude > 0.01f;
|
||||
if (isMoving)
|
||||
{
|
||||
PreviousDirection = Rb.velocity.normalized;
|
||||
PreviousDirection = currentDirection.normalized;
|
||||
Animator.SetFloat(XDirectionHash, PreviousDirection.x);
|
||||
Animator.SetFloat(ZDirectionHash, PreviousDirection.z);
|
||||
}
|
||||
Animator.SetBool(IsMovingHash, isMoving);
|
||||
|
||||
var localScale = visualLook.localScale;
|
||||
localScale.x = Rb.velocity.x switch
|
||||
localScale.x = PreviousDirection.x switch
|
||||
{
|
||||
> 0.01f => Mathf.Abs(localScale.x),
|
||||
< -0.01f => -Mathf.Abs(localScale.x),
|
||||
_ => localScale.x
|
||||
};
|
||||
visualLook.localScale = localScale;
|
||||
|
||||
isOnSlope = IsOnSlope();
|
||||
isGrounded = IsGrounded();
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
@ -193,25 +203,38 @@ namespace BlueWaterProject
|
||||
|
||||
private void HandleMovement()
|
||||
{
|
||||
var movement = new Vector3(movementInput.x, 0, movementInput.y);
|
||||
var moveDirection = IsDashing ? PreviousDirection : movement.normalized;
|
||||
currentDirection = new Vector3(movementInput.x, 0, movementInput.y).normalized;
|
||||
//var currentDirection = IsDashing ? PreviousDirection : currentMovement.normalized;
|
||||
|
||||
var velocity = CalculateNextFrameGroundAngle(moveDirection) < maxSlopeAngle ? moveDirection : Vector3.zero;
|
||||
var gravity = Vector3.down * Mathf.Abs(Rb.velocity.y);
|
||||
|
||||
if (IsOnSlope() || velocity == moveDirection && IsGrounded())
|
||||
if (isOnSlope && isGrounded)
|
||||
{
|
||||
velocity = Vector3.ProjectOnPlane(moveDirection, slopeHit.normal).normalized;
|
||||
gravity = Vector3.zero;
|
||||
Rb.useGravity = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Rb.useGravity = true;
|
||||
currentDirection = Vector3.ProjectOnPlane(currentDirection, slopeHit.normal).normalized;
|
||||
}
|
||||
|
||||
var moveValue = IsDashing ? DashForce : moveSpeed;
|
||||
Rb.velocity = velocity * moveValue + gravity;
|
||||
var newPosition = Rb.position + currentDirection * (moveValue * Time.fixedDeltaTime);
|
||||
|
||||
Rb.MovePosition(newPosition);
|
||||
|
||||
// var currentMovement = new Vector3(movementInput.x, 0, movementInput.y);
|
||||
// var currentDirection = IsDashing ? PreviousDirection : currentMovement.normalized;
|
||||
// var isNextFrameInAngle = CalculateNextFrameGroundAngle(currentDirection) < maxSlopeAngle;
|
||||
// var velocity = isNextFrameInAngle ? currentDirection : Vector3.zero;
|
||||
// var gravity = Vector3.down * Mathf.Abs(Rb.velocity.y);
|
||||
//
|
||||
// if (IsOnSlope() && IsGrounded())
|
||||
// {
|
||||
// velocity = Vector3.ProjectOnPlane(currentDirection, slopeHit.normal).normalized;
|
||||
// gravity = Vector3.zero;
|
||||
// Rb.useGravity = false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Rb.useGravity = true;
|
||||
// }
|
||||
//
|
||||
// var moveValue = IsDashing ? DashForce : moveSpeed;
|
||||
// Rb.velocity = velocity * moveValue + gravity;
|
||||
}
|
||||
|
||||
public bool IsOnSlope()
|
||||
@ -237,7 +260,7 @@ namespace BlueWaterProject
|
||||
|
||||
private float CalculateNextFrameGroundAngle(Vector3 direction)
|
||||
{
|
||||
var nextFramePlayerPosition = transform.position + direction * (3f * moveSpeed * Time.fixedDeltaTime);
|
||||
var nextFramePlayerPosition = transform.position + direction * (nextFrameCoefficient * moveSpeed * Time.fixedDeltaTime);
|
||||
|
||||
if (Physics.Raycast(nextFramePlayerPosition, Vector3.down, out var hitInfo, RAY_DISTANCE, groundLayer))
|
||||
{
|
||||
|
@ -0,0 +1,600 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace BlueWaterProject
|
||||
{
|
||||
public class NewCombatPlayer : MonoBehaviour, IMovement3D
|
||||
{
|
||||
/***********************************************************************
|
||||
* Definitions
|
||||
***********************************************************************/
|
||||
|
||||
#region Class
|
||||
|
||||
[Serializable]
|
||||
public class Components
|
||||
{
|
||||
public PlayerInput playerInput;
|
||||
public CapsuleCollider capsuleCollider;
|
||||
public Rigidbody rb;
|
||||
public Transform visualLook;
|
||||
public Animator animator;
|
||||
public Transform groundCheck;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CheckOption
|
||||
{
|
||||
[Tooltip("지면으로 체크할 레이어 설정")] public LayerMask groundLayerMask = -1;
|
||||
|
||||
[Range(0.01f, 0.5f), Tooltip("전방 감지 거리")]
|
||||
public float forwardCheckDistance = 0.1f;
|
||||
|
||||
[Range(0.1f, 10.0f), Tooltip("지면 감지 거리")]
|
||||
public float groundCheckDistance = 2.0f;
|
||||
|
||||
[Range(0.0f, 0.1f), Tooltip("지면 인식 허용 거리")]
|
||||
public float groundCheckThreshold = 0.01f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class MovementOption
|
||||
{
|
||||
[Range(1f, 10f), Tooltip("이동속도")] public float speed = 10f;
|
||||
|
||||
[Range(1f, 3f), Tooltip("달리기 이동속도 증가 계수")]
|
||||
public float runningCoefficient = 1.5f;
|
||||
|
||||
[Range(1f, 75f), Tooltip("등반 가능한 경사각")]
|
||||
public float maxSlopeAngle = 50f;
|
||||
|
||||
[Range(0f, 4f), Tooltip("경사로 이동속도 변화율(가속/감속)")]
|
||||
public float slopeAccel = 1f;
|
||||
|
||||
[Range(-9.81f, 0f), Tooltip("중력")] public float gravity = -9.81f;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CurrentState
|
||||
{
|
||||
public bool isMoving;
|
||||
public bool isRunning;
|
||||
public bool isGrounded;
|
||||
public bool isOnSteepSlope; // 등반 불가능한 경사로에 올라와 있음
|
||||
public bool isForwardBlocked; // 전방에 장애물 존재
|
||||
public bool isOutOfControl; // 제어 불가 상태
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CurrentValue
|
||||
{
|
||||
public Vector2 movementInput;
|
||||
public Vector3 worldMoveDirection;
|
||||
public Vector3 groundNormal;
|
||||
public Vector3 groundCross;
|
||||
public Vector3 horizontalVelocity;
|
||||
|
||||
[Space] public float outOfControlDuration;
|
||||
|
||||
[Space] public float groundDistance;
|
||||
public float groundSlopeAngle; // 현재 바닥의 경사각
|
||||
public float groundVerticalSlopeAngle; // 수직으로 재측정한 경사각
|
||||
public float forwardSlopeAngle; // 캐릭터가 바라보는 방향의 경사각
|
||||
public float slopeAccel; // 경사로 인한 가속/감속 비율
|
||||
|
||||
[Space] public float gravity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* Variables
|
||||
***********************************************************************/
|
||||
|
||||
#region Variables
|
||||
|
||||
// [SerializeField] private Components components = new();
|
||||
// [SerializeField] private CheckOption checkOptions = new();
|
||||
// [SerializeField] private MovementOption moveOptions = new();
|
||||
// [SerializeField] private CurrentState currentStates = new();
|
||||
// [SerializeField] private CurrentValue currentValues = new();
|
||||
|
||||
[field: SerializeField] public Components PlayerComponents { get; private set; } = new();
|
||||
[field: SerializeField] public CheckOption PlayerCheckOption { get; private set; } = new();
|
||||
[field: SerializeField] public MovementOption PlayerMovementOption { get; private set; } = new();
|
||||
[field: SerializeField] public CurrentState PlayerCurrentState { get; set; } = new();
|
||||
[field: SerializeField] public CurrentValue PlayerCurrentValue { get; set; } = new();
|
||||
|
||||
private float capsuleRadiusDifferent;
|
||||
private float fixedDeltaTime;
|
||||
|
||||
private float castRadius; // Sphere, Capsule 레이캐스트 반지름
|
||||
|
||||
private Vector3 CapsuleTopCenterPoint => new(transform.position.x,
|
||||
transform.position.y + PlayerComponents.capsuleCollider.height - PlayerComponents.capsuleCollider.radius,
|
||||
transform.position.z);
|
||||
|
||||
private Vector3 CapsuleBottomCenterPoint => new(transform.position.x,
|
||||
transform.position.y + PlayerComponents.capsuleCollider.radius, transform.position.z);
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* Unity Events
|
||||
***********************************************************************/
|
||||
|
||||
#region Unity Events
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitRigidbody();
|
||||
InitCapsuleCollider();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Move();
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
fixedDeltaTime = Time.fixedDeltaTime;
|
||||
|
||||
CheckGround();
|
||||
CheckForward();
|
||||
|
||||
UpdatePhysics();
|
||||
UpdateValues();
|
||||
|
||||
CalculateMovements();
|
||||
ApplyMovementsToRigidbody();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* Init Methods
|
||||
***********************************************************************/
|
||||
|
||||
#region Init Methods
|
||||
|
||||
private void InitRigidbody()
|
||||
{
|
||||
TryGetComponent(out PlayerComponents.rb);
|
||||
if (PlayerComponents.rb == null)
|
||||
{
|
||||
PlayerComponents.rb = gameObject.AddComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
PlayerComponents.rb.constraints = RigidbodyConstraints.FreezeRotation;
|
||||
PlayerComponents.rb.interpolation = RigidbodyInterpolation.Interpolate;
|
||||
PlayerComponents.rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
PlayerComponents.rb.useGravity = false;
|
||||
}
|
||||
|
||||
private void InitCapsuleCollider()
|
||||
{
|
||||
TryGetComponent(out PlayerComponents.capsuleCollider);
|
||||
if (PlayerComponents.capsuleCollider == null)
|
||||
{
|
||||
PlayerComponents.capsuleCollider = gameObject.AddComponent<CapsuleCollider>();
|
||||
|
||||
// 렌더러를 모두 탐색하여 높이 결정
|
||||
var maxHeight = -1f;
|
||||
|
||||
// // 1. SMR 확인
|
||||
// var smrArr = GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
// if (smrArr.Length > 0)
|
||||
// {
|
||||
// foreach (var smr in smrArr)
|
||||
// {
|
||||
// foreach (var vertex in smr.sharedMesh.vertices)
|
||||
// {
|
||||
// if(maxHeight < vertex.y)
|
||||
// maxHeight = vertex.y;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // 2. MR 확인
|
||||
// else
|
||||
// {
|
||||
// var mfArr = GetComponentsInChildren<MeshFilter>();
|
||||
// if (mfArr.Length > 0)
|
||||
// {
|
||||
// foreach (var mf in mfArr)
|
||||
// {
|
||||
// foreach (var vertex in mf.mesh.vertices)
|
||||
// {
|
||||
// if (maxHeight < vertex.y)
|
||||
// maxHeight = vertex.y;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// 3. 캡슐 콜라이더 값 설정
|
||||
if (maxHeight <= 0)
|
||||
{
|
||||
maxHeight = 1f;
|
||||
}
|
||||
|
||||
var center = maxHeight * 0.5f;
|
||||
|
||||
PlayerComponents.capsuleCollider.height = maxHeight;
|
||||
PlayerComponents.capsuleCollider.center = Vector3.up * center;
|
||||
PlayerComponents.capsuleCollider.radius = 0.5f;
|
||||
}
|
||||
|
||||
var capsuleColliderRadius = PlayerComponents.capsuleCollider.radius;
|
||||
castRadius = capsuleColliderRadius * 0.9f;
|
||||
capsuleRadiusDifferent = capsuleColliderRadius - castRadius + 0.05f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* PlayerInput
|
||||
***********************************************************************/
|
||||
|
||||
#region PlayerInput
|
||||
|
||||
private void OnMove(InputValue value)
|
||||
{
|
||||
PlayerCurrentValue.movementInput = value.Get<Vector2>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* IMovement3D interface
|
||||
***********************************************************************/
|
||||
|
||||
#region IMovement3D interface
|
||||
|
||||
public bool IsMoving() => PlayerCurrentState.isMoving;
|
||||
public bool IsGrounded() => PlayerCurrentState.isGrounded;
|
||||
public float GetDistanceFromGround() => PlayerCurrentValue.groundDistance;
|
||||
|
||||
public void SetMovement(in Vector3 worldMoveDirection, bool isRunning)
|
||||
{
|
||||
PlayerCurrentValue.worldMoveDirection = worldMoveDirection;
|
||||
PlayerCurrentState.isMoving = worldMoveDirection.sqrMagnitude > 0.01f;
|
||||
PlayerCurrentState.isRunning = isRunning;
|
||||
}
|
||||
|
||||
public void StopMoving()
|
||||
{
|
||||
PlayerCurrentValue.worldMoveDirection = Vector3.zero;
|
||||
PlayerCurrentState.isMoving = false;
|
||||
PlayerCurrentState.isRunning = false;
|
||||
}
|
||||
|
||||
public void KnockBack(in Vector3 force, float time)
|
||||
{
|
||||
SetOutOfControl(time);
|
||||
PlayerComponents.rb.AddForce(force, ForceMode.Impulse);
|
||||
}
|
||||
|
||||
public void SetOutOfControl(float time)
|
||||
{
|
||||
PlayerCurrentValue.outOfControlDuration = time;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* Methods
|
||||
***********************************************************************/
|
||||
|
||||
#region Methods
|
||||
|
||||
private void Move()
|
||||
{
|
||||
// 이동하지 않는 경우, 미끄럼 방지
|
||||
if (PlayerCurrentState.isMoving == false)
|
||||
{
|
||||
PlayerComponents.rb.velocity = new Vector3(0f, PlayerComponents.rb.velocity.y, 0f);
|
||||
return;
|
||||
}
|
||||
|
||||
// 실제 이동 벡터 계산
|
||||
var worldMove = new Vector3(PlayerCurrentValue.movementInput.x, 0, PlayerCurrentValue.movementInput.y).normalized;
|
||||
worldMove *= (PlayerMovementOption.speed) * (PlayerCurrentState.isRunning ? PlayerMovementOption.runningCoefficient : 1f);
|
||||
|
||||
// Y축 속도는 유지하면서 XZ평면 이동
|
||||
PlayerComponents.rb.velocity = new Vector3(worldMove.x, PlayerComponents.rb.velocity.y, worldMove.z);
|
||||
}
|
||||
|
||||
/// <summary> 하단 지면 검사 </summary>
|
||||
private void CheckGround()
|
||||
{
|
||||
PlayerCurrentValue.groundDistance = float.MaxValue;
|
||||
PlayerCurrentValue.groundNormal = Vector3.up;
|
||||
PlayerCurrentValue.groundSlopeAngle = 0f;
|
||||
PlayerCurrentValue.forwardSlopeAngle = 0f;
|
||||
|
||||
var cast = Physics.SphereCast(CapsuleBottomCenterPoint, castRadius,
|
||||
Vector3.down, out var hit, PlayerCheckOption.groundCheckDistance, PlayerCheckOption.groundLayerMask,
|
||||
QueryTriggerInteraction.Ignore);
|
||||
|
||||
PlayerCurrentState.isGrounded = false;
|
||||
|
||||
if (cast)
|
||||
{
|
||||
// 지면 노멀벡터 초기화
|
||||
PlayerCurrentValue.groundNormal = hit.normal;
|
||||
|
||||
// 현재 위치한 지면의 경사각 구하기(캐릭터 이동방향 고려)
|
||||
PlayerCurrentValue.groundSlopeAngle = Vector3.Angle(PlayerCurrentValue.groundNormal, Vector3.up);
|
||||
PlayerCurrentValue.forwardSlopeAngle =
|
||||
Vector3.Angle(PlayerCurrentValue.groundNormal, PlayerCurrentValue.worldMoveDirection) - 90f;
|
||||
|
||||
PlayerCurrentState.isOnSteepSlope =
|
||||
PlayerCurrentValue.groundSlopeAngle >= PlayerMovementOption.maxSlopeAngle;
|
||||
|
||||
// 경사각 이중검증 (수직 레이캐스트) : 뾰족하거나 각진 부분 체크
|
||||
//if (State.isOnSteepSlope)
|
||||
//{
|
||||
// Vector3 ro = hit.point + Vector3.up * 0.1f;
|
||||
// Vector3 rd = Vector3.down;
|
||||
// bool rayD =
|
||||
// Physics.SphereCast(ro, 0.09f, rd, out var hitRayD, 0.2f, COption.groundLayerMask, QueryTriggerInteraction.Ignore);
|
||||
|
||||
// Current.groundVerticalSlopeAngle = rayD ? Vector3.Angle(hitRayD.normal, Vector3.up) : Current.groundSlopeAngle;
|
||||
|
||||
// State.isOnSteepSlope = Current.groundVerticalSlopeAngle >= MOption.maxSlopeAngle;
|
||||
//}
|
||||
|
||||
PlayerCurrentValue.groundDistance =
|
||||
Mathf.Max(hit.distance - capsuleRadiusDifferent - PlayerCheckOption.groundCheckThreshold, 0f);
|
||||
|
||||
PlayerCurrentState.isGrounded =
|
||||
(PlayerCurrentValue.groundDistance <= 0.0001f) && !PlayerCurrentState.isOnSteepSlope;
|
||||
|
||||
GzUpdateValue(ref gzGroundTouch, hit.point);
|
||||
}
|
||||
|
||||
// 월드 이동벡터 회전축
|
||||
PlayerCurrentValue.groundCross = Vector3.Cross(PlayerCurrentValue.groundNormal, Vector3.up);
|
||||
}
|
||||
|
||||
/// <summary> 전방 장애물 검사 : 레이어 관계 없이 trigger가 아닌 모든 장애물 검사 </summary>
|
||||
private void CheckForward()
|
||||
{
|
||||
bool cast =
|
||||
Physics.CapsuleCast(CapsuleBottomCenterPoint, CapsuleTopCenterPoint, castRadius,
|
||||
PlayerCurrentValue.worldMoveDirection + Vector3.down * 0.1f,
|
||||
out var hit, PlayerCheckOption.forwardCheckDistance, -1, QueryTriggerInteraction.Ignore);
|
||||
|
||||
PlayerCurrentState.isForwardBlocked = false;
|
||||
if (cast)
|
||||
{
|
||||
float forwardObstacleAngle = Vector3.Angle(hit.normal, Vector3.up);
|
||||
PlayerCurrentState.isForwardBlocked = forwardObstacleAngle >= PlayerMovementOption.maxSlopeAngle;
|
||||
|
||||
GzUpdateValue(ref gzForwardTouch, hit.point);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePhysics()
|
||||
{
|
||||
// Custom Gravity, Jumping State
|
||||
if (PlayerCurrentState.isGrounded)
|
||||
{
|
||||
PlayerCurrentValue.gravity = 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayerCurrentValue.gravity += fixedDeltaTime * PlayerMovementOption.gravity;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValues()
|
||||
{
|
||||
// Out Of Control
|
||||
PlayerCurrentState.isOutOfControl = PlayerCurrentValue.outOfControlDuration > 0f;
|
||||
|
||||
if (PlayerCurrentState.isOutOfControl)
|
||||
{
|
||||
PlayerCurrentValue.outOfControlDuration -= fixedDeltaTime;
|
||||
PlayerCurrentValue.worldMoveDirection = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateMovements()
|
||||
{
|
||||
if (PlayerCurrentState.isOutOfControl)
|
||||
{
|
||||
PlayerCurrentValue.horizontalVelocity = Vector3.zero;
|
||||
return;
|
||||
}
|
||||
|
||||
// 0. 가파른 경사면에 있는 경우 : 꼼짝말고 미끄럼틀 타기
|
||||
//if (State.isOnSteepSlope && Current.groundDistance < 0.1f)
|
||||
//{
|
||||
// DebugMark(0);
|
||||
|
||||
// Current.horizontalVelocity =
|
||||
// Quaternion.AngleAxis(90f - Current.groundSlopeAngle, Current.groundCross) * (Vector3.up * Current.gravity);
|
||||
|
||||
// Com.rBody.velocity = Current.horizontalVelocity;
|
||||
|
||||
// return;
|
||||
//}
|
||||
|
||||
// 2. XZ 이동속도 계산
|
||||
// 공중에서 전방이 막힌 경우 제한 (지상에서는 벽에 붙어서 이동할 수 있도록 허용)
|
||||
if (PlayerCurrentState.isForwardBlocked && !PlayerCurrentState.isGrounded)
|
||||
{
|
||||
PlayerCurrentValue.horizontalVelocity = Vector3.zero;
|
||||
}
|
||||
else // 이동 가능한 경우 : 지상 or 전방이 막히지 않음
|
||||
{
|
||||
float speed = !PlayerCurrentState.isMoving ? 0f :
|
||||
!PlayerCurrentState.isRunning ? PlayerMovementOption.speed :
|
||||
PlayerMovementOption.speed * PlayerMovementOption.runningCoefficient;
|
||||
|
||||
PlayerCurrentValue.horizontalVelocity = PlayerCurrentValue.worldMoveDirection * speed;
|
||||
}
|
||||
|
||||
// 3. XZ 벡터 회전
|
||||
// 지상이거나 지면에 가까운 높이
|
||||
if (PlayerCurrentState.isGrounded ||
|
||||
PlayerCurrentValue.groundDistance < PlayerCheckOption.groundCheckDistance)
|
||||
{
|
||||
if (PlayerCurrentState.isMoving && !PlayerCurrentState.isForwardBlocked)
|
||||
{
|
||||
// 경사로 인한 가속/감속
|
||||
if (PlayerMovementOption.slopeAccel > 0f)
|
||||
{
|
||||
bool isPlus = PlayerCurrentValue.forwardSlopeAngle >= 0f;
|
||||
float absFsAngle = isPlus
|
||||
? PlayerCurrentValue.forwardSlopeAngle
|
||||
: -PlayerCurrentValue.forwardSlopeAngle;
|
||||
float accel = PlayerMovementOption.slopeAccel * absFsAngle * 0.01111f + 1f;
|
||||
PlayerCurrentValue.slopeAccel = !isPlus ? accel : 1.0f / accel;
|
||||
|
||||
PlayerCurrentValue.horizontalVelocity *= PlayerCurrentValue.slopeAccel;
|
||||
}
|
||||
|
||||
// 벡터 회전 (경사로)
|
||||
PlayerCurrentValue.horizontalVelocity =
|
||||
Quaternion.AngleAxis(-PlayerCurrentValue.groundSlopeAngle, PlayerCurrentValue.groundCross) *
|
||||
PlayerCurrentValue.horizontalVelocity;
|
||||
}
|
||||
}
|
||||
|
||||
GzUpdateValue(ref gzRotatedWorldMoveDirection, PlayerCurrentValue.horizontalVelocity * 0.2f);
|
||||
}
|
||||
|
||||
/// <summary> 리지드바디 최종 속도 적용 </summary>
|
||||
private void ApplyMovementsToRigidbody()
|
||||
{
|
||||
if (PlayerCurrentState.isOutOfControl)
|
||||
{
|
||||
PlayerComponents.rb.velocity = new Vector3(PlayerComponents.rb.velocity.x, PlayerCurrentValue.gravity,
|
||||
PlayerComponents.rb.velocity.z);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerComponents.rb.velocity =
|
||||
PlayerCurrentValue.horizontalVelocity + Vector3.up * (PlayerCurrentValue.gravity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/***********************************************************************
|
||||
* Gizmos, GUI
|
||||
***********************************************************************/
|
||||
|
||||
#region .
|
||||
|
||||
private Vector3 gzGroundTouch;
|
||||
private Vector3 gzForwardTouch;
|
||||
private Vector3 gzRotatedWorldMoveDirection;
|
||||
|
||||
[Header("Gizmos Option")] public bool showGizmos = true;
|
||||
|
||||
[SerializeField, Range(0.01f, 2f)] private float gizmoRadius = 0.05f;
|
||||
|
||||
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
if (Application.isPlaying == false) return;
|
||||
if (!showGizmos) return;
|
||||
if (!enabled) return;
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawSphere(gzGroundTouch, gizmoRadius);
|
||||
|
||||
if (PlayerCurrentState.isForwardBlocked)
|
||||
{
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawSphere(gzForwardTouch, gizmoRadius);
|
||||
}
|
||||
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawLine(gzGroundTouch - PlayerCurrentValue.groundCross,
|
||||
gzGroundTouch + PlayerCurrentValue.groundCross);
|
||||
|
||||
Gizmos.color = Color.black;
|
||||
Gizmos.DrawLine(transform.position, transform.position + gzRotatedWorldMoveDirection);
|
||||
|
||||
Gizmos.color = new Color(0.5f, 1.0f, 0.8f, 0.8f);
|
||||
Gizmos.DrawWireSphere(CapsuleTopCenterPoint, castRadius);
|
||||
Gizmos.DrawWireSphere(CapsuleBottomCenterPoint, castRadius);
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("UNITY_EDITOR")]
|
||||
private void GzUpdateValue<T>(ref T variable, in T value)
|
||||
{
|
||||
variable = value;
|
||||
}
|
||||
|
||||
[SerializeField, Space] private bool showGUI = true;
|
||||
[SerializeField] private int guiTextSize = 28;
|
||||
|
||||
private float prevForwardSlopeAngle;
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
if (Application.isPlaying == false) return;
|
||||
if (!showGUI) return;
|
||||
if (!enabled) return;
|
||||
|
||||
GUIStyle labelStyle = GUI.skin.label;
|
||||
labelStyle.normal.textColor = Color.yellow;
|
||||
labelStyle.fontSize = Math.Max(guiTextSize, 20);
|
||||
|
||||
prevForwardSlopeAngle = PlayerCurrentValue.forwardSlopeAngle == -90f
|
||||
? prevForwardSlopeAngle
|
||||
: PlayerCurrentValue.forwardSlopeAngle;
|
||||
|
||||
var oldColor = GUI.color;
|
||||
GUI.color = new Color(0f, 0f, 0f, 0.5f);
|
||||
GUI.Box(new Rect(40, 40, 420, 260), "");
|
||||
GUI.color = oldColor;
|
||||
|
||||
GUILayout.BeginArea(new Rect(50, 50, 1000, 500));
|
||||
GUILayout.Label($"Ground Height : {Mathf.Min(PlayerCurrentValue.groundDistance, 99.99f): 00.00}",
|
||||
labelStyle);
|
||||
GUILayout.Label($"Slope Angle(Ground) : {PlayerCurrentValue.groundSlopeAngle: 00.00}", labelStyle);
|
||||
GUILayout.Label($"Slope Angle(Forward) : {prevForwardSlopeAngle: 00.00}", labelStyle);
|
||||
GUILayout.Label($"Allowed Slope Angle : {PlayerMovementOption.maxSlopeAngle: 00.00}", labelStyle);
|
||||
GUILayout.Label($"Current Slope Accel : {PlayerCurrentValue.slopeAccel: 00.00}", labelStyle);
|
||||
GUILayout.Label($"Current Speed Mag : {PlayerCurrentValue.horizontalVelocity.magnitude: 00.00}",
|
||||
labelStyle);
|
||||
GUILayout.EndArea();
|
||||
|
||||
float sWidth = Screen.width;
|
||||
float sHeight = Screen.height;
|
||||
|
||||
GUIStyle RTLabelStyle = GUI.skin.label;
|
||||
RTLabelStyle.fontSize = 20;
|
||||
RTLabelStyle.normal.textColor = Color.green;
|
||||
|
||||
oldColor = GUI.color;
|
||||
GUI.color = new Color(1f, 1f, 1f, 0.5f);
|
||||
GUI.Box(new Rect(sWidth - 355f, 5f, 340f, 100f), "");
|
||||
GUI.color = oldColor;
|
||||
|
||||
var yPos = 10f;
|
||||
GUI.Label(new Rect(sWidth - 350f, yPos, 150f, 30f), $"Speed : {PlayerMovementOption.speed: 00.00}",
|
||||
RTLabelStyle);
|
||||
PlayerMovementOption.speed = GUI.HorizontalSlider(new Rect(sWidth - 200f, yPos + 10f, 180f, 20f),
|
||||
PlayerMovementOption.speed, 1f, 10f);
|
||||
|
||||
yPos += 20f;
|
||||
GUI.Label(new Rect(sWidth - 350f, yPos, 150f, 30f), $"Max Slope : {PlayerMovementOption.maxSlopeAngle: 00}",
|
||||
RTLabelStyle);
|
||||
PlayerMovementOption.maxSlopeAngle = (int)GUI.HorizontalSlider(
|
||||
new Rect(sWidth - 200f, yPos + 10f, 180f, 20f), PlayerMovementOption.maxSlopeAngle, 1f, 75f);
|
||||
|
||||
labelStyle.fontSize = Math.Max(guiTextSize, 20);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 01fef22c346a235499c530436f7c9c53
|
23
BlueWater/Assets/02.Scripts/Interface/IMovement3D.cs
Normal file
23
BlueWater/Assets/02.Scripts/Interface/IMovement3D.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace BlueWaterProject
|
||||
{
|
||||
public interface IMovement3D
|
||||
{
|
||||
/// <summary> 현재 이동 중인지 여부 </summary>
|
||||
bool IsMoving();
|
||||
/// <summary> 지면에 닿아 있는지 여부 </summary>
|
||||
bool IsGrounded();
|
||||
/// <summary> 지면으로부터의 거리 </summary>
|
||||
float GetDistanceFromGround();
|
||||
|
||||
/// <summary> 월드 이동벡터 초기화(이동 명령) </summary>
|
||||
void SetMovement(in Vector3 worldMoveDirection, bool isRunning);
|
||||
/// <summary> 이동 중지 </summary>
|
||||
void StopMoving();
|
||||
|
||||
/// <summary> 밀쳐내기 </summary>
|
||||
void KnockBack(in Vector3 force, float time);
|
||||
}
|
||||
}
|
11
BlueWater/Assets/02.Scripts/Interface/IMovement3D.cs.meta
Normal file
11
BlueWater/Assets/02.Scripts/Interface/IMovement3D.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1165eeeca2febd42b2f40167c74d02f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -64,7 +64,7 @@ Material:
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 2800000, guid: ee40cf7053c686c49a4d70e09c6cff39, type: 3}
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
|
@ -159,7 +159,7 @@ GameObject:
|
||||
- component: {fileID: 7729150195808218711}
|
||||
- component: {fileID: 6121288256300454469}
|
||||
- component: {fileID: 4906016173319036404}
|
||||
- component: {fileID: 2650321273105086144}
|
||||
- component: {fileID: 1096317451054001340}
|
||||
m_Layer: 9
|
||||
m_Name: CombatPlayer
|
||||
m_TagString: Untagged
|
||||
@ -231,9 +231,9 @@ Rigidbody:
|
||||
m_ImplicitTensor: 1
|
||||
m_UseGravity: 1
|
||||
m_IsKinematic: 0
|
||||
m_Interpolate: 0
|
||||
m_Interpolate: 1
|
||||
m_Constraints: 112
|
||||
m_CollisionDetection: 0
|
||||
m_CollisionDetection: 2
|
||||
--- !u!114 &6121288256300454469
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -272,7 +272,7 @@ MonoBehaviour:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2391945466483065398}
|
||||
m_Enabled: 1
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5258cddac7934c1469d147dddbdb5023, type: 3}
|
||||
m_Name:
|
||||
@ -302,29 +302,63 @@ MonoBehaviour:
|
||||
visualLook: {fileID: 8557381169392297019}
|
||||
<Animator>k__BackingField: {fileID: 3138574858532492034}
|
||||
groundCheck: {fileID: 7407663186225048994}
|
||||
nextFrameCoefficient: 3
|
||||
<HitColliders>k__BackingField: []
|
||||
--- !u!64 &2650321273105086144
|
||||
MeshCollider:
|
||||
--- !u!114 &1096317451054001340
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2391945466483065398}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 5
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
m_Mesh: {fileID: 0}
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 01fef22c346a235499c530436f7c9c53, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
<PlayerComponents>k__BackingField:
|
||||
playerInput: {fileID: 6121288256300454469}
|
||||
capsuleCollider: {fileID: 6858674358337067996}
|
||||
rb: {fileID: 7729150195808218711}
|
||||
visualLook: {fileID: 8557381169392297019}
|
||||
animator: {fileID: 3138574858532492034}
|
||||
groundCheck: {fileID: 7407663186225048994}
|
||||
<PlayerCheckOption>k__BackingField:
|
||||
groundLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 8
|
||||
forwardCheckDistance: 0.1
|
||||
groundCheckDistance: 2
|
||||
groundCheckThreshold: 0.01
|
||||
<PlayerMovementOption>k__BackingField:
|
||||
speed: 10
|
||||
runningCoefficient: 1.5
|
||||
maxSlopeAngle: 50
|
||||
slopeAccel: 1
|
||||
gravity: -9.81
|
||||
<PlayerCurrentState>k__BackingField:
|
||||
isMoving: 0
|
||||
isRunning: 0
|
||||
isGrounded: 0
|
||||
isOnSteepSlope: 0
|
||||
isForwardBlocked: 0
|
||||
isOutOfControl: 0
|
||||
<PlayerCurrentValue>k__BackingField:
|
||||
worldMoveDirection: {x: 0, y: 0, z: 0}
|
||||
groundNormal: {x: 0, y: 0, z: 0}
|
||||
groundCross: {x: 0, y: 0, z: 0}
|
||||
horizontalVelocity: {x: 0, y: 0, z: 0}
|
||||
outOfControlDuration: 0
|
||||
groundDistance: 0
|
||||
groundSlopeAngle: 0
|
||||
groundVerticalSlopeAngle: 0
|
||||
forwardSlopeAngle: 0
|
||||
slopeAccel: 0
|
||||
gravity: 0
|
||||
showGizmos: 1
|
||||
gizmoRadius: 0.05
|
||||
showGUI: 1
|
||||
guiTextSize: 28
|
||||
--- !u!1 &3669261844365681366
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aba6fa52907672a4da99307e9c925639
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user