Files
Colosseum/Assets/Scripts/Effects/VictoryEffect.cs
dal4segno 00233ee977 feat: 게임 오버 및 승리 UI 구현
- GameOverUI: 게임 오버 화면 표시
- VictoryUI: 보스 처치 시 승리 화면 표시
- VictoryEffect: 슬로우 모션, 카메라 연출, 이펙트 재생

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-14 15:08:34 +09:00

188 lines
5.2 KiB
C#

using System.Collections;
using UnityEngine;
using Colosseum.Enemy;
namespace Colosseum.Effects
{
/// <summary>
/// 보스 승리 연출 이펙트.
/// 보스 사망 시 카메라 연출, 이펙트, 슬로우 모션 등을 처리합니다.
/// GameManager에 의해 활성화됩니다.
/// </summary>
public class VictoryEffect : MonoBehaviour
{
[Header("Victory Settings")]
[Tooltip("승리 시 슬로우 모션 배율")]
[SerializeField] private float slowMotionScale = 0.3f;
[Tooltip("슬로우 모션 지속 시간")]
[SerializeField] private float slowMotionDuration = 2f;
[Header("Effects")]
[Tooltip("승리 시 생성할 이펙트 프리팹")]
[SerializeField] private GameObject victoryEffectPrefab;
[Tooltip("이펙트 생성 위치 오프셋")]
[SerializeField] private Vector3 effectOffset = Vector3.up * 2f;
[Header("Audio")]
[Tooltip("승리 사운드")]
[SerializeField] private AudioClip victorySound;
[Tooltip("사운드 볼륨")]
[SerializeField] private float soundVolume = 1f;
[Header("Debug")]
[SerializeField] private bool debugMode = true;
// 상태
private bool isPlaying = false;
private float originalTimeScale;
private Camera mainCamera;
private void Awake()
{
mainCamera = Camera.main;
}
private void OnEnable()
{
PlayVictoryEffect();
}
private void OnDisable()
{
// 시간 스케일 복구
if (isPlaying)
{
Time.timeScale = 1f;
isPlaying = false;
}
}
/// <summary>
/// 승리 연출 재생
/// </summary>
public void PlayVictoryEffect()
{
if (isPlaying) return;
StartCoroutine(VictorySequence());
}
private IEnumerator VictorySequence()
{
isPlaying = true;
originalTimeScale = Time.timeScale;
if (debugMode)
{
Debug.Log("[VictoryEffect] Starting victory sequence");
}
// 1. 슬로우 모션
yield return StartCoroutine(PlaySlowMotion());
// 2. 카메라 연출
yield return StartCoroutine(PlayCameraEffect());
// 3. 이펙트 생성
SpawnVictoryEffect();
// 4. 사운드 재생
PlayVictorySound();
// 5. 시간 복구
Time.timeScale = originalTimeScale;
isPlaying = false;
if (debugMode)
{
Debug.Log("[VictoryEffect] Victory sequence complete");
}
}
private IEnumerator PlaySlowMotion()
{
float elapsed = 0f;
while (elapsed < slowMotionDuration)
{
elapsed += Time.unscaledDeltaTime;
float t = elapsed / slowMotionDuration;
// 처음에는 슬로우, 나중에는 복구
if (t < 0.5f)
{
Time.timeScale = Mathf.Lerp(originalTimeScale, slowMotionScale, t * 2f);
}
else
{
Time.timeScale = Mathf.Lerp(slowMotionScale, originalTimeScale, (t - 0.5f) * 2f);
}
yield return null;
}
Time.timeScale = slowMotionScale;
}
private IEnumerator PlayCameraEffect()
{
if (mainCamera == null || BossEnemy.ActiveBoss == null)
yield break;
// 보스 위치로 카메라 이동
Transform bossTransform = BossEnemy.ActiveBoss.transform;
Vector3 targetPosition = bossTransform.position + effectOffset;
float elapsed = 0f;
float duration = slowMotionDuration * 0.5f;
while (elapsed < duration)
{
elapsed += Time.unscaledDeltaTime;
// 카메라가 보스를 바라보도록
mainCamera.transform.LookAt(targetPosition);
yield return null;
}
}
private void SpawnVictoryEffect()
{
if (victoryEffectPrefab == null) return;
Vector3 spawnPosition = transform.position + effectOffset;
if (BossEnemy.ActiveBoss != null)
{
spawnPosition = BossEnemy.ActiveBoss.transform.position + effectOffset;
}
var effect = Instantiate(victoryEffectPrefab, spawnPosition, Quaternion.identity);
// 일정 시간 후 제거
Destroy(effect, 5f);
if (debugMode)
{
Debug.Log("[VictoryEffect] Spawned victory effect");
}
}
private void PlayVictorySound()
{
if (victorySound == null) return;
AudioSource.PlayClipAtPoint(victorySound, transform.position, soundVolume);
if (debugMode)
{
Debug.Log("[VictoryEffect] Played victory sound");
}
}
}
}