Project_WL/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs

174 lines
11 KiB
C#
Raw Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLSkillTestSceneBuilder.cs — 독립 스킬 테스트 씬(Assets/WL/SkillTest/WLSkillTest.unity)을 코드로 조립·저장 (PD #947 · 2026-10-20 재지시판)
//
// ■ 들어가는 것(= 원본 StatTest 하네스 구성 + WL 치비·더미·평지) — 로그인·HUD·섬 없음
// Directional Light · Ground(Plane · URP Lit 재질 자산 · 하네스가 런타임 NavMesh 베이크) · Mgr(AddrResourceMgr·CoroutineMgr·TimeMgr) · EventSystem
// · ResWork/UIPrefabs/Title/{Tables, SoundInfo, SortOrder_5(OptionInfo·Popup), ActorInfo}.prefab
// · ResWork/Prefabs/Ingame/{InGameInfo(이펙트·투사체 풀), ProjectileInfo, Main Camera(RealCamera)}.prefab
// · PCPos/치비 PC 프리팹(WLCharacterSwapSettings.pcPrefabName · 없으면 Ai01) · MobPos/더미 몹 프리팹(Res_Addr/Mobs/WL/RPG_Bat)
// · Canvas + 글자 4(원본 Set_Dmglabel 이 쓰는 피해 로그) · WLSkillTestHarness(actors · labels · pcActor · dummyActor)
// ■ 메뉴 WL/Skill Test/Build Scene · CLI: 먼저 CloseTestScene() 또는 Title 을 연 뒤 Build()
// ─────────────────────────────────────────────────────────────────────────────
using System.Text;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace WL.SkillTest.Editor
{
public static class WLSkillTestSceneBuilder
{
public const string Folder = "Assets/WL/SkillTest";
public const string ScenePath = Folder + "/WLSkillTest.unity";
public const string GroundMatPath = Folder + "/WLSkillTestGround.mat";
static readonly string[] TitlePrefabs = { "Tables", "SoundInfo", "SortOrder_5", "ActorInfo" };
const string TitlePrefabFolder = "Assets/ResWork/UIPrefabs/Title/";
static readonly string[] IngamePrefabs = { "InGameInfo", "ProjectileInfo", "Main Camera" };
const string IngamePrefabFolder = "Assets/ResWork/Prefabs/Ingame/";
const string PcPrefabFolder = "Assets/Res_Addr/PC/";
const string DummyPrefab = "Assets/Res_Addr/Mobs/WL/RPG_Bat.prefab";
[MenuItem("WL/Skill Test/Build Scene (다시 만들기)")]
public static void BuildMenu() { Debug.Log(Build()); }
[MenuItem("WL/Skill Test/Open Scene")]
public static void OpenMenu() { Debug.Log(Open()); }
public static string Open()
{
var cur = SceneManager.GetActiveScene();
if (cur.path == ScenePath) return "열림 " + ScenePath;
if (cur.isDirty) return "현재 씬 " + cur.name + " 이 저장되지 않음 — 저장 없이 다시 열거나 저장한 뒤 다시";
if (!System.IO.File.Exists(ScenePath)) return "씬 없음 " + ScenePath + " — Build() 먼저";
EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
return "열림 " + ScenePath;
}
static bool IsTestSceneOpen()
{
for (int i = 0; i < SceneManager.sceneCount; i++) if (SceneManager.GetSceneAt(i).path == ScenePath) return true;
return false;
}
/// <summary>테스트 씬이 열려 있으면 저장 없이 빈 씬으로(다시 만들기 전 · 저장 안 된 변경이 있으면 거절).</summary>
public static string CloseTestScene()
{
if (Application.isPlaying) return "Play 중";
if (!IsTestSceneOpen()) return "테스트 씬 안 열림";
var a = SceneManager.GetActiveScene();
if (a.path == ScenePath && a.isDirty) return "테스트 씬이 저장되지 않음";
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
return "빈 씬으로 바꿈";
}
static string PcPrefabPath()
{
var s = WL.Character.WLCharacterSwapSettings.Instance;
string name = s != null && !string.IsNullOrEmpty(s.pcPrefabName) ? s.pcPrefabName : "Ai01";
string p = PcPrefabFolder + name + ".prefab";
if (AssetDatabase.LoadAssetAtPath<GameObject>(p) == null) p = PcPrefabFolder + "Ai01.prefab";
return p;
}
static TextMeshProUGUI MakeLabel(Transform canvas, string name, Vector2 anchor, Vector2 pos, Vector2 size, TextAlignmentOptions align)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(canvas, false);
var rt = go.GetComponent<RectTransform>();
rt.anchorMin = rt.anchorMax = rt.pivot = anchor; rt.anchoredPosition = pos; rt.sizeDelta = size;
var t = go.AddComponent<TextMeshProUGUI>();
t.fontSize = 16; t.alignment = align; t.color = Color.white; t.text = "";
return t;
}
public static string Build()
{
if (Application.isPlaying) return "Play 중에는 만들지 않는다";
if (IsTestSceneOpen()) return "테스트 씬이 열려 있음 — 먼저 CloseTestScene() 또는 Title 을 연 뒤 Build()";
var sb = new StringBuilder();
var prevActive = SceneManager.GetActiveScene();
if (!AssetDatabase.IsValidFolder(Folder)) AssetDatabase.CreateFolder("Assets/WL", "SkillTest");
var cfg = WLSkillTestHarness.Cfg;
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
SceneManager.SetActiveScene(scene);
var lightGo = new GameObject("Directional Light");
var light = lightGo.AddComponent<Light>();
light.type = LightType.Directional; light.intensity = 1.1f; light.shadows = LightShadows.Soft;
lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
var ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
ground.name = "Ground";
float size = cfg != null && cfg.groundSize > 1f ? cfg.groundSize : 100f;
ground.transform.localScale = new Vector3(size / 10f, 1f, size / 10f);
var mat = AssetDatabase.LoadAssetAtPath<Material>(GroundMatPath);
if (mat == null) { var sh = Shader.Find("Universal Render Pipeline/Lit"); mat = new Material(sh != null ? sh : Shader.Find("Standard")); AssetDatabase.CreateAsset(mat, GroundMatPath); }
var gc = cfg != null && cfg.groundColor != null && cfg.groundColor.Length >= 3 ? new Color(cfg.groundColor[0], cfg.groundColor[1], cfg.groundColor[2]) : new Color(0.45f, 0.62f, 0.35f);
if (mat.HasProperty("_BaseColor")) mat.SetColor("_BaseColor", gc); else mat.color = gc;
EditorUtility.SetDirty(mat);
ground.GetComponent<Renderer>().sharedMaterial = mat;
GameObjectUtility.SetStaticEditorFlags(ground, StaticEditorFlags.BatchingStatic);
var mgr = new GameObject("Mgr"); mgr.AddComponent<AddrResourceMgr>(); mgr.AddComponent<CoroutineMgr>(); mgr.AddComponent<TimeMgr>();
var es = new GameObject("EventSystem"); es.AddComponent<EventSystem>(); es.AddComponent<StandaloneInputModule>();
foreach (var n in TitlePrefabs)
{
var p = AssetDatabase.LoadAssetAtPath<GameObject>(TitlePrefabFolder + n + ".prefab");
if (p == null) { sb.Append("🔴 프리팹 없음 ").Append(n).Append('\n'); continue; }
var go = PrefabUtility.InstantiatePrefab(p, scene) as GameObject; if (go != null) go.name = n;
}
foreach (var n in IngamePrefabs)
{
var p = AssetDatabase.LoadAssetAtPath<GameObject>(IngamePrefabFolder + n + ".prefab");
if (p == null) { sb.Append("🔴 프리팹 없음 ").Append(n).Append('\n'); continue; }
var go = PrefabUtility.InstantiatePrefab(p, scene) as GameObject; if (go != null) go.name = n;
}
// PC(치비) · 더미 — 씬에 직접(StatTest 의 PCPos/MobPos 와 같은 꼴)
Vector3 spawn = cfg != null && cfg.spawnPos != null && cfg.spawnPos.Length >= 3 ? new Vector3(cfg.spawnPos[0], cfg.spawnPos[1], cfg.spawnPos[2]) : Vector3.zero;
var pcPos = new GameObject("PCPos"); pcPos.transform.position = spawn;
var pcPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(PcPrefabPath());
Actor pcActor = null;
if (pcPrefab != null) { var pc = PrefabUtility.InstantiatePrefab(pcPrefab, scene) as GameObject; pc.transform.SetParent(pcPos.transform, false); pcActor = pc.GetComponent<Actor>(); }
else sb.Append("🔴 PC 프리팹 없음\n");
var mobPos = new GameObject("MobPos"); mobPos.transform.position = spawn + new Vector3(2f, 0f, -1.5f);
var mobPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(DummyPrefab);
MobActor mob = null;
if (mobPrefab != null) { var m = PrefabUtility.InstantiatePrefab(mobPrefab, scene) as GameObject; m.transform.SetParent(mobPos.transform, false); mob = m.GetComponent<MobActor>(); }
else sb.Append("🔴 더미 프리팹 없음 " + DummyPrefab + "\n");
if (pcActor == null) sb.Append("🔴 PC 프리팹에 Actor 없음\n");
if (mob == null) sb.Append("🔴 더미 프리팹에 MobActor 없음\n");
// 글자 4(원본 분기 Set_Dmglabel 이 labels[2] 에 피해를 적는다)
var canvasGo = new GameObject("WLSkillTestCanvas");
var canvas = canvasGo.AddComponent<Canvas>(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; canvas.sortingOrder = 50;
canvasGo.AddComponent<CanvasScaler>(); canvasGo.AddComponent<GraphicRaycaster>();
var labels = new TextMeshProUGUI[4];
labels[0] = MakeLabel(canvasGo.transform, "pcstat", new Vector2(0f, 0f), new Vector2(10f, 10f), new Vector2(360f, 220f), TextAlignmentOptions.BottomLeft);
labels[1] = MakeLabel(canvasGo.transform, "mobstat", new Vector2(1f, 0f), new Vector2(-10f, 10f), new Vector2(360f, 220f), TextAlignmentOptions.BottomRight);
labels[2] = MakeLabel(canvasGo.transform, "dmg", new Vector2(1f, 1f), new Vector2(-10f, -10f), new Vector2(420f, 300f), TextAlignmentOptions.TopRight);
labels[3] = MakeLabel(canvasGo.transform, "classinfo", new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(600f, 40f), TextAlignmentOptions.Bottom);
var hGo = new GameObject("WLSkillTestHarness");
var h = hGo.AddComponent<WLSkillTestHarness>();
h.pcActor = pcActor; h.dummyActor = mob;
h.actors = new Actor[] { pcActor, mob };
h.labels = labels;
bool saved = EditorSceneManager.SaveScene(scene, ScenePath);
sb.Append(saved ? "저장 " : "🔴 저장 실패 ").Append(ScenePath).Append(" · 루트 ").Append(scene.rootCount).Append(" · PC ").Append(PcPrefabPath()).Append('\n');
if (prevActive.IsValid() && prevActive.isLoaded) SceneManager.SetActiveScene(prevActive);
EditorSceneManager.CloseScene(scene, true);
AssetDatabase.SaveAssets();
return sb.ToString();
}
}
}