ThreeKingdomsSlingshot/Assets/Scripts/Runtime/IbAimArrow.cs

156 lines
7.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
namespace TKS
{
/// <summary>
/// 슬링샷 방향 결정 UI — IB(쿠로네코) GameTouchArrow 이식(PD 2026-11-10 「IB 와 같이 슬링샷 조작 시 방향 결정 UI 등을 그대로 이식」).
/// 프리팹 = Resources/Fx/TKS_Fx_IB_GameArrow(원본 Assets/VFX/IB/AssetResources/Game/GameArrow · 사본 = Tools/make_ib_fx_copies.cs · IB 스크립트 제거).
/// IB 와 같은 동작: 누르면(Ready) 유닛 둘레에 고리 1개 + 퍼지는 고리 3개(3초 동안 0 → 1.3배 · 투명해짐 · 1초 간격 · 반복) ·
/// 당기면 화살표가 쏠 방향으로 돌고(정면 +Y) 당긴 세기만큼 길어짐(가로 0.8 + v × 0.2 · 세로 v · v = 0.5~1.5) · 화살 끝이 전장 밖이면 안으로 밀어 넣음 · 놓으면(End) 숨김.
/// IB 세기 v = 당긴 거리 × 0.6 → 이 게임은 슬링샷 최대 거리 대비 비율로 0.5~1.5 에 맞춘다. 색 = IB 공격 종류 3(하늘색 · 프리팹 기본값).
/// </summary>
public class IbAimArrow : MonoBehaviour
{
public const string PrefabName = "TKS_Fx_IB_GameArrow";
/// <summary>IB GameArrow 공격 종류 3 색(하늘색 · 원본 프리팹 color[2]).</summary>
static readonly Color RingColor = new Color(0.468f, 0.866f, 0.897f, 1f);
const float RingPulseSeconds = 3f;
const float RingPulseScale = 1.3f;
const float RingStagger = 1f;
/// <summary>바닥 고리 정렬(사거리 원 −10·−9 와 같은 바닥 층) · 화살표 = 조준선과 같은 10.</summary>
const int RingOrder = -9;
const int ArrowOrder = 10;
Transform root;
SpriteRenderer arrow;
Transform arrowTip;
SpriteRenderer ring;
SpriteRenderer[] pulses = new SpriteRenderer[0];
float readyTime;
bool ready;
Board board;
public bool Ready
{
get { return ready; }
}
public bool ArrowVisible
{
get { return arrow != null && arrow.gameObject.activeSelf && ready; }
}
public void Init(Board b)
{
board = b;
var prefab = SkillVfx.LoadFx(PrefabName);
if (prefab == null) return;
var go = Instantiate(prefab, transform);
go.name = PrefabName;
root = go.transform;
foreach (var sr in go.GetComponentsInChildren<SpriteRenderer>(true))
{
if (sr.name == "arrow") arrow = sr;
else if (sr.name == "ring01") ring = sr;
}
var list = new System.Collections.Generic.List<SpriteRenderer>();
foreach (var n in new[] { "ring02-01", "ring02-02", "ring02-03" })
{
var t = root.Find(n);
if (t != null && t.GetComponent<SpriteRenderer>() != null) list.Add(t.GetComponent<SpriteRenderer>());
}
pulses = list.ToArray();
int ringOrder = Mathf.RoundToInt(IbUi.Num("order_ground", RingOrder));
if (arrow != null)
{
arrowTip = arrow.transform.Find("object");
arrow.sortingOrder = Mathf.RoundToInt(IbUi.Num("order_arrow", ArrowOrder));
}
if (ring != null)
{
ring.sortingOrder = ringOrder;
ring.color = RingColor;
}
foreach (var p in pulses) p.sortingOrder = ringOrder;
go.SetActive(false);
}
/// <summary>IB Ready: 유닛을 누른 순간 — 고리 표시 · 화살표는 당길 때까지 숨김.</summary>
public void Begin(Unit u)
{
if (root == null || u == null) return;
ready = true;
readyTime = Time.time;
root.gameObject.SetActive(true);
Place(u);
if (arrow != null) arrow.gameObject.SetActive(false);
}
/// <summary>
/// 유닛 크기에 맞춰 놓기 — IB 와 같은 비율(PD 2026-11-10 「IB 와 화살표 크기도 다름」): 배율 = 이 게임 유닛 반지름 / IB 유닛 반지름 0.496(표 ib_ui.csv) ·
/// 그러면 고리 지름 = 반지름 × 5.4 · 화살표 = 반지름 × 5.9~17.8 길이(IB 실측 · 화살 그림 피벗 y 0.3 = 유닛 가운데).
/// </summary>
void Place(Unit u)
{
root.position = new Vector3(u.Position.x, u.Position.y, 0f);
float k = IbUi.Scale(u);
root.localScale = new Vector3(k, k, 1f);
}
/// <summary>
/// 당기는 중: launch = 쏠 방향·거리(월드 · 목표점 − 유닛) — IB SetTouchArrowScale 그대로: IB 월드로 바꾼 당김 × 0.6 = 세기 s(0.5~1.5) ·
/// 가로 0.8 + 0.2 s · 세로 s · 당김이 IB 취소 거리(0.5) 안이면 화살표 숨김(고리만).
/// </summary>
public void Aim(Unit u, Vector2 launch)
{
if (root == null || u == null) return;
if (!ready) Begin(u);
Place(u);
if (arrow == null) return;
float dragIb = launch.magnitude / Mathf.Max(0.001f, IbUi.Scale(u));
bool on = dragIb >= IbUi.Num("arrow_cancel", 0.5f);
if (arrow.gameObject.activeSelf != on) arrow.gameObject.SetActive(on);
if (!on) return;
arrow.transform.localPosition = Vector3.zero;
arrow.transform.rotation = Quaternion.FromToRotation(Vector3.up, new Vector3(launch.x, launch.y, 0f));
float v = Mathf.Clamp(dragIb * IbUi.Num("arrow_drag_k", 0.6f), IbUi.Num("arrow_s_min", 0.5f), IbUi.Num("arrow_s_max", 1.5f));
arrow.transform.localScale = new Vector3(0.8f + v * 0.2f, v, 1f);
// IB LateUpdate: 화살 끝이 벽(전장 밖)을 넘으면 화살 전체를 안으로 밀어 넣음
if (board != null && arrowTip != null)
{
Vector2 tip = arrowTip.position;
Vector2 inside = board.ClampInside(tip, 0f);
if ((inside - tip).sqrMagnitude > 1e-6f) arrow.transform.position += (Vector3)(inside - tip);
}
}
/// <summary>IB End: 놓거나 취소 — 모두 숨김.</summary>
public void End()
{
ready = false;
if (root != null) root.gameObject.SetActive(false);
}
void Update()
{
if (!ready || root == null) return;
float t = Time.time - readyTime;
for (int i = 0; i < pulses.Length; i++)
{
float local = t - i * RingStagger;
var p = pulses[i];
if (local < 0f)
{
p.transform.localScale = Vector3.zero;
continue;
}
float k = (local % RingPulseSeconds) / RingPulseSeconds;
p.transform.localScale = new Vector3(RingPulseScale * k, RingPulseScale * k, 1f);
var c = RingColor;
c.a = 1f - k;
p.color = c;
}
}
}
}