152 lines
4.2 KiB
C#
152 lines
4.2 KiB
C#
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
public class JunpyoCollapseEvent : MonoBehaviour
|
|
{
|
|
[Header("구준표")]
|
|
[SerializeField] private Animator junpyoAnimator;
|
|
[SerializeField] private string collapseTrigger = "Collapse";
|
|
|
|
[Header("플레이어 시선")]
|
|
[SerializeField] private Transform xrOrigin;
|
|
[SerializeField] private Transform playerCamera;
|
|
[SerializeField] private Transform lookTarget;
|
|
|
|
[Tooltip("구준표 쪽으로 화면이 돌아가는 시간")]
|
|
[SerializeField] private float lookDuration = 0.4f;
|
|
|
|
[Tooltip("화면 전환을 시작하고 몇 초 뒤 쓰러질지")]
|
|
[SerializeField] private float collapseStartDelay = 0.15f;
|
|
|
|
[Header("페이드 타이밍")]
|
|
[Tooltip("쓰러지기 시작한 뒤 페이드 아웃까지 기다리는 시간")]
|
|
[SerializeField] private float fadeDelay = 1.3f;
|
|
|
|
[Header("쓰러진 뒤 실행")]
|
|
[SerializeField] private UnityEvent onCollapseFinished;
|
|
|
|
private bool hasPlayed;
|
|
|
|
public void PlayCollapse()
|
|
{
|
|
if (hasPlayed)
|
|
return;
|
|
|
|
if (junpyoAnimator == null)
|
|
{
|
|
Debug.LogWarning(
|
|
"[JunpyoCollapseEvent] 구준표 Animator가 연결되지 않았습니다."
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (xrOrigin == null ||
|
|
playerCamera == null ||
|
|
lookTarget == null)
|
|
{
|
|
Debug.LogWarning(
|
|
"[JunpyoCollapseEvent] 플레이어 시선 설정이 비어 있습니다."
|
|
);
|
|
return;
|
|
}
|
|
|
|
hasPlayed = true;
|
|
StartCoroutine(CollapseRoutine());
|
|
}
|
|
|
|
private IEnumerator CollapseRoutine()
|
|
{
|
|
// 구준표 방향으로 화면 전환 시작
|
|
StartCoroutine(LookAtJunpyoRoutine());
|
|
|
|
// 화면이 돌아가기 시작한 뒤 잠깐 기다림
|
|
yield return new WaitForSeconds(collapseStartDelay);
|
|
|
|
// 구준표 쓰러지는 애니메이션 실행
|
|
junpyoAnimator.ResetTrigger(collapseTrigger);
|
|
junpyoAnimator.SetTrigger(collapseTrigger);
|
|
|
|
// 쓰러지는 모습을 보여준 뒤 페이드 아웃
|
|
yield return new WaitForSeconds(fadeDelay);
|
|
|
|
onCollapseFinished?.Invoke();
|
|
}
|
|
|
|
private IEnumerator LookAtJunpyoRoutine()
|
|
{
|
|
Vector3 cameraForward = playerCamera.forward;
|
|
cameraForward.y = 0f;
|
|
|
|
Vector3 targetDirection =
|
|
lookTarget.position - playerCamera.position;
|
|
targetDirection.y = 0f;
|
|
|
|
if (cameraForward.sqrMagnitude < 0.001f ||
|
|
targetDirection.sqrMagnitude < 0.001f)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
cameraForward.Normalize();
|
|
targetDirection.Normalize();
|
|
|
|
// 현재 시선에서 구준표까지 필요한 좌우 회전각
|
|
float totalAngle = Vector3.SignedAngle(
|
|
cameraForward,
|
|
targetDirection,
|
|
Vector3.up
|
|
);
|
|
|
|
float duration = Mathf.Max(0.01f, lookDuration);
|
|
float elapsed = 0f;
|
|
float previousAngle = 0f;
|
|
|
|
while (elapsed < duration)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
|
|
float t = Mathf.Clamp01(elapsed / duration);
|
|
|
|
// 부드럽게 시작하고 부드럽게 멈춤
|
|
float easedT = t * t * (3f - 2f * t);
|
|
|
|
float currentAngle = totalAngle * easedT;
|
|
float frameAngle = currentAngle - previousAngle;
|
|
|
|
// 카메라 현재 위치를 중심으로 XR Origin 회전
|
|
// 플레이어 위치가 옆으로 밀리는 현상을 방지
|
|
xrOrigin.RotateAround(
|
|
playerCamera.position,
|
|
Vector3.up,
|
|
frameAngle
|
|
);
|
|
|
|
previousAngle = currentAngle;
|
|
|
|
yield return null;
|
|
}
|
|
}
|
|
|
|
public void ResetCollapse()
|
|
{
|
|
StopAllCoroutines();
|
|
|
|
hasPlayed = false;
|
|
|
|
if (junpyoAnimator != null)
|
|
junpyoAnimator.ResetTrigger(collapseTrigger);
|
|
}
|
|
|
|
[ContextMenu("Test Collapse")]
|
|
private void TestCollapse()
|
|
{
|
|
PlayCollapse();
|
|
}
|
|
|
|
[ContextMenu("Reset Collapse")]
|
|
private void TestResetCollapse()
|
|
{
|
|
ResetCollapse();
|
|
}
|
|
} |