ThreeKingdomsSlingshot/Tools/make_ib_fx_copies.cs

156 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
// IB(쿠로네코) 이펙트 → 게임이 읽는 사본 Resources/Fx/TKS_Fx_IB_*(PD 2026-11-10 「IB 이펙트를 그대로 써서 확인 → 나중에 구조에 맞게 새로 그림(화풍 통일)」).
// 원본 = Tools/wl_vfx_import.py copy … --wl E:/NerdNavis/Kuroneko/KuronekoClient --dest Assets/VFX/IB --skip-scripts 로 옮긴 Assets/VFX/IB/AssetResources/FX/*.prefab(무수정).
// 사본 = IB 전용 스크립트 부품(VisualEffectData 등 · 이 게임에 없음) 제거 · 방향 있는 것은 감싸는 루트를 두고 안쪽을 Z −90°
// (IB 정면 = +Y · FollowUnit = FromToRotation(up, 진행 방향) → 이 게임 b_flat 정면 = +X 에 맞춤).
// 실행: unity command --project-path "E:/NerdNavis/ThreeKingdomsSlingshot" script eval --file Tools/make_ib_fx_copies.cs
string src = "Assets/VFX/IB/AssetResources/";
string dst = "Assets/Resources/Fx/";
// { IB 경로(AssetResources 아래), 사본 이름, 1 = 진행 방향으로 돌림(감싸는 루트) }
var jobs = new[]
{
new[] { "FX/ef_booster", "TKS_Fx_IB_Booster", "1" },
new[] { "FX/ef_speed_tail", "TKS_Fx_IB_SpeedTail", "1" },
new[] { "FX/ef_booster_dust", "TKS_Fx_IB_BoosterDust", "1" },
new[] { "FX/ef_normal_hit", "TKS_Fx_IB_NormalHit", "0" },
new[] { "FX/ef_bounce_hit", "TKS_Fx_IB_BounceHit", "0" },
new[] { "FX/ef_dust", "TKS_Fx_IB_Dust", "0" },
// 방향 결정 UI(PD 2026-11-10 「IB 와 같이 슬링샷 조작 시 방향 결정 UI 등을 그대로 이식」) — 동작은 TKS.IbAimArrow(IB GameTouchArrow 이식)
new[] { "Game/GameArrow", "TKS_Fx_IB_GameArrow", "0" },
// 슬링샷 진행 중 캐릭터 연출(PD 2026-11-10 「선택된 캐릭터 주위 연출 누락」) — IB ShowUserTurnMark(발밑 도는 원 · 몸 둘레 빛) · 조준 중 모든 유닛 속성 방울(SetActiveBarrierVisualEffect)
new[] { "FX/ef_target_circle", "TKS_Fx_IB_TargetCircle", "0" },
new[] { "FX/ef_attack_ready_standard", "TKS_Fx_IB_AttackReady", "0" },
new[] { "FX/ef_attribute_water", "TKS_Fx_IB_Attribute_water", "0" },
new[] { "FX/ef_attribute_tree", "TKS_Fx_IB_Attribute_tree", "0" },
new[] { "FX/ef_attribute_fire", "TKS_Fx_IB_Attribute_fire", "0" },
new[] { "FX/ef_attribute_dark", "TKS_Fx_IB_Attribute_dark", "0" },
new[] { "FX/ef_attribute_glow", "TKS_Fx_IB_Attribute_glow", "0" },
};
AssetDatabase.Refresh();
var sb = new System.Text.StringBuilder();
var scene = EditorSceneManager.NewPreviewScene();
foreach (var j in jobs)
{
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(src + j[0] + ".prefab");
if (prefab == null) { sb.AppendLine("없음 " + j[0]); continue; }
var inst = (GameObject)PrefabUtility.InstantiatePrefab(prefab, scene);
PrefabUtility.UnpackPrefabInstance(inst, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
int removed = 0;
foreach (var t in inst.GetComponentsInChildren<Transform>(true)) removed += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(t.gameObject);
foreach (var r in inst.GetComponentsInChildren<Renderer>(true)) r.sortingLayerID = 0; // IB 정렬 레이어(이 게임에 없음 · <unknown layer>) → Default · 순서는 표 n_order
foreach (var an in inst.GetComponentsInChildren<Animator>(true)) // 클립의 소리 이벤트(FGPlaySoundByString) 받는 곳 — 없으면 재생마다 콘솔 오류
if (an.GetComponent<TKS.IbFxAnimEvents>() == null) an.gameObject.AddComponent<TKS.IbFxAnimEvents>();
GameObject root = inst;
if (j[2] == "1")
{
root = new GameObject(j[1]);
SceneManager.MoveGameObjectToScene(root, scene);
inst.transform.SetParent(root.transform, false);
inst.transform.localPosition = Vector3.zero;
inst.transform.localRotation = Quaternion.Euler(0f, 0f, -90f);
}
else root.name = j[1];
PrefabUtility.SaveAsPrefabAsset(root, dst + j[1] + ".prefab");
Object.DestroyImmediate(root);
var rs = AssetDatabase.LoadAssetAtPath<GameObject>(dst + j[1] + ".prefab").GetComponentsInChildren<Renderer>(true);
var layers = new System.Collections.Generic.HashSet<string>();
foreach (var r in rs) layers.Add(r.sortingLayerName + "/" + r.sortingOrder);
sb.AppendLine(j[1] + " · 스크립트 부품 제거 " + removed + " · 렌더러 " + rs.Length + " · 정렬 " + string.Join(",", layers));
}
EditorSceneManager.ClosePreviewScene(scene);
// ── 진영색 사본(PD 2026-11-10 「진영 색을 입히고」): TKS_Fx_IB_Booster_<진영> · TKS_Fx_IB_SpeedTail_<진영> ──
// 색 = 채도 있는 색만 색상(H)을 진영색으로(채도·밝기 유지 · 흰 심 그대로) · 꼬리 그림 = Tools/make_ib_camp_textures.py 가 만든 Assets/VFX/IB/_TKS/Tail_Basic02_<진영>.png
// 진영색 원본 = Tools/dash2d_camps.csv
Color HueTo(Color c, float hue)
{
float h, s, v;
Color.RGBToHSV(c, out h, out s, out v);
if (s < 0.15f) return c;
var n = Color.HSVToRGB(hue, s, v);
n.a = c.a;
return n;
}
Gradient GradHue(Gradient g, float hue)
{
if (g == null) return null;
var ks = g.colorKeys;
for (int i = 0; i < ks.Length; i++) ks[i].color = HueTo(ks[i].color, hue);
var ng = new Gradient();
ng.SetKeys(ks, g.alphaKeys);
ng.mode = g.mode;
return ng;
}
ParticleSystem.MinMaxGradient MmgHue(ParticleSystem.MinMaxGradient m, float hue)
{
switch (m.mode)
{
case ParticleSystemGradientMode.Color: return new ParticleSystem.MinMaxGradient(HueTo(m.color, hue));
case ParticleSystemGradientMode.TwoColors: return new ParticleSystem.MinMaxGradient(HueTo(m.colorMin, hue), HueTo(m.colorMax, hue));
case ParticleSystemGradientMode.TwoGradients: return new ParticleSystem.MinMaxGradient(GradHue(m.gradientMin, hue), GradHue(m.gradientMax, hue));
default:
var r = new ParticleSystem.MinMaxGradient(GradHue(m.gradient, hue));
r.mode = m.mode;
return r;
}
}
string tks = "Assets/VFX/IB/_TKS/";
var tailMat0 = AssetDatabase.LoadAssetAtPath<Material>("Assets/VFX/IB/FGResources/Effect/Material/Tail_Basic02.mat");
foreach (var line in System.IO.File.ReadAllLines("Tools/dash2d_camps.csv"))
{
var cells = line.Trim('').Split(',');
if (cells.Length < 2 || cells[0] == "s_camp" || cells[0].Length == 0) continue;
string camp = cells[0].Trim();
Color cc;
ColorUtility.TryParseHtmlString(cells[1].Trim(), out cc);
float hue, cs, cv;
Color.RGBToHSV(cc, out hue, out cs, out cv);
var tp = tks + "Tail_Basic02_" + camp + ".png";
var ti = (TextureImporter)AssetImporter.GetAtPath(tp);
ti.wrapMode = TextureWrapMode.Clamp;
ti.mipmapEnabled = false;
ti.alphaIsTransparency = true;
ti.SaveAndReimport();
var mp = tks + "Tail_Basic02_" + camp + ".mat";
var mat = AssetDatabase.LoadAssetAtPath<Material>(mp);
if (mat == null)
{
mat = new Material(tailMat0);
AssetDatabase.CreateAsset(mat, mp);
}
mat.CopyPropertiesFromMaterial(tailMat0);
mat.SetTexture("_MainTex", AssetDatabase.LoadAssetAtPath<Texture2D>(tp));
EditorUtility.SetDirty(mat);
var b = PrefabUtility.LoadPrefabContents(dst + "TKS_Fx_IB_Booster.prefab");
b.name = "TKS_Fx_IB_Booster_" + camp;
foreach (var sr in b.GetComponentsInChildren<SpriteRenderer>(true)) sr.color = HueTo(sr.color, hue);
PrefabUtility.SaveAsPrefabAsset(b, dst + b.name + ".prefab");
PrefabUtility.UnloadPrefabContents(b);
var t = PrefabUtility.LoadPrefabContents(dst + "TKS_Fx_IB_SpeedTail.prefab");
t.name = "TKS_Fx_IB_SpeedTail_" + camp;
foreach (var tr in t.GetComponentsInChildren<TrailRenderer>(true))
{
tr.sharedMaterial = mat;
tr.colorGradient = GradHue(tr.colorGradient, hue);
}
foreach (var ps in t.GetComponentsInChildren<ParticleSystem>(true))
{
var m = ps.main;
m.startColor = MmgHue(m.startColor, hue);
var col = ps.colorOverLifetime;
if (col.enabled) col.color = MmgHue(col.color, hue);
}
PrefabUtility.SaveAsPrefabAsset(t, dst + t.name + ".prefab");
PrefabUtility.UnloadPrefabContents(t);
sb.AppendLine("진영색 " + camp + " " + cells[1].Trim() + " → TKS_Fx_IB_Booster_" + camp + " · TKS_Fx_IB_SpeedTail_" + camp);
}
AssetDatabase.SaveAssets();
return sb.ToString();