ThreeKingdomsSlingshot/Assets/Scripts/Runtime/IbUi.cs

153 lines
5.5 KiB
C#
Raw Permalink Normal View History

스킬 이펙트 1차 + IB(쿠로네코) 연출 시험 — 돌진·조준 UI·타격감 (PD 2026-11-10) - 기본 공격(돌진) = IB 이펙트 그대로 시험: 앞 빛 물결 · 속도 꼬리(진영색 4색 · 멈춘 자리에서 0.12초 흐려짐) · WL 흙먼지 · 기준점 dash(아래로 갈수록 발 쪽) · 히트 이펙트 제거(PD 지정 대기) - 슬링샷 방향 결정 UI = IB GameTouchArrow 이식(IbAimArrow · IB 유닛 반지름 0.496 기준 비율) + 진행 중 캐릭터 연출(IbUnitMarks: 발밑 원 · 몸 빛 · 조준 중 속성 방울) · 값 표 Resources/Skills/ib_ui.csv - 기병: 복귀 영역(채운 원 · PreviewCavalryReturn) · 복귀 때 돌아서기(UnitView.TurnAround) - 타격감: HitFeel + CameraController.Shake · 표 hit_feel.csv(IB 흔들림 값은 디컴파일에 없어 추정값) - 액티브 스킬 1차 연출(Fable 제안 56 스킬 · 사본 Resources/Fx/TKS_Fx_Sk_*) · SkillVfx 확장(s_camp · f_fwd · f_drift · f_fade · s_over · b_flat · 트레일 페이드) · 셀화 셰이더 포트 - 2D 돌진 시험본(Dash2D · Aura2D test_aura) · 이전 시험 행은 skill_vfx.csv 에 # 주석 - 여포 크기: dz_boss_lvbu 몸 높이 819 → 718(깃털 제외 · 말 탄 유닛 머리 끝 상한 때문에 작아졌음) - 원본 = Assets/VFX/IB(쿠로네코 · GUID 유지 · Tools/wl_vfx_import.py --skip-scripts) · 사본 도구 Tools/make_ib_fx_copies.cs · make_ib_camp_textures.py · build_dash2d.cs - 테스트 PlayMode 118/118 통과 · 자동 재직렬화(png meta · UITheme · ProjectSettings) 제외 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 04:19:44 +00:00
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;
namespace TKS
{
/// <summary>
/// IB(쿠로네코) 슬링샷 UI 이식 값 표(Resources/Skills/ib_ui.csv · C45) + 진행 중 캐릭터 연출(IbUnitMarks)이 쓰는 공용 도우미.
/// </summary>
public static class IbUi
{
public const string TablePath = "Skills/ib_ui";
static Dictionary<string, string> values;
public static void Reload()
{
values = null;
}
static void Ensure()
{
if (values != null) return;
values = new Dictionary<string, string>();
var ta = Resources.Load<TextAsset>(TablePath);
if (ta == null) return;
bool head = true;
foreach (var raw in ta.text.Replace("\r", "").Split('\n'))
{
var line = raw.TrimStart('');
if (line.Trim().Length == 0 || line.TrimStart().StartsWith("#")) continue;
if (head)
{
head = false;
continue;
}
var c = SkillVfx.SplitCsv(line);
if (c.Length >= 2) values[c[0].Trim()] = c[1].Trim();
}
}
public static string Str(string key, string def)
{
Ensure();
string v;
return values.TryGetValue(key, out v) && v.Length > 0 ? v : def;
}
public static float Num(string key, float def)
{
float f;
return float.TryParse(Str(key, ""), NumberStyles.Float, CultureInfo.InvariantCulture, out f) ? f : def;
}
/// <summary>IB 이펙트·UI 배율 = 이 게임 유닛 반지름 / IB 유닛 반지름.</summary>
public static float Scale(Unit u)
{
return u == null ? 1f : u.Radius / Mathf.Max(0.01f, Num("ib_unit_radius", 0.5f));
}
/// <summary>진영 → IB 속성(water · tree · fire · dark · glow).</summary>
public static string Element(Unit u)
{
string camp = u != null && !string.IsNullOrEmpty(u.Camp) ? u.Camp.ToLowerInvariant() : "none";
return Str("element_" + camp, Str("element_none", "dark"));
}
/// <summary>IB 사본 하나 띄우기(유닛에 붙임 · 배율 · 렌더러 정렬).</summary>
public static GameObject Attach(string fxName, Unit u, Vector2 local, float scale, int order)
{
var prefab = SkillVfx.LoadFx(fxName);
if (prefab == null || u == null) return null;
var go = Object.Instantiate(prefab, u.transform);
go.name = fxName;
go.transform.localPosition = new Vector3(local.x, local.y, 0f);
go.transform.localRotation = Quaternion.identity;
var ls = u.transform.lossyScale;
go.transform.localScale = new Vector3(scale / Mathf.Max(0.001f, Mathf.Abs(ls.x)), scale / Mathf.Max(0.001f, Mathf.Abs(ls.y)), 1f);
foreach (var r in go.GetComponentsInChildren<Renderer>(true)) r.sortingOrder = order;
return go;
}
}
/// <summary>
/// 슬링샷 진행 중 캐릭터 연출 — IB 이식(PD 2026-11-10 「선택된 캐릭터 주위 연출이 누락」):
/// 행동할 유닛(선택된 아군 · 행동 가능) = 발밑 도는 원(ef_target_circle · IB BottomPosition) + 몸 둘레 빛(ef_attack_ready_standard · IB CenterPosition) — IB ShowUserTurnMark ·
/// 조준 중(누름 ~ 놓음) = 모든 유닛에 속성 방울(ef_attribute_&lt;속성&gt; · 배율 0.9) — IB SetActiveBarrierVisualEffect.
/// </summary>
public class IbUnitMarks : MonoBehaviour
{
Unit turnUnit;
readonly List<GameObject> turnFx = new List<GameObject>();
readonly List<GameObject> bubbles = new List<GameObject>();
public int BubbleCount
{
get { return bubbles.Count; }
}
public bool TurnMarkVisible
{
get { return turnFx.Count > 0; }
}
public void ShowTurn(Unit u)
{
if (u == turnUnit && turnFx.Count > 0) return;
Clear(turnFx);
turnUnit = u;
if (u == null) return;
float k = IbUi.Scale(u);
int order = Mathf.RoundToInt(IbUi.Num("order_ground", -8f));
Add(turnFx, IbUi.Attach("TKS_Fx_IB_TargetCircle", u, new Vector2(0f, -u.Radius * 0.55f), k, order)); // 발(아트 발 = 원 아래쪽 −0.55 R)
Add(turnFx, IbUi.Attach("TKS_Fx_IB_AttackReady", u, Vector2.zero, k, order));
}
public void ShowBubbles(IEnumerable<Unit> units)
{
Clear(bubbles);
if (units == null) return;
int order = Mathf.RoundToInt(IbUi.Num("order_bubble", 12f));
float bs = IbUi.Num("barrier_scale", 0.9f);
foreach (var u in units)
{
if (u == null || !u.IsAlive) continue;
Add(bubbles, IbUi.Attach("TKS_Fx_IB_Attribute_" + IbUi.Element(u), u, Vector2.zero, IbUi.Scale(u) * bs, order));
}
}
public void HideBubbles()
{
Clear(bubbles);
}
static void Add(List<GameObject> list, GameObject go)
{
if (go != null) list.Add(go);
}
static void Clear(List<GameObject> list)
{
foreach (var g in list)
if (g != null) Destroy(g);
list.Clear();
}
void OnDestroy()
{
Clear(turnFx);
Clear(bubbles);
}
}
}