Files
WhaleAdventure_VR/Assets/02_Scripts/Cave/RaftRiverController.cs
2026-06-16 16:41:50 +09:00

179 lines
4.6 KiB
C#

using UnityEngine;
using UnityEngine.Events;
public class RaftRiverController : MonoBehaviour
{
[Header("Path")]
[SerializeField] private Transform[] pathPoints;
[Header("Steering Input")]
[SerializeField] private SteeringKeyXR steeringKey;
[Tooltip("체크하면 키 움직임과 반대로 뗏목이 좌우 이동합니다.")]
[SerializeField] private bool reverseControl = true;
[Header("Move Speed")]
[SerializeField] private float forwardSpeed = 5f;
[SerializeField] private float turnSpeed = 4f;
[Header("Side Control")]
[SerializeField] private float sideMoveSpeed = 10f;
[Tooltip("강 중앙선 기준 좌우 이동 가능 범위입니다. 동굴 폭 46이면 14~16 추천.")]
[SerializeField] private float maxSideOffset = 16f;
[Header("Arrival")]
[SerializeField] private float pointReachDistance = 1.5f;
[Header("Events")]
public UnityEvent onArrived;
private int currentPointIndex = 0;
private float sideOffset = 0f;
private Vector3 currentCenterPosition;
private Vector3 currentForward;
private Vector3 currentRight;
private bool isFinished = false;
public bool IsFinished => isFinished;
private void Start()
{
if (pathPoints == null || pathPoints.Length == 0)
{
Debug.LogWarning("[RaftRiverController] Path Points가 비어 있습니다.", this);
enabled = false;
return;
}
currentCenterPosition = transform.position;
// 첫 목표 지점은 0번이 아니라 1번부터 가는 것이 자연스러울 때가 많음.
// 단, 0번이 현재 위치와 다르면 그대로 0번부터 이동.
currentPointIndex = 0;
}
private void Update()
{
if (isFinished)
return;
MoveAlongPath();
HandleSideControl();
ApplyRaftPositionAndRotation();
}
private void MoveAlongPath()
{
if (currentPointIndex >= pathPoints.Length)
{
FinishRaftRide();
return;
}
Transform targetPoint = pathPoints[currentPointIndex];
Vector3 toTarget = targetPoint.position - currentCenterPosition;
toTarget.y = 0f;
float distance = toTarget.magnitude;
if (distance < pointReachDistance)
{
currentPointIndex++;
if (currentPointIndex >= pathPoints.Length)
{
FinishRaftRide();
return;
}
targetPoint = pathPoints[currentPointIndex];
toTarget = targetPoint.position - currentCenterPosition;
toTarget.y = 0f;
}
if (toTarget.sqrMagnitude < 0.001f)
return;
currentForward = toTarget.normalized;
currentCenterPosition += currentForward * forwardSpeed * Time.deltaTime;
// 진행 방향 기준 오른쪽 벡터
currentRight = Vector3.Cross(Vector3.up, currentForward).normalized;
}
private void HandleSideControl()
{
float input = 0f;
if (steeringKey != null)
{
input = steeringKey.SteeringValue;
}
else
{
// 키 연결 전 테스트용
input = Input.GetAxis("Horizontal");
}
// 반전은 여기서만 처리
if (reverseControl)
{
input *= -1f;
}
sideOffset += input * sideMoveSpeed * Time.deltaTime;
sideOffset = Mathf.Clamp(sideOffset, -maxSideOffset, maxSideOffset);
}
private void ApplyRaftPositionAndRotation()
{
Vector3 finalPosition = currentCenterPosition + currentRight * sideOffset;
// 현재 뗏목 높이 유지
finalPosition.y = transform.position.y;
transform.position = finalPosition;
if (currentForward != Vector3.zero)
{
Quaternion targetRotation = Quaternion.LookRotation(currentForward, Vector3.up);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
turnSpeed * Time.deltaTime
);
}
}
private void FinishRaftRide()
{
if (isFinished)
return;
isFinished = true;
Debug.Log("[RaftRiverController] 목적지 도착. 뗏목 정지.");
onArrived?.Invoke();
}
public void StopRaft()
{
isFinished = true;
}
public void ResumeRaft()
{
isFinished = false;
}
public void SetSteeringKey(SteeringKeyXR newSteeringKey)
{
steeringKey = newSteeringKey;
}
}