171 lines
5.1 KiB
C#

using System.Collections;
using UnityEngine;
namespace Streamingle.Contents.BossRaid
{
/// <summary>
/// 화면 전체 이펙트: 흔들림, 플래시, 히트스톱.
/// 메인 카메라 기반으로 동작합니다.
/// </summary>
public class ScreenEffect : MonoBehaviour
{
#region Fields
[Header("플래시 오버레이")]
[SerializeField]
[Tooltip("플래시용 CanvasGroup (ScreenSpace Overlay 하위). 없으면 자동 생성")]
private CanvasGroup flashOverlay;
private Coroutine _shakeCoroutine;
private Coroutine _flashCoroutine;
private Coroutine _hitStopCoroutine;
private Camera _mainCamera;
private Vector3 _cameraOriginalPos;
private Transform _cameraTransform;
#endregion
#region Unity Messages
private void Start()
{
_mainCamera = Camera.main;
if (_mainCamera != null)
{
_cameraTransform = _mainCamera.transform;
_cameraOriginalPos = _cameraTransform.localPosition;
}
EnsureFlashOverlay();
}
#endregion
#region Public Methods
/// <summary>
/// 화면 흔들림
/// </summary>
public void PlayShake(float intensity, float duration)
{
if (_cameraTransform == null) return;
if (_shakeCoroutine != null)
StopCoroutine(_shakeCoroutine);
_shakeCoroutine = StartCoroutine(ShakeCoroutine(intensity, duration));
}
/// <summary>
/// 화면 플래시 (흰색/색상 오버레이)
/// </summary>
public void PlayFlash(Color color, float duration)
{
if (_flashCoroutine != null)
StopCoroutine(_flashCoroutine);
_flashCoroutine = StartCoroutine(FlashCoroutine(color, duration));
}
/// <summary>
/// 히트스톱 (TimeScale 일시 정지)
/// </summary>
public void PlayHitStop(float duration)
{
if (duration <= 0f) return;
if (_hitStopCoroutine != null)
StopCoroutine(_hitStopCoroutine);
_hitStopCoroutine = StartCoroutine(HitStopCoroutine(duration));
}
#endregion
#region Private Methods
private void EnsureFlashOverlay()
{
if (flashOverlay != null) return;
var canvasObj = new GameObject("BossRaid_FlashOverlay");
var canvas = canvasObj.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 999;
var img = canvasObj.AddComponent<UnityEngine.UI.Image>();
img.color = new Color(1f, 1f, 1f, 0f);
img.raycastTarget = false;
flashOverlay = canvasObj.AddComponent<CanvasGroup>();
flashOverlay.alpha = 0f;
flashOverlay.interactable = false;
flashOverlay.blocksRaycasts = false;
canvasObj.transform.SetParent(transform);
}
private IEnumerator ShakeCoroutine(float intensity, float duration)
{
_cameraOriginalPos = _cameraTransform.localPosition;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
float t = 1f - (elapsed / duration);
float x = Random.Range(-intensity, intensity) * t;
float y = Random.Range(-intensity, intensity) * t;
_cameraTransform.localPosition = _cameraOriginalPos + new Vector3(x, y, 0f);
yield return null;
}
_cameraTransform.localPosition = _cameraOriginalPos;
_shakeCoroutine = null;
}
private IEnumerator FlashCoroutine(Color color, float duration)
{
if (flashOverlay == null) yield break;
var img = flashOverlay.GetComponent<UnityEngine.UI.Image>();
if (img != null) img.color = color;
// 페이드 인 (빠르게)
float fadeIn = duration * 0.2f;
float fadeOut = duration * 0.8f;
float elapsed = 0f;
while (elapsed < fadeIn)
{
elapsed += Time.unscaledDeltaTime;
flashOverlay.alpha = Mathf.Lerp(0f, 0.8f, elapsed / fadeIn);
yield return null;
}
// 페이드 아웃
elapsed = 0f;
while (elapsed < fadeOut)
{
elapsed += Time.unscaledDeltaTime;
flashOverlay.alpha = Mathf.Lerp(0.8f, 0f, elapsed / fadeOut);
yield return null;
}
flashOverlay.alpha = 0f;
_flashCoroutine = null;
}
private IEnumerator HitStopCoroutine(float duration)
{
float prevTimeScale = Time.timeScale;
Time.timeScale = 0f;
yield return new WaitForSecondsRealtime(duration);
Time.timeScale = prevTimeScale;
_hitStopCoroutine = null;
}
#endregion
}
}