Project_WL/Assets/WL/Combat/Telegraph/TelegraphRunner.cs

82 lines
4.1 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// TelegraphRunner.cs — 815f 전용 틱 러너(숨은 오브젝트 1개 · DontDestroyOnLoad · 코루틴 없음 · GC 0)
//
// PD 지시 #815-5 · 발주서 WL-815f
//
// ■ 왜 ImpactRunner(811gh) · WLReactionRunner(811def) 에 얹지 않았나
// 같은 파일에 틱을 붙이면 **병합 충돌**이 난다(811gh 가 811i 때문에 파일을 나눈 것과 같은 이유).
// 새 Manager/Singleton 패러다임이 아니라 기존 러너와 **같은 패턴**을 하나 더 두는 것이다.
//
// ■ 무엇을 하나
// Telegraph.Tick — 데칼 위치·회전 추종 · 채워지는 진행 · 스케일 펄스 · 히트 직전 흰 섬광 · 타임아웃 원복.
// 파괴·비활성·앱 종료·씬 전환 시 애니메이터 속도 / 스케일 / MPB 색 / 데칼을 전부 원복한다.
//
// ■ 에디트 모드
// 프로브가 Play 없이 검증할 수 있게 ForceTick(delta) 를 둔다(러너 오브젝트 없이 같은 틱 1회 + 시뮬레이션 시계).
//
// 🔴 어셈블리 주의: Assets/WL/Combat/ 에 .asmdef 를 만들지 말 것.
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
using UnityEngine.SceneManagement;
namespace WL.Combat.Telegraph
{
public sealed class TelegraphRunner : MonoBehaviour
{
static TelegraphRunner s_runner;
static bool s_sceneHooked;
public static bool Exists { get { return s_runner != null; } }
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
static void Boot() { if (WLTelegraphSettings.Enabled) Ensure(); }
public static void Ensure()
{
if (s_runner != null) return;
if (!Application.isPlaying) return; // 에디트 모드에서는 오브젝트를 만들지 않는다(프로브는 ForceTick 을 쓴다)
var go = new GameObject("__WLTelegraph");
go.hideFlags = HideFlags.DontSave;
DontDestroyOnLoad(go);
s_runner = go.AddComponent<TelegraphRunner>();
if (!s_sceneHooked) { SceneManager.activeSceneChanged += OnSceneChanged; s_sceneHooked = true; }
}
static void OnSceneChanged(Scene from, Scene to) { Telegraph.ClearAll(); }
// ── 에디트 모드 시뮬레이션 시계(811gh ImpactRunner 와 같은 방식)
static float s_simNow;
static bool s_simActive;
/// <summary>연출 타이밍의 기준 시각. Play = Time.unscaledTime · 프로브(ForceTick) = 시뮬레이션 시계.</summary>
public static float Now { get { return s_simActive ? s_simNow : Time.unscaledTime; } }
void Update()
{
s_simActive = false;
Telegraph.Tick(Time.unscaledTime);
}
/// <summary>프로브(에디트 모드)용 — 러너 없이 같은 틱을 1회 돌리고 시뮬레이션 시계를 delta 만큼 민다.</summary>
public static void ForceTick(float unscaledDelta)
{
if (!s_simActive) { s_simActive = true; s_simNow = Time.unscaledTime; }
s_simNow += unscaledDelta;
Telegraph.Tick(s_simNow);
}
/// <summary>프로브용 — 시뮬레이션 시계를 켜기만 한다(이벤트를 올리기 전에 기준점을 고정).</summary>
public static void BeginForceClock()
{
if (!s_simActive) { s_simActive = true; s_simNow = Time.unscaledTime; }
}
/// <summary>프로브용 — 시뮬레이션 시계를 끄고 실제 시간으로 되돌린다.</summary>
public static void ResetForceClock() { s_simActive = false; s_simNow = 0f; }
void OnDisable() { Telegraph.RestoreAll(); }
void OnApplicationQuit() { Telegraph.RestoreAll(); }
}
}