165 lines
6.5 KiB
C#
165 lines
6.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
|
|
namespace TKS
|
|
{
|
|
/// <summary>간단한 연출(이동 트윈·돌진·화살·피해 숫자). 코루틴 실행자는 BattleManager. 가상 실행(Sim.Active · 적 AI 예측) 중 = 이동·대기 즉시 · 팝업·터짐 없음.</summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>from → to 이동(기본 = 감속 · easeOut false = 등속 → 부딪히는 순간까지 속도를 유지). onDistance 에는 지금까지 지나온 거리를 넘긴다.</summary>
|
|
public static IEnumerator MoveAlong(Transform tr, Vector2 from, Vector2 to, float speed, Action<float> 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);
|
|
}
|
|
|
|
/// <summary>근접 공격 돌진(짧게 찌르고 복귀).</summary>
|
|
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);
|
|
|
|
/// <summary>화살(가늘고 긴 사각형) 날리기.</summary>
|
|
public static IEnumerator Arrow(Vector2 from, Vector2 to)
|
|
{
|
|
return Arrow(from, to, ArrowColor);
|
|
}
|
|
|
|
/// <summary>투사체(가늘고 긴 사각형) 날리기 — 궁병 화살 · 책사 계략(색만 다름).</summary>
|
|
public static IEnumerator Arrow(Vector2 from, Vector2 to, Color color)
|
|
{
|
|
if (Sim.Active) yield break;
|
|
var go = new GameObject("Arrow");
|
|
var sr = go.AddComponent<SpriteRenderer>();
|
|
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);
|
|
}
|
|
|
|
/// <summary>광역 터짐(반지름 radius 원이 짧게 커지며 사라짐 · 책사 광역).</summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|