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(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(true)) removed += GameObjectUtility.RemoveMonoBehavioursWithMissingScript(t.gameObject); foreach (var r in inst.GetComponentsInChildren(true)) r.sortingLayerID = 0; // IB 정렬 레이어(이 게임에 없음 · ) → Default · 순서는 표 n_order foreach (var an in inst.GetComponentsInChildren(true)) // 클립의 소리 이벤트(FGPlaySoundByString) 받는 곳 — 없으면 재생마다 콘솔 오류 if (an.GetComponent() == null) an.gameObject.AddComponent(); 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(dst + j[1] + ".prefab").GetComponentsInChildren(true); var layers = new System.Collections.Generic.HashSet(); 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("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(mp); if (mat == null) { mat = new Material(tailMat0); AssetDatabase.CreateAsset(mat, mp); } mat.CopyPropertiesFromMaterial(tailMat0); mat.SetTexture("_MainTex", AssetDatabase.LoadAssetAtPath(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(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(true)) { tr.sharedMaterial = mat; tr.colorGradient = GradHue(tr.colorGradient, hue); } foreach (var ps in t.GetComponentsInChildren(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();