131 lines
6.4 KiB
C#
131 lines
6.4 KiB
C#
|
|
using System.IO;
|
||
|
|
using System.Text;
|
||
|
|
using UnityEditor;
|
||
|
|
using UnityEditor.SceneManagement;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
namespace TKS.EditorTools
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 스킬 이펙트 작업 도구(2026-11-09 PD 「WL 방식과 동일하게 유닛별 스킬 구현 · 테스트용 씬」).
|
||
|
|
/// ① 스킬 테스트 씬 만들기/열기 ② WL 에서 옮겨 온 이펙트(Assets/VFX) 재질 검사(분홍 · 빠진 스크립트)
|
||
|
|
/// ③ 고른 팩 프리팹 → 스킬 이펙트 사본 Resources/Fx/TKS_Fx_<이름>.prefab(원본 팩 무수정 · WL 의 WL_Fx_* 사본 규칙).
|
||
|
|
/// </summary>
|
||
|
|
public static class SkillTestTools
|
||
|
|
{
|
||
|
|
public const string ScenePath = "Assets/Scenes/SkillTest.unity";
|
||
|
|
public const string FxCopyFolder = "Assets/Resources/Fx";
|
||
|
|
|
||
|
|
/// <summary>씬을 새로 만든다(열린 씬은 건드리지 않음 · 추가로 열었다 저장 후 닫음 · 대화 상자 없음).</summary>
|
||
|
|
[MenuItem("TKS/Skill Test/Build Scene (스킬 테스트 씬 만들기)")]
|
||
|
|
public static void BuildScene()
|
||
|
|
{
|
||
|
|
var open = EditorSceneManager.GetSceneByPath(ScenePath);
|
||
|
|
if (open.IsValid() && open.isLoaded)
|
||
|
|
{
|
||
|
|
Debug.LogWarning("[TKS] 스킬 테스트 씬이 열려 있어 다시 만들지 않습니다(다른 씬을 연 뒤 다시).");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
|
||
|
|
var cam = new GameObject("Main Camera");
|
||
|
|
cam.tag = "MainCamera";
|
||
|
|
var c = cam.AddComponent<Camera>();
|
||
|
|
c.orthographic = true;
|
||
|
|
cam.transform.position = new Vector3(0f, 0f, -10f);
|
||
|
|
EditorSceneManager.MoveGameObjectToScene(cam, scene);
|
||
|
|
var go = new GameObject("SkillTestBootstrap");
|
||
|
|
var boot = go.AddComponent<SkillTestBootstrap>();
|
||
|
|
boot.Config = AssetDatabase.LoadAssetAtPath<BalanceConfig>("Assets/Resources/BalanceConfig.asset");
|
||
|
|
EditorSceneManager.MoveGameObjectToScene(go, scene);
|
||
|
|
Directory.CreateDirectory(Path.GetDirectoryName(ScenePath));
|
||
|
|
EditorSceneManager.SaveScene(scene, ScenePath);
|
||
|
|
EditorSceneManager.CloseScene(scene, true);
|
||
|
|
AssetDatabase.Refresh();
|
||
|
|
Debug.Log("[TKS] 스킬 테스트 씬 저장: " + ScenePath);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MenuItem("TKS/Skill Test/Open Scene (스킬 테스트 씬 열기)")]
|
||
|
|
public static void OpenScene()
|
||
|
|
{
|
||
|
|
if (!File.Exists(ScenePath)) BuildScene();
|
||
|
|
if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Assets/VFX 재질(셰이더 없음 · 오류 셰이더 · 미지원 · 컴파일 오류)과 빠진 스크립트 프리팹을 센다. 반환 = 보고 문자열.</summary>
|
||
|
|
[MenuItem("TKS/VFX/Check Imported VFX (이펙트 재질 검사)")]
|
||
|
|
public static string CheckVfx()
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
int total = 0, bad = 0;
|
||
|
|
foreach (var g in AssetDatabase.FindAssets("t:Material", new[] { SkillTestBootstrap.VfxRoot }))
|
||
|
|
{
|
||
|
|
var p = AssetDatabase.GUIDToAssetPath(g);
|
||
|
|
var m = AssetDatabase.LoadAssetAtPath<Material>(p);
|
||
|
|
total++;
|
||
|
|
string why = null;
|
||
|
|
if (m == null) why = "읽기 실패";
|
||
|
|
else if (m.shader == null) why = "셰이더 없음";
|
||
|
|
else if (m.shader.name == "Hidden/InternalErrorShader") why = "오류 셰이더";
|
||
|
|
else if (!m.shader.isSupported) why = "미지원 " + m.shader.name;
|
||
|
|
else if (ShaderUtil.ShaderHasError(m.shader)) why = "컴파일 오류 " + m.shader.name;
|
||
|
|
if (why == null) continue;
|
||
|
|
bad++;
|
||
|
|
if (bad <= 20) sb.AppendLine(" 재질 " + why + " · " + p);
|
||
|
|
}
|
||
|
|
int prefabs = 0, missing = 0;
|
||
|
|
foreach (var g in AssetDatabase.FindAssets("t:Prefab", new[] { SkillTestBootstrap.VfxRoot }))
|
||
|
|
{
|
||
|
|
var p = AssetDatabase.GUIDToAssetPath(g);
|
||
|
|
var go = AssetDatabase.LoadAssetAtPath<GameObject>(p);
|
||
|
|
if (go == null) continue;
|
||
|
|
prefabs++;
|
||
|
|
foreach (var comp in go.GetComponentsInChildren<Component>(true))
|
||
|
|
{
|
||
|
|
if (comp != null) continue;
|
||
|
|
missing++;
|
||
|
|
if (missing <= 20) sb.AppendLine(" 빠진 스크립트 · " + p);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
string head = "[TKS] VFX 검사: 재질 " + total + " · 문제 " + bad + " · 프리팹 " + prefabs + " · 빠진 스크립트 " + missing;
|
||
|
|
Debug.Log(head + "\n" + sb);
|
||
|
|
return head + "\n" + sb;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>Project 창에서 고른 팩 프리팹 → Resources/Fx/TKS_Fx_<이름>.prefab 사본(이미 있으면 건너뜀). 스킬 이펙트 표 s_fx 에 그 이름을 쓴다.</summary>
|
||
|
|
[MenuItem("TKS/VFX/Make Skill Fx Copy (선택 프리팹 → 스킬 이펙트 사본)")]
|
||
|
|
public static void CopySelected()
|
||
|
|
{
|
||
|
|
foreach (var o in Selection.objects)
|
||
|
|
{
|
||
|
|
var p = AssetDatabase.GetAssetPath(o);
|
||
|
|
if (!p.EndsWith(".prefab")) continue;
|
||
|
|
MakeCopy(p, null);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>팩 프리팹 사본 만들기(이름 = TKS_Fx_ + name 또는 원본 이름) · 반환 = 사본 경로.</summary>
|
||
|
|
public static string MakeCopy(string srcPath, string name)
|
||
|
|
{
|
||
|
|
if (!Directory.Exists(FxCopyFolder)) Directory.CreateDirectory(FxCopyFolder);
|
||
|
|
string n = string.IsNullOrEmpty(name) ? Path.GetFileNameWithoutExtension(srcPath) : name;
|
||
|
|
n = n.Replace(' ', '_');
|
||
|
|
if (!n.StartsWith("TKS_Fx_")) n = "TKS_Fx_" + n;
|
||
|
|
string dst = FxCopyFolder + "/" + n + ".prefab";
|
||
|
|
if (File.Exists(dst))
|
||
|
|
{
|
||
|
|
Debug.Log("[TKS] 사본 이미 있음: " + dst);
|
||
|
|
return dst;
|
||
|
|
}
|
||
|
|
AssetDatabase.Refresh();
|
||
|
|
if (!AssetDatabase.CopyAsset(srcPath, dst))
|
||
|
|
{
|
||
|
|
Debug.LogError("[TKS] 사본 실패: " + srcPath + " → " + dst);
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
Debug.Log("[TKS] 스킬 이펙트 사본: " + srcPath + " → " + dst);
|
||
|
|
return dst;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|