Merge branch 'main' of https://www.nakjungit.site/sharedacc520k/WhaleAdventure_VR
This commit is contained in:
@@ -17,11 +17,18 @@ public class RaftRiverController : MonoBehaviour
|
||||
[SerializeField] private float turnSpeed = 4f;
|
||||
|
||||
[Header("Side Control")]
|
||||
[SerializeField] private float sideMoveSpeed = 10f;
|
||||
[SerializeField] private float sideMoveSpeed = 16f;
|
||||
[SerializeField] private float sideAcceleration = 40f;
|
||||
|
||||
[Tooltip("강 중앙선 기준 좌우 이동 가능 범위입니다. 동굴 폭 46이면 14~16 추천.")]
|
||||
[SerializeField] private float maxSideOffset = 16f;
|
||||
|
||||
[Header("Path Follow Feel")]
|
||||
[SerializeField] private float pathFollowSmoothTime = 0.28f;
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float rotationVelocityBlend = 0.45f;
|
||||
[SerializeField] private float steeringYawAngle = 18f;
|
||||
|
||||
[Header("Arrival")]
|
||||
[SerializeField] private float pointReachDistance = 1.5f;
|
||||
|
||||
@@ -30,17 +37,29 @@ public class RaftRiverController : MonoBehaviour
|
||||
|
||||
private int currentPointIndex = 0;
|
||||
private float sideOffset = 0f;
|
||||
private float sideVelocity = 0f;
|
||||
private float currentSteeringInput = 0f;
|
||||
|
||||
private Vector3 currentCenterPosition;
|
||||
private Vector3 currentForward;
|
||||
private Vector3 currentRight;
|
||||
private Vector3 positionSmoothVelocity;
|
||||
private Vector3 previousPosition;
|
||||
|
||||
private bool isFinished = false;
|
||||
private bool warnedMissingSteeringKey;
|
||||
|
||||
public bool IsFinished => isFinished;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
ResolveSteeringKey();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
ResolveSteeringKey();
|
||||
|
||||
if (pathPoints == null || pathPoints.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("[RaftRiverController] Path Points가 비어 있습니다.", this);
|
||||
@@ -49,6 +68,9 @@ private void Start()
|
||||
}
|
||||
|
||||
currentCenterPosition = transform.position;
|
||||
currentForward = transform.forward;
|
||||
currentRight = transform.right;
|
||||
previousPosition = transform.position;
|
||||
|
||||
// 첫 목표 지점은 0번이 아니라 1번부터 가는 것이 자연스러울 때가 많음.
|
||||
// 단, 0번이 현재 위치와 다르면 그대로 0번부터 이동.
|
||||
@@ -67,6 +89,8 @@ private void Update()
|
||||
|
||||
private void MoveAlongPath()
|
||||
{
|
||||
SkipMissingPathPoints();
|
||||
|
||||
if (currentPointIndex >= pathPoints.Length)
|
||||
{
|
||||
FinishRaftRide();
|
||||
@@ -108,6 +132,7 @@ private void MoveAlongPath()
|
||||
private void HandleSideControl()
|
||||
{
|
||||
float input = 0f;
|
||||
ResolveSteeringKey();
|
||||
|
||||
if (steeringKey != null)
|
||||
{
|
||||
@@ -115,8 +140,13 @@ private void HandleSideControl()
|
||||
}
|
||||
else
|
||||
{
|
||||
// 키 연결 전 테스트용
|
||||
input = Input.GetAxis("Horizontal");
|
||||
input = ReadLegacyHorizontalInput();
|
||||
|
||||
if (!warnedMissingSteeringKey)
|
||||
{
|
||||
warnedMissingSteeringKey = true;
|
||||
Debug.LogWarning("[RaftRiverController] SteeringKeyXR 참조가 없어 좌우 조향 없이 전진합니다.", this);
|
||||
}
|
||||
}
|
||||
|
||||
// 반전은 여기서만 처리
|
||||
@@ -125,22 +155,60 @@ private void HandleSideControl()
|
||||
input *= -1f;
|
||||
}
|
||||
|
||||
sideOffset += input * sideMoveSpeed * Time.deltaTime;
|
||||
currentSteeringInput = input;
|
||||
|
||||
float targetSideVelocity = input * sideMoveSpeed;
|
||||
sideVelocity = Mathf.MoveTowards(
|
||||
sideVelocity,
|
||||
targetSideVelocity,
|
||||
sideAcceleration * Time.deltaTime
|
||||
);
|
||||
|
||||
sideOffset += sideVelocity * Time.deltaTime;
|
||||
sideOffset = Mathf.Clamp(sideOffset, -maxSideOffset, maxSideOffset);
|
||||
|
||||
if (Mathf.Approximately(Mathf.Abs(sideOffset), maxSideOffset) &&
|
||||
Mathf.Sign(sideVelocity) == Mathf.Sign(sideOffset))
|
||||
{
|
||||
sideVelocity = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRaftPositionAndRotation()
|
||||
{
|
||||
Vector3 finalPosition = currentCenterPosition + currentRight * sideOffset;
|
||||
previousPosition = transform.position;
|
||||
|
||||
Vector3 targetPosition = currentCenterPosition + currentRight * sideOffset;
|
||||
|
||||
// 현재 뗏목 높이 유지
|
||||
finalPosition.y = transform.position.y;
|
||||
targetPosition.y = transform.position.y;
|
||||
|
||||
transform.position = finalPosition;
|
||||
float smoothTime = Mathf.Max(0.01f, pathFollowSmoothTime);
|
||||
transform.position = Vector3.SmoothDamp(
|
||||
transform.position,
|
||||
targetPosition,
|
||||
ref positionSmoothVelocity,
|
||||
smoothTime
|
||||
);
|
||||
|
||||
if (currentForward != Vector3.zero)
|
||||
{
|
||||
Quaternion targetRotation = Quaternion.LookRotation(currentForward, Vector3.up);
|
||||
Vector3 frameVelocity = transform.position - previousPosition;
|
||||
frameVelocity.y = 0f;
|
||||
|
||||
Vector3 lookDirection = currentForward;
|
||||
if (frameVelocity.sqrMagnitude > 0.0001f)
|
||||
{
|
||||
lookDirection = Vector3.Slerp(
|
||||
currentForward,
|
||||
frameVelocity.normalized,
|
||||
rotationVelocityBlend
|
||||
);
|
||||
}
|
||||
|
||||
Quaternion targetRotation =
|
||||
Quaternion.LookRotation(lookDirection, Vector3.up) *
|
||||
Quaternion.Euler(0f, currentSteeringInput * steeringYawAngle, 0f);
|
||||
|
||||
transform.rotation = Quaternion.Slerp(
|
||||
transform.rotation,
|
||||
@@ -175,5 +243,31 @@ public void ResumeRaft()
|
||||
public void SetSteeringKey(SteeringKeyXR newSteeringKey)
|
||||
{
|
||||
steeringKey = newSteeringKey;
|
||||
warnedMissingSteeringKey = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveSteeringKey()
|
||||
{
|
||||
if (steeringKey != null)
|
||||
return;
|
||||
|
||||
steeringKey = GetComponentInChildren<SteeringKeyXR>(true);
|
||||
}
|
||||
|
||||
private void SkipMissingPathPoints()
|
||||
{
|
||||
while (currentPointIndex < pathPoints.Length && pathPoints[currentPointIndex] == null)
|
||||
{
|
||||
currentPointIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private float ReadLegacyHorizontalInput()
|
||||
{
|
||||
#if ENABLE_LEGACY_INPUT_MANAGER
|
||||
return Input.GetAxis("Horizontal");
|
||||
#else
|
||||
return 0f;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.XR.Interaction.Toolkit;
|
||||
using UnityEngine.XR.Interaction.Toolkit.Interactables;
|
||||
|
||||
public class SteeringKeyXR : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private UnityEngine.XR.Interaction.Toolkit.Interactables.XRGrabInteractable grabInteractable;
|
||||
[SerializeField] private XRGrabInteractable grabInteractable;
|
||||
[SerializeField] private Transform keyPivot;
|
||||
|
||||
[Header("Steering")]
|
||||
@@ -16,6 +17,7 @@ public class SteeringKeyXR : MonoBehaviour
|
||||
|
||||
private Transform currentInteractor;
|
||||
private bool isGrabbed;
|
||||
private bool warnedMissingGrabInteractable;
|
||||
|
||||
private Quaternion initialPivotRotation;
|
||||
private float currentAngle;
|
||||
@@ -24,6 +26,9 @@ public float SteeringValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Mathf.Approximately(maxAngle, 0f))
|
||||
return 0f;
|
||||
|
||||
return Mathf.Clamp(currentAngle / maxAngle, -1f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -33,11 +38,14 @@ private void Awake()
|
||||
if (keyPivot == null)
|
||||
keyPivot = transform;
|
||||
|
||||
ResolveGrabInteractable();
|
||||
initialPivotRotation = keyPivot.localRotation;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ResolveGrabInteractable();
|
||||
|
||||
if (grabInteractable != null)
|
||||
{
|
||||
grabInteractable.selectEntered.AddListener(OnGrab);
|
||||
@@ -82,7 +90,7 @@ private void OnRelease(SelectExitEventArgs args)
|
||||
private void UpdateAngleFromHand()
|
||||
{
|
||||
Vector3 worldDir = currentInteractor.position - keyPivot.position;
|
||||
Vector3 localDir = keyPivot.InverseTransformDirection(worldDir);
|
||||
Vector3 localDir = GetInitialPivotSpaceDirection(worldDir);
|
||||
|
||||
float targetAngle = Mathf.Atan2(localDir.x, localDir.z) * Mathf.Rad2Deg;
|
||||
|
||||
@@ -96,4 +104,27 @@ private void ApplyRotation()
|
||||
keyPivot.localRotation =
|
||||
initialPivotRotation * Quaternion.Euler(0f, currentAngle, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetInitialPivotSpaceDirection(Vector3 worldDirection)
|
||||
{
|
||||
Quaternion referenceRotation = keyPivot.parent != null
|
||||
? keyPivot.parent.rotation * initialPivotRotation
|
||||
: initialPivotRotation;
|
||||
|
||||
return Quaternion.Inverse(referenceRotation) * worldDirection;
|
||||
}
|
||||
|
||||
private void ResolveGrabInteractable()
|
||||
{
|
||||
if (grabInteractable != null)
|
||||
return;
|
||||
|
||||
grabInteractable = GetComponentInChildren<XRGrabInteractable>(true);
|
||||
|
||||
if (grabInteractable == null && !warnedMissingGrabInteractable)
|
||||
{
|
||||
warnedMissingGrabInteractable = true;
|
||||
Debug.LogWarning("[SteeringKeyXR] XRGrabInteractable 참조가 없어 키를 잡아도 조향 이벤트를 받을 수 없습니다.", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user