171 lines
5.1 KiB
C#
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
|
|
}
|
|
}
|