using System; using System.Collections; using UnityEngine; namespace TKS { /// 간단한 연출(이동 트윈·돌진·화살·피해 숫자). 코루틴 실행자는 BattleManager. 가상 실행(Sim.Active · 적 AI 예측) 중 = 이동·대기 즉시 · 팝업·터짐 없음. public static class Fx { public static MonoBehaviour Runner; public static Coroutine Run(IEnumerator routine) { if (Sim.Active) { Sim.Run(routine); // 가상 실행 = 그 자리에서 끝까지(병렬 밀기 등) return null; } if (Runner == null) return null; return Runner.StartCoroutine(routine); } public static IEnumerator Wait(float seconds) { if (Sim.Active) yield break; float t = 0f; while (t < seconds) { t += Time.deltaTime; yield return null; } } /// from → to 이동(기본 = 감속 · easeOut false = 등속 → 부딪히는 순간까지 속도를 유지). onDistance 에는 지금까지 지나온 거리를 넘긴다. public static IEnumerator MoveAlong(Transform tr, Vector2 from, Vector2 to, float speed, Action onDistance, bool easeOut = true) { float total = Vector2.Distance(from, to); if (total < 1e-4f) yield break; if (Sim.Active) { tr.position = new Vector3(to.x, to.y, 0f); if (onDistance != null) onDistance(total); yield break; } float duration = Mathf.Max(0.12f, total / Mathf.Max(0.1f, speed)); float el = 0f; while (el < duration) { el += Time.deltaTime; float k = Mathf.Clamp01(el / duration); float e = easeOut ? 1f - (1f - k) * (1f - k) : k; var p = Vector2.Lerp(from, to, e); tr.position = new Vector3(p.x, p.y, 0f); if (onDistance != null) onDistance(e * total); yield return null; } tr.position = new Vector3(to.x, to.y, 0f); if (onDistance != null) onDistance(total); } /// 근접 공격 돌진(짧게 찌르고 복귀). public static IEnumerator Lunge(Unit a, Vector2 toward) { if (Sim.Active) yield break; Vector2 start = a.Position; Vector2 dir = (toward - start); if (dir.sqrMagnitude < 1e-4f) yield break; dir.Normalize(); Vector2 peak = start + dir * 0.3f; yield return MoveAlong(a.transform, start, peak, 6f, null); yield return MoveAlong(a.transform, peak, start, 4f, null); a.Position = start; } public static readonly Color ArrowColor = new Color(1f, 0.95f, 0.75f); public static readonly Color StrategistShotColor = new Color(0.75f, 0.55f, 1f); public static readonly Color SplashColor = new Color(0.7f, 0.45f, 1f, 0.45f); public static readonly Color FriendlyFireColor = new Color(1f, 0.3f, 0.3f); /// 화살(가늘고 긴 사각형) 날리기. public static IEnumerator Arrow(Vector2 from, Vector2 to) { return Arrow(from, to, ArrowColor); } /// 투사체(가늘고 긴 사각형) 날리기 — 궁병 화살 · 책사 계략(색만 다름). public static IEnumerator Arrow(Vector2 from, Vector2 to, Color color) { if (Sim.Active) yield break; var go = new GameObject("Arrow"); var sr = go.AddComponent(); sr.sprite = ProceduralSprites.Square; sr.color = color; sr.sortingOrder = 8; Vector2 d = to - from; float ang = Mathf.Atan2(d.y, d.x) * Mathf.Rad2Deg; go.transform.rotation = Quaternion.Euler(0f, 0f, ang); go.transform.localScale = new Vector3(0.5f, 0.06f, 1f); float dur = Mathf.Max(0.12f, d.magnitude / 14f); float t = 0f; while (t < dur) { t += Time.deltaTime; var p = Vector2.Lerp(from, to, Mathf.Clamp01(t / dur)); go.transform.position = new Vector3(p.x, p.y, 0f); yield return null; } UnityEngine.Object.Destroy(go); } /// 광역 터짐(반지름 radius 원이 짧게 커지며 사라짐 · 책사 광역). public static void Burst(Vector2 center, float radius, Color color) { if (Sim.Active) return; Run(BurstRoutine(center, radius, color)); } static IEnumerator BurstRoutine(Vector2 center, float radius, Color color) { var sr = ProceduralSprites.NewSprite(null, "Burst", ProceduralSprites.Circle, color, radius * 2f, 7); sr.transform.position = new Vector3(center.x, center.y, 0f); float t = 0f; const float dur = 0.35f; while (t < dur) { t += Time.deltaTime; float k = Mathf.Clamp01(t / dur); float s = radius * 2f * (0.6f + 0.4f * k); sr.transform.localScale = new Vector3(s, s, 1f); var c = color; c.a = color.a * (1f - k); sr.color = c; yield return null; } UnityEngine.Object.Destroy(sr.gameObject); } public static void Popup(Vector2 pos, string text, Color color) { if (Sim.Active) return; Run(PopupRoutine(pos, text, color)); } static IEnumerator PopupRoutine(Vector2 pos, string text, Color color) { var tm = TextUtil.CreateWorldText(null, text, 0.42f, color, 20); tm.gameObject.name = "Popup"; var tr = tm.transform; float t = 0f; const float dur = 0.75f; while (t < dur) { t += Time.deltaTime; float k = Mathf.Clamp01(t / dur); tr.position = new Vector3(pos.x, pos.y + 0.55f + 0.7f * k, 0f); var c = color; c.a = 1f - k * k; tm.color = c; yield return null; } UnityEngine.Object.Destroy(tm.gameObject); } } }