diff --git a/Assets/WL/SkillTest.meta b/Assets/WL/SkillTest.meta
new file mode 100644
index 000000000..6bb595f57
--- /dev/null
+++ b/Assets/WL/SkillTest.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fbf99ff76a4410847858e3565d85eea1
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/Editor.meta b/Assets/WL/SkillTest/Editor.meta
new file mode 100644
index 000000000..cf1706cff
--- /dev/null
+++ b/Assets/WL/SkillTest/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fe25a56d1c4ad2845b39d29dcaac17ec
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs b/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs
new file mode 100644
index 000000000..71a7d0e2b
--- /dev/null
+++ b/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs
@@ -0,0 +1,158 @@
+// ─────────────────────────────────────────────────────────────────────────────
+// WLSkillTestSceneBuilder.cs — 테스트 씬(Assets/WL/SkillTest/WLSkillTest.unity)을 코드로 조립·저장한다 (PD #947 · 2026-10-19)
+//
+// ■ 왜 코드로: 씬에 무엇이 들어가는지가 글로 남고, 깨지면 메뉴 한 번으로 다시 만든다(에디터 손작업 0 · 열린 씬(Title)은 건드리지 않는다).
+// ■ 들어가는 것 (= Title 이 Addressables 로 올리던 매니저 + InGame 씬 루트 + 원본 카메라 프리팹 + 평지 + 부트)
+// Directional Light · Ground(Plane · URP Lit 재질 자산 · 부트가 런타임 NavMesh 베이크) · Mgr(AddrResourceMgr·CoroutineMgr·TimeMgr) · EventSystem
+// · ResWork/UIPrefabs/Title/{Tables, SoundInfo, UIAtlasMgr, SortOrder_5, ActorInfo}.prefab · ResWork/Prefabs/Ingame/Main Camera.prefab(RealCamera)
+// · InGame 씬 루트 4종(IngameTouchInfo · DungeonInfo · DropItemInfo · Global Volume(GlobalVolumeMgr + 꺼진 Volume · 같은 프로필)) · ProjectileInfo
+// · WLSkillTestBoot(로그인 뒤 InGameInfo·PCInfo 프리팹 생성)
+// ■ 메뉴 WL/Skill Test/Build Scene · CLI: unity command eval --code 'return WL.SkillTest.Editor.WLSkillTestSceneBuilder.Build();'
+// ─────────────────────────────────────────────────────────────────────────────
+using System.Text;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.Rendering;
+using UnityEngine.SceneManagement;
+
+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", "UIAtlasMgr", "SortOrder_5", "ActorInfo" };
+ const string TitlePrefabFolder = "Assets/ResWork/UIPrefabs/Title/";
+ const string MainCameraPrefab = "Assets/ResWork/Prefabs/Ingame/Main Camera.prefab";
+ const string InGameInfoPrefab = "Assets/ResWork/Prefabs/Ingame/InGameInfo.prefab";
+ const string PCInfoPrefab = "Assets/ResWork/Prefabs/Ingame/PCInfo.prefab";
+ const string ProjectileInfoPrefab = "Assets/ResWork/Prefabs/Ingame/ProjectileInfo.prefab";
+ const string VolumeProfileGuid = "96e4f5994a1f2d948a293a2d5f2bd1b3"; // InGame.unity 의 Global Volume 프로필(실측)
+
+ [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()); }
+
+ /// 테스트 씬을 연다(Single). 현재 씬이 저장 안 된 상태면 열지 않고 알린다(K-WL-114).
+ public static string Open()
+ {
+ var cur = SceneManager.GetActiveScene();
+ 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;
+ }
+
+ /// 테스트 씬이 열려 있으면 **저장 없이** 빈 씬으로 바꾼다(다시 만들기 전 · 저장 안 된 변경이 있으면 거절).
+ 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 "빈 씬으로 바꿈";
+ }
+
+ public static string Build()
+ {
+ if (Application.isPlaying) return "Play 중에는 만들지 않는다";
+ var sb = new StringBuilder();
+ var prevActive = SceneManager.GetActiveScene();
+ if (!AssetDatabase.IsValidFolder(Folder)) { AssetDatabase.CreateFolder("Assets/WL", "SkillTest"); }
+
+ var cfg = WLSkillTestBoot.Cfg;
+ // 테스트 씬이 열려 있으면 그 경로로 저장할 수 없다(Unity: "Overwriting the same path as another open scene is not allowed")
+ // → 같은 호출 안에서 씬을 바꾸면 파이프라인 실행이 끊긴다(실측) · 먼저 CloseTestScene() 을 따로 부른 뒤 Build()
+ if (IsTestSceneOpen()) return "테스트 씬이 열려 있음 — 먼저 CloseTestScene()(빈 씬으로) 을 부른 뒤 Build()";
+ var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
+ SceneManager.SetActiveScene(scene);
+
+ // 빛
+ var lightGo = new GameObject("Directional Light");
+ var light = lightGo.AddComponent();
+ light.type = LightType.Directional; light.intensity = 1.1f; light.shadows = LightShadows.Soft; light.color = Color.white;
+ lightGo.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
+
+ // 평지(콜라이더 = NavMesh 베이크 기준)
+ var ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
+ ground.name = "Ground";
+ float size = cfg != null && cfg.groundSize > 1f ? cfg.groundSize : 60f;
+ ground.transform.localScale = new Vector3(size / 10f, 1f, size / 10f);
+ var mat = AssetDatabase.LoadAssetAtPath(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().sharedMaterial = mat;
+ GameObjectUtility.SetStaticEditorFlags(ground, StaticEditorFlags.BatchingStatic); // NavMesh 는 부트가 콜라이더 기준 런타임 베이크
+
+ // 매니저(Title 의 Mgr 과 같은 구성)
+ var mgr = new GameObject("Mgr");
+ mgr.AddComponent(); mgr.AddComponent(); mgr.AddComponent();
+ var es = new GameObject("EventSystem");
+ es.AddComponent(); es.AddComponent();
+
+ // Title 이 Addressables 로 올리던 프리팹 → 씬에 직접
+ foreach (var n in TitlePrefabs)
+ {
+ var p = AssetDatabase.LoadAssetAtPath(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;
+ }
+ // 원본 인게임 카메라(RealCamera · 태그 MainCamera)
+ var camP = AssetDatabase.LoadAssetAtPath(MainCameraPrefab);
+ if (camP != null) { var cam = PrefabUtility.InstantiatePrefab(camP, scene) as GameObject; if (cam != null) cam.name = "Main Camera"; }
+ else sb.Append("🔴 Main Camera 프리팹 없음\n");
+
+ // InGame 씬 루트 4종
+ new GameObject("IngameTouchInfo").AddComponent();
+ new GameObject("DungeonInfo").AddComponent();
+ new GameObject("DropItemInfo").AddComponent();
+ var gv = new GameObject("Global Volume");
+ gv.AddComponent();
+ var vol = gv.AddComponent();
+ vol.isGlobal = true; vol.enabled = false; // InGame.unity 실측: Volume 컴포넌트 꺼짐
+ var profPath = AssetDatabase.GUIDToAssetPath(VolumeProfileGuid);
+ if (!string.IsNullOrEmpty(profPath)) vol.sharedProfile = AssetDatabase.LoadAssetAtPath(profPath);
+ // ProjectileInfo 는 씬에 두지 않는다 — 부트가 InGameInfo 뒤에 원본 프리팹으로 만든다(1차 Play: 예열 투사체가 InGameInfo.Ins null 로 NRE 127건)
+
+ // 부트
+ var bootGo = new GameObject("WLSkillTestBoot");
+ var boot = bootGo.AddComponent();
+ boot.inGameInfoPrefab = AssetDatabase.LoadAssetAtPath(InGameInfoPrefab);
+ boot.pcInfoPrefab = AssetDatabase.LoadAssetAtPath(PCInfoPrefab);
+ boot.projectileInfoPrefab = AssetDatabase.LoadAssetAtPath(ProjectileInfoPrefab);
+ if (boot.inGameInfoPrefab == null) sb.Append("🔴 InGameInfo 프리팹 없음\n");
+ if (boot.pcInfoPrefab == null) sb.Append("🔴 PCInfo 프리팹 없음\n");
+ if (boot.projectileInfoPrefab == null) sb.Append("🔴 ProjectileInfo 프리팹 없음\n");
+
+ bool saved = EditorSceneManager.SaveScene(scene, ScenePath);
+ sb.Append(saved ? "저장 " : "🔴 저장 실패 ").Append(ScenePath).Append(" · 루트 ").Append(scene.rootCount).Append('\n');
+
+ // 열려 있던 씬으로 되돌리고 새 씬은 닫는다(Title 무변경)
+ if (prevActive.IsValid() && prevActive.isLoaded) SceneManager.SetActiveScene(prevActive);
+ EditorSceneManager.CloseScene(scene, true);
+ AssetDatabase.SaveAssets();
+ return sb.ToString();
+ }
+ }
+}
diff --git a/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs.meta b/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs.meta
new file mode 100644
index 000000000..6373990a0
--- /dev/null
+++ b/Assets/WL/SkillTest/Editor/WLSkillTestSceneBuilder.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 87ce98ef730cc604bacdad3a67282092
\ No newline at end of file
diff --git a/Assets/WL/SkillTest/Resources.meta b/Assets/WL/SkillTest/Resources.meta
new file mode 100644
index 000000000..6be996342
--- /dev/null
+++ b/Assets/WL/SkillTest/Resources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6c1e9723645442b48a842cdfe5980b98
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/Resources/WL.meta b/Assets/WL/SkillTest/Resources/WL.meta
new file mode 100644
index 000000000..1fa811774
--- /dev/null
+++ b/Assets/WL/SkillTest/Resources/WL.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a26aaa0b1ac0c404096587f6ba76614c
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json
new file mode 100644
index 000000000..b4c7a137c
--- /dev/null
+++ b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json
@@ -0,0 +1,28 @@
+{
+ "note": "WL 스킬 테스트 씬(PD #947 · 2026-10-19) 값. loginId = 개발 로그인 ID(useOptionUserId 1 이면 Title 에서 마지막에 쓴 ID 우선) · mapId 0 = 섬 행(900 · Lobby 모드 · 씬은 로드하지 않음) · dummyMobId 0 = WLIslandMobSettings.hitTestDummyMobId · dummyOffset 전부 0 = WLIslandMobSettings.hitTestDummyOffset · 키 이름 = UnityEngine.KeyCode",
+ "loginId": "TestID",
+ "useOptionUserId": 1,
+ "mapId": 0,
+ "spawnPos": [0, 0, 0],
+ "cameraRig": 1,
+ "dummyMobId": 0,
+ "dummyCount": 1,
+ "dummySpawnerId": 1,
+ "dummyOffset": [0, 0, 0],
+ "dummyGap": 2.5,
+ "waitPcSeconds": 30,
+ "forceSpawn": 1,
+ "probeAtlas": 0,
+ "probeNoLogin": 0,
+ "probeProfiler": 0,
+ "dummyStill": 1,
+ "skyColor": [0.55, 0.75, 0.95],
+ "moveKeys": 1,
+ "weaponPrevKey": "LeftBracket",
+ "weaponNextKey": "RightBracket",
+ "dummyResetKey": "R",
+ "label": 1,
+ "labelFontScale": 0.018,
+ "groundSize": 100,
+ "groundColor": [0.45, 0.62, 0.35]
+}
diff --git a/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json.meta b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json.meta
new file mode 100644
index 000000000..38a781373
--- /dev/null
+++ b/Assets/WL/SkillTest/Resources/WL/WLSkillTest.json.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: bb979365e442a1241be2d7b5975bf741
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/WLSkillTest.unity b/Assets/WL/SkillTest/WLSkillTest.unity
new file mode 100644
index 000000000..5858b5e9a
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTest.unity
@@ -0,0 +1,1193 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_OcclusionBakeSettings:
+ smallestOccluder: 5
+ smallestHole: 0.25
+ backfaceThreshold: 100
+ m_SceneGUID: 00000000000000000000000000000000
+ m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 10
+ m_Fog: 0
+ m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+ m_FogMode: 3
+ m_FogDensity: 0.01
+ m_LinearFogStart: 0
+ m_LinearFogEnd: 300
+ m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+ m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+ m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+ m_AmbientIntensity: 1
+ m_AmbientMode: 0
+ m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+ m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+ m_HaloStrength: 0.5
+ m_FlareStrength: 1
+ m_FlareFadeSpeed: 3
+ m_HaloTexture: {fileID: 0}
+ m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+ m_DefaultReflectionMode: 0
+ m_DefaultReflectionResolution: 128
+ m_ReflectionBounces: 1
+ m_ReflectionIntensity: 1
+ m_CustomReflection: {fileID: 0}
+ m_Sun: {fileID: 0}
+ m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+ m_ObjectHideFlags: 0
+ serializedVersion: 13
+ m_BakeOnSceneLoad: 0
+ m_GISettings:
+ serializedVersion: 2
+ m_BounceScale: 1
+ m_IndirectOutputScale: 1
+ m_AlbedoBoost: 1
+ m_EnvironmentLightingMode: 0
+ m_EnableBakedLightmaps: 1
+ m_EnableRealtimeLightmaps: 0
+ m_LightmapEditorSettings:
+ serializedVersion: 12
+ m_Resolution: 2
+ m_BakeResolution: 40
+ m_AtlasSize: 1024
+ m_AO: 0
+ m_AOMaxDistance: 1
+ m_CompAOExponent: 1
+ m_CompAOExponentDirect: 0
+ m_ExtractAmbientOcclusion: 0
+ m_Padding: 2
+ m_LightmapParameters: {fileID: 0}
+ m_LightmapsBakeMode: 1
+ m_TextureCompression: 1
+ m_ReflectionCompression: 2
+ m_MixedBakeMode: 2
+ m_BakeBackend: 2
+ m_PVRSampling: 1
+ m_PVRDirectSampleCount: 32
+ m_PVRSampleCount: 512
+ m_PVRBounces: 2
+ m_PVREnvironmentSampleCount: 256
+ m_PVREnvironmentReferencePointCount: 2048
+ m_PVRFilteringMode: 1
+ m_PVRDenoiserTypeDirect: 1
+ m_PVRDenoiserTypeIndirect: 1
+ m_PVRDenoiserTypeAO: 1
+ m_PVRFilterTypeDirect: 0
+ m_PVRFilterTypeIndirect: 0
+ m_PVRFilterTypeAO: 0
+ m_PVREnvironmentMIS: 1
+ m_PVRCulling: 1
+ m_PVRFilteringGaussRadiusDirect: 1
+ m_PVRFilteringGaussRadiusIndirect: 1
+ m_PVRFilteringGaussRadiusAO: 1
+ m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+ m_PVRFilteringAtrousPositionSigmaIndirect: 2
+ m_PVRFilteringAtrousPositionSigmaAO: 1
+ m_ExportTrainingData: 0
+ m_TrainingDataDestination: TrainingData
+ m_LightProbeSampleCountMultiplier: 4
+ m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0}
+ m_LightingSettings: {fileID: 0}
+--- !u!196 &4
+NavMeshSettings:
+ serializedVersion: 2
+ m_ObjectHideFlags: 0
+ m_BuildSettings:
+ serializedVersion: 3
+ agentTypeID: 0
+ agentRadius: 0.5
+ agentHeight: 2
+ agentSlope: 45
+ agentClimb: 0.4
+ ledgeDropHeight: 0
+ maxJumpAcrossDistance: 0
+ minRegionArea: 2
+ manualCellSize: 0
+ cellSize: 0.16666667
+ manualTileSize: 0
+ tileSize: 256
+ buildHeightMesh: 0
+ maxJobWorkers: 0
+ preserveTilesOutsideBounds: 0
+ debug:
+ m_Flags: 0
+ m_NavMeshData: {fileID: 0}
+--- !u!1 &19310268
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 19310271}
+ - component: {fileID: 19310270}
+ - component: {fileID: 19310269}
+ m_Layer: 0
+ m_Name: Global Volume
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &19310269
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 19310268}
+ m_Enabled: 0
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.Volume
+ m_IsGlobal: 1
+ priority: 0
+ blendDistance: 0
+ weight: 1
+ sharedProfile: {fileID: 11400000, guid: 96e4f5994a1f2d948a293a2d5f2bd1b3, type: 2}
+--- !u!114 &19310270
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 19310268}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 38f50b30e61fb0341aed3733ef1b8fdd, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::GlobalVolumeMgr
+ m_Volume: {fileID: 0}
+--- !u!4 &19310271
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 19310268}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &498260968
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 498260971}
+ - component: {fileID: 498260970}
+ - component: {fileID: 498260969}
+ m_Layer: 0
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &498260969
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 498260968}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.StandaloneInputModule
+ m_SendPointerHoverToParent: 1
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &498260970
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 498260968}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.EventSystems.EventSystem
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 10
+--- !u!4 &498260971
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 498260968}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &597521493
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_Pivot.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_Pivot.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchorMax.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchorMax.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchorMin.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchorMin.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_SizeDelta.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_SizeDelta.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchoredPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_AnchoredPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 2109408569755660315, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5535108574643066261, guid: cc1441de98672f94e8db36f727d41651,
+ type: 3}
+ propertyPath: m_Name
+ value: SortOrder_5
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: cc1441de98672f94e8db36f727d41651, type: 3}
+--- !u!1 &697666336
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 697666338}
+ - component: {fileID: 697666337}
+ m_Layer: 0
+ m_Name: IngameTouchInfo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &697666337
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 697666336}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 68bebb0d1512fbc4180d3cae3ab568f1, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::InGameTouchInfo
+--- !u!4 &697666338
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 697666336}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &881267190
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 881267192}
+ - component: {fileID: 881267191}
+ m_Layer: 0
+ m_Name: DungeonInfo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &881267191
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 881267190}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 7ed190b399bd4be4ebc361c911eb9d95, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::DungeonInfo
+ m_TotalTime: 0
+ WaveMakeGapTime: 0
+--- !u!4 &881267192
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 881267190}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &886385477
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 886385479}
+ - component: {fileID: 886385478}
+ m_Layer: 0
+ m_Name: WLSkillTestBoot
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &886385478
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 886385477}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d2262201ce81fd5449555b2f0fa8dfed, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::WL.SkillTest.WLSkillTestBoot
+ inGameInfoPrefab: {fileID: 4525569738805394790, guid: e3c4be67fc01ba74e89bad5eeb0de437,
+ type: 3}
+ pcInfoPrefab: {fileID: 3011261262900193068, guid: aa5c8ea19f5e09a4da7d903094635d40,
+ type: 3}
+ projectileInfoPrefab: {fileID: 5648830385143125634, guid: dbd6b7f67ff82574e95622f5e861d0c6,
+ type: 3}
+--- !u!4 &886385479
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 886385477}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &1278164270
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 285758041279125022, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_Name
+ value: UIAtlasMgr
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8413869023028529553, guid: 3543732faf47be94b9d43adce4338783,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 3543732faf47be94b9d43adce4338783, type: 3}
+--- !u!1 &1593120247
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1593120249}
+ - component: {fileID: 1593120248}
+ m_Layer: 0
+ m_Name: DropItemInfo
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1593120248
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1593120247}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 2a9c4fdffa888b84897a97d0bef9fd6d, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::DropItemInfo
+--- !u!4 &1593120249
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1593120247}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &1605975905
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 1602866066306103301, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_Name
+ value: ActorInfo
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 4462996344097564095, guid: 35cc51d1c9df5c84cb39e0005f798da6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 35cc51d1c9df5c84cb39e0005f798da6, type: 3}
+--- !u!1 &1658048616
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1658048618}
+ - component: {fileID: 1658048617}
+ m_Layer: 0
+ m_Name: Directional Light
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!108 &1658048617
+Light:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1658048616}
+ m_Enabled: 1
+ serializedVersion: 12
+ m_Type: 1
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_Intensity: 1.1
+ m_Range: 10
+ m_SpotAngle: 30
+ m_InnerSpotAngle: 21.80208
+ m_CookieSize2D: {x: 0.5, y: 0.5}
+ m_Shadows:
+ m_Type: 2
+ m_Resolution: -1
+ m_CustomResolution: -1
+ m_Strength: 1
+ m_Bias: 0.05
+ m_NormalBias: 0.4
+ m_NearPlane: 0.2
+ m_CullingMatrixOverride:
+ e00: 1
+ e01: 0
+ e02: 0
+ e03: 0
+ e10: 0
+ e11: 1
+ e12: 0
+ e13: 0
+ e20: 0
+ e21: 0
+ e22: 1
+ e23: 0
+ e30: 0
+ e31: 0
+ e32: 0
+ e33: 1
+ m_UseCullingMatrixOverride: 0
+ m_Cookie: {fileID: 0}
+ m_DrawHalo: 0
+ m_Flare: {fileID: 0}
+ m_RenderMode: 0
+ m_CullingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+ m_RenderingLayerMask: 1
+ m_Lightmapping: 4
+ m_LightShadowCasterMode: 0
+ m_AreaSize: {x: 1, y: 1}
+ m_BounceIntensity: 1
+ m_ColorTemperature: 6570
+ m_UseColorTemperature: 0
+ m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+ m_UseBoundingSphereOverride: 0
+ m_UseViewFrustumForShadowCasterCull: 1
+ m_ForceVisible: 0
+ m_ShadowRadius: 0
+ m_ShadowAngle: 0
+ m_LightUnit: 1
+ m_LuxAtDistance: 1
+ m_EnableSpotReflector: 1
+--- !u!4 &1658048618
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1658048616}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0.40821794, y: -0.23456973, z: 0.10938166, w: 0.8754261}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1738185520
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1738185524}
+ - component: {fileID: 1738185523}
+ - component: {fileID: 1738185522}
+ - component: {fileID: 1738185521}
+ m_Layer: 0
+ m_Name: Ground
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 4
+ m_IsActive: 1
+--- !u!23 &1738185521
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1738185520}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RayTracingAccelStructBuildFlagsOverride: 0
+ m_RayTracingAccelStructBuildFlags: 1
+ m_SmallMeshCulling: 1
+ m_ForceMeshLod: -1
+ m_MeshLodSelectionBias: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 2100000, guid: bb102d8fd6df38040a18663db0f2504f, type: 2}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 1
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_GlobalIlluminationMeshLod: 0
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_MaskInteraction: 0
+ m_AdditionalVertexStreams: {fileID: 0}
+--- !u!64 &1738185522
+MeshCollider:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1738185520}
+ m_Material: {fileID: 0}
+ m_IncludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_ExcludeLayers:
+ serializedVersion: 2
+ m_Bits: 0
+ m_LayerOverridePriority: 0
+ m_IsTrigger: 0
+ m_ProvidesContacts: 0
+ m_Enabled: 1
+ serializedVersion: 5
+ m_Convex: 0
+ m_CookingOptions: 30
+ m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!33 &1738185523
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1738185520}
+ m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &1738185524
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1738185520}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 10, y: 1, z: 10}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &1786167120
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 5592969400081043368, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_Name
+ value: SoundInfo
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 8705745286184766240, guid: e47583f3231d50740a2a7fbf18e6c0c6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: e47583f3231d50740a2a7fbf18e6c0c6, type: 3}
+--- !u!1 &1797140228
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1797140232}
+ - component: {fileID: 1797140231}
+ - component: {fileID: 1797140230}
+ - component: {fileID: 1797140229}
+ m_Layer: 0
+ m_Name: Mgr
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!114 &1797140229
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1797140228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: cc1d3bc18379c0a4196064b2ab851346, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::TimeMgr
+--- !u!114 &1797140230
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1797140228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 205e9f58da95dca43bcd6f29f4cc3b41, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::CoroutineMgr
+--- !u!114 &1797140231
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1797140228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 01d734a0ae085284eb82b59533011c90, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Assembly-CSharp::AddrResourceMgr
+--- !u!4 &1797140232
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1797140228}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &2082658120
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 5043163894663193716, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_Name
+ value: Tables
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5043163894663193739, guid: 2d20f62727d330b45b53a44adbb375e6,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 2d20f62727d330b45b53a44adbb375e6, type: 3}
+--- !u!1001 &2124090511
+PrefabInstance:
+ m_ObjectHideFlags: 0
+ serializedVersion: 2
+ m_Modification:
+ serializedVersion: 3
+ m_TransformParent: {fileID: 0}
+ m_Modifications:
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalPosition.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalPosition.y
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalPosition.z
+ value: -10
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalRotation.w
+ value: 1
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalRotation.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalRotation.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalRotation.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.x
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.y
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 142741943360815210, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_LocalEulerAnglesHint.z
+ value: 0
+ objectReference: {fileID: 0}
+ - target: {fileID: 5448332676958061915, guid: 1fb5fd58cbcc4714eaf59008cbda15a1,
+ type: 3}
+ propertyPath: m_Name
+ value: Main Camera
+ objectReference: {fileID: 0}
+ m_RemovedComponents: []
+ m_RemovedGameObjects: []
+ m_AddedGameObjects: []
+ m_AddedComponents: []
+ m_SourcePrefab: {fileID: 100100000, guid: 1fb5fd58cbcc4714eaf59008cbda15a1, type: 3}
+--- !u!1660057539 &9223372036854775807
+SceneRoots:
+ m_ObjectHideFlags: 0
+ m_Roots:
+ - {fileID: 1658048618}
+ - {fileID: 1738185524}
+ - {fileID: 1797140232}
+ - {fileID: 498260971}
+ - {fileID: 2082658120}
+ - {fileID: 1786167120}
+ - {fileID: 1278164270}
+ - {fileID: 597521493}
+ - {fileID: 1605975905}
+ - {fileID: 2124090511}
+ - {fileID: 697666338}
+ - {fileID: 881267192}
+ - {fileID: 1593120249}
+ - {fileID: 19310271}
+ - {fileID: 886385479}
diff --git a/Assets/WL/SkillTest/WLSkillTest.unity.meta b/Assets/WL/SkillTest/WLSkillTest.unity.meta
new file mode 100644
index 000000000..7b2cf2c31
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTest.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e7b4ccbbf2b0ad0409dfdd76b3d350cd
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/WL/SkillTest/WLSkillTestBoot.cs b/Assets/WL/SkillTest/WLSkillTestBoot.cs
new file mode 100644
index 000000000..29b8b5917
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTestBoot.cs
@@ -0,0 +1,493 @@
+// ─────────────────────────────────────────────────────────────────────────────
+// WLSkillTestBoot.cs — 무기 스킬 구현용 가벼운 테스트 씬(WLSkillTest) 부트 (PD #947 · 2026-10-19)
+//
+// PD 원문: 「Title 씬에서 무기 스킬 작업을 진행하다보니 쓸데 없이 로딩이 너무 길고 테스트가 힘들어. … 최대한 가벼운 테스트용 씬을 만들고
+// 이 씬에서 스킬 작업을 할 수 있도록 … (우리 게임의 무기 스킬 구현은 앞으로 테스트 씬을 통해 진행할게)」
+//
+// ■ 왜 — Title → 로그인 → Ingame → 섬 흐름은 Play 1회에 약 115 s(도메인 리로드 32 + Title 씬 21 + Title 초기화 47 + 로그인 3.5 + 섬 9 · #943-① 실측).
+// 스킬 작업은 PC 1 + 무적 더미 1 + 평지면 충분하다 → Title 씬·섬 씬(FarmingIsland)·농장 시스템을 빼고 **원본 InGame 흐름만** 돌린다.
+// ■ 어떻게 (원본 코드 0줄 · 테이블 0줄 · 빌드 설정 0줄)
+// ① 씬에 매니저 프리팹을 직접 둔다(Title 이 Addressables 로 올리던 Tables·SoundInfo·UIAtlasMgr·SortOrder_5·ActorInfo + Mgr(AddrResourceMgr·CoroutineMgr·TimeMgr)
+// + EventSystem + 원본 Main Camera 프리팹(RealCamera) + InGame 씬 루트 4종) → 테이블 로딩 대기. ※ 씬 조립 = Editor/WLSkillTestSceneBuilder.cs
+// ② 개발 로그인(ServerInfo.Login · TitleInfo 없이 · ID = PlayerPrefs Option_UserID 또는 설정값) → m_ServerData(진짜 계정 데이터 · 장비·스킬 슬롯 그대로).
+// ③ 섬·던전과 같은 LoadMapMgr 대체 노드(WLIslandMapNodes)에 시작 지점 + 섬 구도 카메라 리그(WLDungeonCameraRig).
+// ④ InGameInfo·PCInfo 프리팹을 **로그인 뒤** 생성 → InGameInfo 가 NewGameUI(HUD · 조이스틱 · 장비창)를 올리고 NewGameUI.Start 가 Load_Map(섬 900)을 부른다.
+// 그 전에 SceneInfo.CurStageData 를 900 행으로 미리 채워(리플렉션) 「같은 맵」 분기 = **섬 씬을 로드하지 않고** PCInfo.Make_Actors 만 돈다.
+// ⑤ PC 가 생기면 StartCam + SetUI_byGameMode(Lobby) + 무적 더미(몬스터 표 hitTestDummyMobId · WLTrailTest 채택 · HP 되채움) → Stage = ready.
+// ⑥ 키: WASD/방향키 이동(원본 Run_byJoystick) · [ ] 낀 무기 바꾸기(WLInventory 장비 순환) · R 더미 제자리 · Q/E/숫자 = 기존 훅(WLSkillHotkey·WLWeaponGradeCheat).
+// ■ 값 = Resources/WL/WLSkillTest.json (C45) · 진단 = Describe() · 단계 시각 = Laps(로딩 실측 보고용)
+// 🔴 Debug.LogError 금지(원본 ErrorLogHook 이 팝업을 띄운다) · 씬 이름 "WLSkillTest" 는 원본 "StatTest" 분기와 무관(그 분기는 MainToStatTest 를 요구한다).
+// 🔴 섬 씬 이름(Level01)과 다르므로 WLIslandBridge·WLIslandMobZones 등 섬 전용 훅은 이 씬에서 돌지 않는다(의도).
+// ─────────────────────────────────────────────────────────────────────────────
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using Unity.AI.Navigation;
+using UnityEngine;
+using UnityEngine.ResourceManagement.AsyncOperations;
+using UnityEngine.SceneManagement;
+using WL.Combat.Weapon;
+using WL.Equip;
+using WL.Island;
+using WL.IslandMob;
+
+namespace WL.SkillTest
+{
+ [System.Serializable]
+ public class WLSkillTestJson
+ {
+ public string note = "";
+ public string loginId = "TestID"; // 개발 로그인 ID 기본값
+ public int useOptionUserId = 1; // 1 = PlayerPrefs Option_UserID(Title 에서 마지막에 쓴 ID)를 우선
+ public int mapId = 0; // 0 = WLIslandSettings.islandMapId(섬 행 900 · Lobby 모드) — 씬은 로드하지 않고 모드·노드만 빌린다
+ public float[] spawnPos = { 0f, 0f, 0f };
+ public int cameraRig = 1; // 1 = 섬 구도 카메라 리그(WLDungeonCameraRig) 생성
+ public int dummyMobId = 0; // 0 = WLIslandMobSettings.hitTestDummyMobId
+ public int dummyCount = 1;
+ public int dummySpawnerId = 1;
+ public float[] dummyOffset = { 0f, 0f, 0f }; // 전부 0 = WLIslandMobSettings.hitTestDummyOffset (PC 기준 오른쪽·위·앞)
+ public float dummyGap = 2.5f; // 더미가 둘 이상일 때 옆 간격 m
+ public float waitPcSeconds = 30f;
+ public int forceSpawn = 1; // 1 = PC 가 생긴 뒤 시작 지점(spawnPos)으로 한 번 더 워프
+ public int probeAtlas = 0; // 진단: 1 = 부트 첫머리에서 UI 아틀라스 로드 시간을 잰다(평소 0)
+ public int probeNoLogin = 0; // 진단: 1 = 로그인 없이 60 s 프레임 멈춤만 기록(평소 0)
+ public int probeProfiler = 0; // 진단: 1 = 프로파일러를 켜고 3 s 넘는 프레임의 무거운 호출 경로를 ProfileDump 에(평소 0 · probeNoLogin 과 함께)
+ public int dummyStill = 1; // 1 = 더미가 제자리(Stop) · 0 = 섬처럼 쫓아옴
+ public float[] skyColor = { 0.55f, 0.75f, 0.95f }; // 스카이박스가 없을 때 카메라 배경색(빈 값이면 손대지 않음)
+ public int moveKeys = 1; // WASD · 방향키 이동
+ public string weaponPrevKey = "LeftBracket";
+ public string weaponNextKey = "RightBracket";
+ public string dummyResetKey = "R";
+ public int label = 1; // 화면 왼쪽 위 상태 글자
+ public float labelFontScale = 0.018f;
+ public float groundSize = 60f; // 평지 한 변 m(빌더가 읽는다)
+ public float[] groundColor = { 0.45f, 0.62f, 0.35f };
+ }
+
+ public sealed class WLSkillTestBoot : MonoBehaviour
+ {
+ public const string SceneName = "WLSkillTest";
+ public const string ResourcesPath = "WL/WLSkillTest";
+
+ [Header("씬 빌더가 채운다 — 로그인 뒤에 생성하는 프리팹(InGameInfo → ProjectileInfo → PCInfo 순 · 투사체 예열이 InGameInfo.Ins 를 쓴다)")]
+ public GameObject inGameInfoPrefab;
+ public GameObject pcInfoPrefab;
+ public GameObject projectileInfoPrefab;
+
+ public static WLSkillTestBoot Ins;
+ public static string Stage = "none";
+ public static string LastLog = "";
+ public static bool Ready;
+ public static Actor Dummy;
+ public static readonly List Laps = new List();
+ /// PlayFab 호출 시각 기록(에디터 · 로딩 실측용 · 2차 Play 에서 로그인 52 s 의 내역을 보려고)
+ public static readonly List PfLog = new List();
+ /// 0.5 s 넘게 멈춘 프레임 기록(시각@길이 · 메인 스레드가 무엇에 막혔는지 PfLog·Laps 와 맞춰 본다)
+ public static readonly List Hangs = new List();
+ public static float T0;
+ float _lastRt;
+
+ void LateUpdate()
+ {
+ float now = Time.realtimeSinceStartup;
+ if (_lastRt > 0f && now - _lastRt > 0.5f && Hangs.Count < 40)
+ {
+ string flags = "";
+#if UNITY_EDITOR
+ flags = " L" + (UnityEditor.Lightmapping.isRunning ? 1 : 0) + "S" + (UnityEditor.ShaderUtil.anythingCompiling ? 1 : 0) + "C" + (UnityEditor.EditorApplication.isCompiling ? 1 : 0) + "U" + (UnityEditor.EditorApplication.isUpdating ? 1 : 0);
+#endif
+ Hangs.Add((now - T0).ToString("F1") + "@" + (now - _lastRt).ToString("F1") + "s" + flags);
+#if UNITY_EDITOR
+ // 멈춤 직후 그 프레임(과 직전 프레임)의 가장 무거운 호출 경로를 바로 적는다 — 프레임 버퍼가 밀려 사라지기 전에
+ if (Cfg.probeProfiler != 0) { int li = UnityEditorInternal.ProfilerDriver.lastFrameIndex; DumpFrame(li - 1, 1000f); DumpFrame(li, 1000f); }
+#endif
+ }
+ _lastRt = now;
+ }
+
+#if UNITY_EDITOR
+ /// 진단: 프로파일러로 잡은 긴 프레임의 가장 무거운 호출 경로(멈춤의 정체)
+ public static string ProfileDump = "";
+ static void ProfilerStart() { s_prof.Length = 0; ProfileDump = ""; UnityEditorInternal.ProfilerDriver.ClearAllFrames(); UnityEditorInternal.ProfilerDriver.enabled = true; }
+ static readonly System.Text.StringBuilder s_prof = new System.Text.StringBuilder();
+ static void DumpFrame(int f, float minMs)
+ {
+ if (f < 0) return;
+ int col = UnityEditor.Profiling.HierarchyFrameDataView.columnTotalTime;
+ var kids = new List();
+ using (var v = UnityEditorInternal.ProfilerDriver.GetHierarchyFrameDataView(f, 0, UnityEditor.Profiling.HierarchyFrameDataView.ViewModes.MergeSamplesWithTheSameName, col, false))
+ {
+ if (v == null || !v.valid || v.frameTimeMs < minMs) return;
+ s_prof.Append(" || frame ").Append(f).Append(' ').Append(v.frameTimeMs.ToString("F0")).Append("ms:");
+ int id = v.GetRootItemID();
+ for (int depth = 0; depth < 12; depth++)
+ {
+ kids.Clear(); v.GetItemChildren(id, kids);
+ if (kids.Count == 0) break;
+ int best = kids[0]; float bestMs = -1f, second = 0f; string secondName = "";
+ foreach (var k in kids) { float ms = v.GetItemColumnDataAsFloat(k, col); if (ms > bestMs) { if (bestMs > second) { second = bestMs; secondName = v.GetItemName(best); } bestMs = ms; best = k; } else if (ms > second) { second = ms; secondName = v.GetItemName(k); } }
+ s_prof.Append(" > ").Append(v.GetItemName(best)).Append(' ').Append(bestMs.ToString("F0"));
+ if (second > 1000f) s_prof.Append(" (+").Append(secondName).Append(' ').Append(second.ToString("F0")).Append(')');
+ id = best;
+ }
+ }
+ ProfileDump = s_prof.ToString();
+ }
+ static void ProfilerDump(float minMs)
+ {
+ int first = UnityEditorInternal.ProfilerDriver.firstFrameIndex, last = UnityEditorInternal.ProfilerDriver.lastFrameIndex;
+ s_prof.Append(" || buffer ").Append(first).Append("~").Append(last);
+ for (int f = first; f <= last && f >= 0; f++) DumpFrame(f, minMs);
+ ProfileDump = s_prof.ToString();
+ UnityEditorInternal.ProfilerDriver.enabled = false;
+ }
+#endif
+
+ static System.Reflection.FieldInfo s_fAtlas;
+ static bool AtlasReady()
+ {
+ if (!UIAtlasMgr.isIns || UIAtlasMgr.Ins == null) return false;
+ if (s_fAtlas == null) s_fAtlas = typeof(UIAtlasMgr).GetField("UIAtlas", BindingFlags.NonPublic | BindingFlags.Instance);
+ return s_fAtlas != null && s_fAtlas.GetValue(UIAtlasMgr.Ins) != null;
+ }
+
+#if UNITY_EDITOR
+ static void OnPf(PlayFab.Internal.ApiProcessingEventArgs e) { PfLog.Add((Time.realtimeSinceStartup - T0).ToString("F1") + " " + e.EventType + " " + e.ApiEndpoint); }
+ static void OnPfErr(PlayFab.SharedModels.PlayFabRequestCommon r, PlayFab.PlayFabError err) { PfLog.Add((Time.realtimeSinceStartup - T0).ToString("F1") + " ERR " + (err != null ? err.Error + " " + err.ErrorMessage : "?")); }
+#endif
+
+ static WLSkillTestJson s_cfg;
+ bool _moving;
+ KeyCode _kPrev = KeyCode.LeftBracket, _kNext = KeyCode.RightBracket, _kReset = KeyCode.R;
+ string _weaponMsg = "";
+ GUIStyle _style;
+
+ public static WLSkillTestJson Cfg
+ {
+ get
+ {
+ if (s_cfg != null) return s_cfg;
+ s_cfg = new WLSkillTestJson();
+ var ta = Resources.Load(ResourcesPath);
+ if (ta == null) { LastLog = "설정 없음 " + ResourcesPath; return s_cfg; }
+ try { JsonUtility.FromJsonOverwrite(ta.text, s_cfg); } catch (System.Exception ex) { LastLog = "설정 파싱 실패 — " + ex.Message; }
+ return s_cfg;
+ }
+ }
+
+ public static bool Active { get { return SceneManager.GetActiveScene().name == SceneName; } }
+
+ static Vector3 V3(float[] a) { return a != null && a.Length >= 3 ? new Vector3(a[0], a[1], a[2]) : Vector3.zero; }
+ static KeyCode Key(string s, KeyCode d) { KeyCode k; return !string.IsNullOrEmpty(s) && System.Enum.TryParse(s, true, out k) ? k : d; }
+
+ /// 상태 파일(Library/ 아래 · 미추적) — 외부 스크립트는 Play 중 `eval` 대신 이 파일을 읽는다. 실측(8차 Play): Play 뒤 첫 `unity command eval/run_script` 가 EditorLoop 에서 약 35 s 메인 스레드를 멈춘다(스크립트 컴파일 준비).
+ public static string StatusPath { get { return System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, "../Library/WLSkillTest_status.txt")); } }
+
+ static void WriteStatus()
+ {
+ try { System.IO.File.WriteAllText(StatusPath, Describe()); } catch { }
+ }
+
+ static void Lap(string name)
+ {
+ Stage = name;
+ Laps.Add(name + " " + (Time.realtimeSinceStartup - T0).ToString("F1") + "s");
+ WriteStatus();
+ }
+
+ void Awake()
+ {
+ Ins = this;
+ T0 = Time.realtimeSinceStartup;
+ Laps.Clear(); PfLog.Clear(); Hangs.Clear(); Ready = false; Dummy = null; s_cfg = null;
+#if UNITY_EDITOR
+ if (Cfg.probeProfiler != 0) ProfilerStart();
+ PlayFab.Internal.PlayFabHttp.ApiProcessingEventHandler -= OnPf; PlayFab.Internal.PlayFabHttp.ApiProcessingEventHandler += OnPf;
+ PlayFab.Internal.PlayFabHttp.ApiProcessingErrorEventHandler -= OnPfErr; PlayFab.Internal.PlayFabHttp.ApiProcessingErrorEventHandler += OnPfErr;
+#endif
+ var cfg = Cfg;
+ _kPrev = Key(cfg.weaponPrevKey, KeyCode.LeftBracket);
+ _kNext = Key(cfg.weaponNextKey, KeyCode.RightBracket);
+ _kReset = Key(cfg.dummyResetKey, KeyCode.R);
+ Lap("awake");
+ }
+
+ IEnumerator Start()
+ {
+ var cfg = Cfg;
+
+#if UNITY_EDITOR
+ // 진단(probeAtlas 1): UI 스프라이트 아틀라스(.spriteatlasv2)를 에디터 API 로 직접 읽어 걸리는 시간을 잰다 — 4차 Play 실측에서 로그인 중 메인 스레드가 16 s + 34 s 멈췄고 첫 멈춤 끝 = 아틀라스 도착 시각
+ if (cfg.probeAtlas != 0)
+ {
+ float a = Time.realtimeSinceStartup;
+ var atl = UnityEditor.AssetDatabase.LoadAssetAtPath("Assets/Res_Addr/MainUI/UIAtlas.spriteatlasv2");
+ float b = Time.realtimeSinceStartup;
+ var sp = atl != null ? atl.GetSprite("grade1") : null;
+ float c = Time.realtimeSinceStartup;
+ Laps.Add("atlasLoad " + (b - a).ToString("F1") + "s getSprite " + (c - b).ToString("F1") + "s spr=" + (sp != null) + " n=" + (atl != null ? atl.spriteCount : -1));
+ }
+#endif
+
+ // ⓪ 평지 NavMesh — 콜라이더 기준 런타임 베이크(WLDungeonSceneSetup 과 같은 방식 · 임포트 설정 0) + 배경색
+ BakeGround();
+ ApplySky(cfg);
+ Lap("navmesh");
+
+ // ⓪-2 Addressables 초기화를 **따로 재서** 보이게 한다(Use Asset Database 모드의 카탈로그 준비 · 어차피 첫 로드 때 든다)
+ var init = UnityEngine.AddressableAssets.Addressables.InitializeAsync();
+ while (!init.IsDone) yield return null;
+ Lap("addr-init");
+
+ // ① 테이블(Tables 프리팹 = TableChecker + 표 44개)
+ while (!TableChecker.isIns || TableChecker.Ins == null || !TableChecker.Ins.CheckAllLoad()) yield return null;
+ Lap("tables");
+
+ // 진단(probeNoLogin 1): 로그인 없이 60 s 동안 프레임 멈춤만 기록하고 멈춘다 — 멈춤이 PlayFab 때문인지 에디터(조명·셰이더) 때문인지 가른다
+ if (cfg.probeNoLogin != 0)
+ {
+ float w = 0f; while (w < 60f) { w += Time.unscaledDeltaTime; yield return null; }
+#if UNITY_EDITOR
+ if (cfg.probeProfiler != 0) ProfilerDump(3000f);
+#endif
+ Stage = "nologin-done"; LastLog = "로그인 없이 60 s 관찰"; WriteStatus(); yield break;
+ }
+
+ // ② 개발 로그인 — TitleInfo.OnClick_DevLogin 과 같은 순서(ID 저장 → ServerInfo.Login)
+ var si = ServerInfo.Ins;
+ string id = cfg.useOptionUserId != 0 ? PlayerPrefs.GetString(MyValue.OptionKey_UserID, cfg.loginId) : cfg.loginId;
+ if (string.IsNullOrEmpty(id)) id = string.IsNullOrEmpty(cfg.loginId) ? "TestID" : cfg.loginId;
+ PlayerPrefs.SetString(MyValue.OptionKey_UserID, id);
+ si.UserID = id;
+ bool done = false, fail = false, atlasLapped = false;
+ si.Login(newuser => { done = true; }, () => { fail = true; });
+ while (!done && !fail)
+ {
+ if (!atlasLapped && AtlasReady()) { atlasLapped = true; Lap("atlas"); Stage = "login"; } // UIAtlasMgr 의 스프라이트 아틀라스(에디터 로드)가 끝난 시각
+ yield return null;
+ }
+ if (!atlasLapped && AtlasReady()) Lap("atlas");
+ if (fail || si.m_ServerData == null) { Stage = "login-fail"; LastLog = "로그인 실패 — ID " + id; WriteStatus(); yield break; }
+ Lap("login " + id);
+
+ // ③ 맵 노드(LoadMapMgr 대체) + 카메라 리그 — PCInfo.Make_Actors 보다 먼저 있어야 한다(같은 프레임)
+ var icfg = WLIslandSettings.Instance;
+ int mapId = cfg.mapId > 0 ? cfg.mapId : (icfg != null ? icfg.islandMapId : 900);
+ MyValue.MyChoiceMapData.n_MapID = mapId;
+ MyValue.MyChoiceMapData.n_PortalIndex = 0;
+ Vector3 spawn = V3(cfg.spawnPos);
+ float yaw = icfg != null ? WLDungeonCameraRig.SolvedYaw(icfg) : 0f;
+ if (icfg != null)
+ {
+ var nodes = WLIslandMapNodes.Ensure(icfg);
+ if (nodes != null) nodes.SetStartPos(spawn, yaw);
+ if (cfg.cameraRig != 0 && Object.FindFirstObjectByType() == null)
+ WLDungeonCameraRig.Create(icfg, gameObject.scene);
+ }
+
+ // ④ InGame 매니저 — NewGameUI.Start 의 Load_Map(mapId) 이 「같은 맵」 분기를 타도록 SceneInfo.CurStageData 를 미리 채운다
+ if (inGameInfoPrefab != null && InGameInfo.Ins == null) Instantiate(inGameInfoPrefab).name = "InGameInfo";
+ if (projectileInfoPrefab != null && !ProjectileInfo.isIns) Instantiate(projectileInfoPrefab).name = "ProjectileInfo"; // 🔴 InGameInfo 뒤 — 예열 투사체 OnEnable → Off → Show_HitEffect 가 InGameInfo.Ins 를 쓴다(1차 Play 실측 NRE 127건)
+ if (pcInfoPrefab != null && !PCInfo.isIns) Instantiate(pcInfoPrefab).name = "PCInfo";
+ var row = table_battlemapconfig.Ins.Get_Data(mapId);
+ var sinfo = InGameInfo.Ins != null ? InGameInfo.Ins.m_SceneInfo : null;
+ if (row == null) { Stage = "map-row-none"; LastLog = "BattleMapConfig 행 없음 " + mapId; WriteStatus(); yield break; }
+ if (sinfo == null) { Stage = "sceneinfo-none"; LastLog = "InGameInfo.m_SceneInfo 없음"; WriteStatus(); yield break; }
+ var f = typeof(SceneInfo).GetField("CurStageData", BindingFlags.NonPublic | BindingFlags.Instance);
+ if (f == null) { Stage = "reflect-fail"; LastLog = "SceneInfo.CurStageData 필드 없음"; WriteStatus(); yield break; }
+ f.SetValue(sinfo, row);
+ Lap("ingame");
+
+ // ⑤ PC — NewGameUI(HUD) 로드 → Load_Map → Make_Actors(Addressables) → PCActor.Set → MyValue.MyPC
+ float t = 0f;
+ while (t < cfg.waitPcSeconds && !(MyValue.bMyPC && MyValue.MyPC != null)) { t += Time.unscaledDeltaTime; yield return null; }
+ if (!(MyValue.bMyPC && MyValue.MyPC != null)) { Stage = "pc-timeout"; LastLog = "PC 생성 대기 초과 " + cfg.waitPcSeconds + "s · NewGameUI " + NewGameUI.isIns; WriteStatus(); yield break; }
+ Lap("pc");
+ yield return null;
+ // 시작 지점 보정 — 1차 Play 실측: PC 가 평지 모서리(28, -29)에 서 있었다(원인 미확정) → NavMesh 위 시작 지점으로 한 번 더 워프
+ if (cfg.forceSpawn != 0) { var pcA = MyValue.MyPC; pcA.Stop(true); pcA.Set_Warp(NavPos(spawn)); }
+ if (InGameInfo.Ins != null) InGameInfo.Ins.StartCam();
+ t = 0f;
+ while (t < 10f && !NewGameUI.isIns) { t += Time.unscaledDeltaTime; yield return null; }
+ if (NewGameUI.isIns && NewGameUI.Ins != null) NewGameUI.Ins.SetUI_byGameMode(eGameMode.Lobby); // 로딩 화면 닫기 + 섬과 같은 로비 UI
+ Lap("ui");
+
+ // ⑥ 무적 더미
+ yield return StartCoroutine(Co_SpawnDummies(cfg));
+
+ Ready = true;
+ LastLog = "";
+ Lap("ready");
+ }
+
+ void OnDestroy() { try { if (System.IO.File.Exists(StatusPath)) System.IO.File.Delete(StatusPath); } catch { } }
+
+ // ─────────────────────────────────────────────────────────────────
+ static Vector3 NavPos(Vector3 want)
+ {
+ UnityEngine.AI.NavMeshHit hit;
+ return UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 5f, UnityEngine.AI.NavMesh.AllAreas) ? hit.position : want;
+ }
+
+ static void ApplySky(WLSkillTestJson cfg)
+ {
+ var cam = Camera.main;
+ if (cam == null || cfg.skyColor == null || cfg.skyColor.Length < 3) return;
+ if (RenderSettings.skybox == null) { cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(cfg.skyColor[0], cfg.skyColor[1], cfg.skyColor[2]); }
+ }
+
+ void BakeGround()
+ {
+ var surf = Object.FindFirstObjectByType();
+ if (surf == null)
+ {
+ var ground = GameObject.Find("Ground");
+ if (ground == null) return;
+ surf = ground.AddComponent();
+ }
+ surf.useGeometry = UnityEngine.AI.NavMeshCollectGeometry.PhysicsColliders;
+ surf.BuildNavMesh();
+ }
+
+ IEnumerator Co_SpawnDummies(WLSkillTestJson cfg)
+ {
+ var ms = WLIslandMobSettings.Instance;
+ int mobId = cfg.dummyMobId > 0 ? cfg.dummyMobId : (ms != null ? ms.hitTestDummyMobId : 101);
+ var data = table_monsterlist.Ins.Get_Data_orNull(mobId);
+ if (data == null) { LastLog = "몬스터 표 없음 " + mobId; yield break; }
+ Vector3 off = V3(cfg.dummyOffset);
+ if (off == Vector3.zero && ms != null) off = ms.hitTestDummyOffset;
+ var pc = MyValue.MyPC;
+ int want = Mathf.Max(1, cfg.dummyCount), made = 0;
+ for (int i = 0; i < want; i++)
+ {
+ bool got = false; int idx = i;
+ AddrResourceMgr.Ins.LoadObject(data.s_MonsterPrefab, h =>
+ {
+ if (h.Status == AsyncOperationStatus.Succeeded && h.Result != null)
+ {
+ var mob = DSUtil.Get_Clone(h.Result);
+ AddrResourceMgr.Ins.Set_AddressableReleaseSelf(mob.gameObject);
+ ActorInfo.Ins.Add_Actor(mob, eRole.Mob, cfg.dummySpawnerId, data.s_MonsterPrefab);
+ mob.Set(true, null, null, cfg.dummySpawnerId, false);
+ Vector3 pos = DummyPos(pc, off, idx, cfg.dummyGap);
+ mob.Set(data, pos, null, 0);
+ if (cfg.dummyStill != 0) mob.Stop(true); // 제자리 더미(쫓아오지 않음) — 0 이면 섬처럼 움직인다
+ if (idx == 0) { WLTrailTest.Adopt(mob); Dummy = mob; }
+ var keep = mob.GetComponent();
+ if (keep == null) mob.gameObject.AddComponent().Begin(mob); // 2마리째부터도 체력 되채움
+ made++;
+ }
+ got = true;
+ });
+ while (!got) yield return null;
+ }
+ Lap("dummy " + made + "(" + mobId + ")");
+ }
+
+ static Vector3 DummyPos(Actor pc, Vector3 off, int idx, float gap)
+ {
+ Vector3 basePos = pc != null ? pc.Get_position() : Vector3.zero;
+ Quaternion rot = pc != null ? Quaternion.Euler(0f, pc.transform.eulerAngles.y, 0f) : Quaternion.identity;
+ Vector3 side = rot * Vector3.right;
+ Vector3 want = basePos + rot * off + side * (gap * ((idx + 1) / 2) * (idx % 2 == 0 ? 1f : -1f)) * (idx == 0 ? 0f : 1f);
+ UnityEngine.AI.NavMeshHit hit;
+ if (UnityEngine.AI.NavMesh.SamplePosition(want, out hit, 3f, UnityEngine.AI.NavMesh.AllAreas)) want = hit.position;
+ return want;
+ }
+
+ /// 더미를 PC 앞 기본 자리로 되돌린다(R 키 · 프로브).
+ public static string ResetDummy()
+ {
+ var pc = MyValue.MyPC; var d = Dummy;
+ if (pc == null || d == null) return "PC/더미 없음";
+ var ms = WLIslandMobSettings.Instance; var cfg = Cfg;
+ Vector3 off = V3(cfg.dummyOffset); if (off == Vector3.zero && ms != null) off = ms.hitTestDummyOffset;
+ var pos = DummyPos(pc, off, 0, cfg.dummyGap);
+ d.Stop(true); d.Set_Warp(pos);
+ return "더미 " + pos.ToString("F1");
+ }
+
+ /// 낀 무기를 인벤토리 무기 목록에서 앞뒤로 바꾼다(dir ±1 · 결과 한 줄).
+ public static string CycleWeapon(int dir)
+ {
+ if (!WLInventory.EnsureLoaded()) return "인벤토리 미로드";
+ var cur = WLInventory.EquippedInSlot(eFREquipmentType.Weapon);
+ var list = new List(); var names = new List(); int curIdx = -1;
+ foreach (var it in WLInventory.SortedItems())
+ {
+ if (it == null || it.itemJson == null || !it.itemJson.IsEquipment || it.itemJson.EquipmentType != eFREquipmentType.Weapon) continue;
+ if (cur != null && ReferenceEquals(it, cur)) curIdx = list.Count;
+ list.Add(it.rawItem._id); names.Add(it.itemJson.Item_name + "(" + it.itemJson.GroupNo + ")");
+ }
+ if (list.Count == 0) return "무기 없음";
+ int next = curIdx < 0 ? 0 : (curIdx + dir + list.Count) % list.Count;
+ bool ok = WLInventory.Equip(list[next]);
+ return (ok ? "장착 " : "실패 ") + names[next] + " " + (next + 1) + "/" + list.Count + (ok ? "" : " · " + WLInventory.LastMessage);
+ }
+
+ /// 보고·프로브용 한 줄.
+ public static string Describe()
+ {
+ var pc = MyValue.MyPC;
+ string w = "-";
+ if (WLInventory.Loaded) { var cur = WLInventory.EquippedInSlot(eFREquipmentType.Weapon); if (cur != null && cur.itemJson != null) w = cur.itemJson.Item_name + "(" + cur.itemJson.GroupNo + ")"; }
+ return "stage=" + Stage + " ready=" + Ready + " pc=" + (pc != null ? pc.name + pc.Get_position().ToString("F1") : "-") + " weapon=" + w +
+ " dummy=" + (Dummy != null ? Dummy.name + Dummy.Get_position().ToString("F1") + " hp " + Dummy.Get_HP().ToString("F0") : "-") +
+ " laps=[" + string.Join(" · ", Laps.ToArray()) + "] pf=[" + string.Join(" · ", PfLog.ToArray()) + "] hangs=[" + string.Join(" ", Hangs.ToArray()) + "] " + LastLog
+#if UNITY_EDITOR
+ + (string.IsNullOrEmpty(ProfileDump) ? "" : " profile=[" + ProfileDump + "]")
+#endif
+ ;
+ }
+
+ // ─────────────────────────────────────────────────────────────────
+ void Update()
+ {
+ if (!Ready) return;
+ var pc = MyValue.MyPC as PCActor;
+ if (pc == null) return;
+ var cfg = Cfg;
+
+ if (cfg.moveKeys != 0)
+ {
+ float h = (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow) ? 1f : 0f) - (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow) ? 1f : 0f);
+ float v = (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow) ? 1f : 0f) - (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow) ? 1f : 0f);
+ bool any = h != 0f || v != 0f;
+ if (any)
+ {
+ if (!_moving) { pc.Set_AutoBraking(false); pc.Set_JoystickStatus(1); _moving = true; }
+ var cam = Camera.main;
+ Vector3 fwd = cam != null ? Vector3.ProjectOnPlane(cam.transform.forward, Vector3.up) : Vector3.forward;
+ if (fwd.sqrMagnitude < 1e-4f) fwd = Vector3.forward;
+ fwd.Normalize();
+ Vector3 right = Vector3.Cross(Vector3.up, fwd);
+ pc.Run_byJoystick((fwd * v + right * h).normalized);
+ }
+ else if (_moving) { pc.Set_JoystickStatus(0); pc.Set_AutoBraking(true); _moving = false; }
+ }
+
+ if (Input.GetKeyDown(_kPrev)) _weaponMsg = CycleWeapon(-1);
+ if (Input.GetKeyDown(_kNext)) _weaponMsg = CycleWeapon(+1);
+ if (Input.GetKeyDown(_kReset)) _weaponMsg = ResetDummy();
+ }
+
+ void OnGUI()
+ {
+ if (Cfg.label == 0) return;
+ if (_style == null) { _style = new GUIStyle(GUI.skin.label); _style.alignment = TextAnchor.UpperLeft; _style.wordWrap = true; }
+ _style.fontSize = Mathf.Max(10, Mathf.RoundToInt(Screen.height * Cfg.labelFontScale));
+ _style.normal.textColor = Ready ? Color.white : new Color(1f, 0.9f, 0.5f);
+ string w = "-";
+ if (WLInventory.Loaded) { var cur = WLInventory.EquippedInSlot(eFREquipmentType.Weapon); if (cur != null && cur.itemJson != null) w = cur.itemJson.Item_name + "(" + cur.itemJson.GroupNo + ")"; }
+ string s = "WL 스킬 테스트 · " + Stage + (Ready ? "" : " …") +
+ "\n무기 " + w + " 더미 " + (Dummy != null ? Dummy.Get_HP().ToString("F0") : "-") +
+ "\nWASD 이동 · [ ] 무기 · R 더미 · Q 주력 · E 각성 · 숫자 등급" +
+ (string.IsNullOrEmpty(_weaponMsg) ? "" : "\n" + _weaponMsg) +
+ (Ready ? "\n" + string.Join(" · ", Laps.ToArray()) : "");
+ var r = new Rect(Screen.width * 0.01f, Screen.height * 0.01f, Screen.width * 0.6f, Screen.height * 0.3f);
+ GUI.color = new Color(0f, 0f, 0f, 0.6f); GUI.Label(new Rect(r.x + 1, r.y + 1, r.width, r.height), s, _style);
+ GUI.color = Color.white; GUI.Label(r, s, _style);
+ }
+ }
+}
diff --git a/Assets/WL/SkillTest/WLSkillTestBoot.cs.meta b/Assets/WL/SkillTest/WLSkillTestBoot.cs.meta
new file mode 100644
index 000000000..6e69858d8
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTestBoot.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: d2262201ce81fd5449555b2f0fa8dfed
\ No newline at end of file
diff --git a/Assets/WL/SkillTest/WLSkillTestGround.mat b/Assets/WL/SkillTest/WLSkillTestGround.mat
new file mode 100644
index 000000000..15a67520f
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTestGround.mat
@@ -0,0 +1,137 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &-7617188927419010439
+MonoBehaviour:
+ m_ObjectHideFlags: 11
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
+ m_Name:
+ m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
+ version: 10
+--- !u!21 &2100000
+Material:
+ serializedVersion: 8
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_Name: WLSkillTestGround
+ m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
+ m_Parent: {fileID: 0}
+ m_ModifiedSerializedProperties: 0
+ m_ValidKeywords: []
+ m_InvalidKeywords: []
+ m_LightmapFlags: 4
+ m_EnableInstancingVariants: 0
+ m_DoubleSidedGI: 0
+ m_CustomRenderQueue: -1
+ stringTagMap:
+ RenderType: Opaque
+ disabledShaderPasses:
+ - MOTIONVECTORS
+ m_LockedProperties:
+ m_SavedProperties:
+ serializedVersion: 3
+ m_TexEnvs:
+ - _BaseMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _BumpMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailAlbedoMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailMask:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _DetailNormalMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _EmissionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MainTex:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _MetallicGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _OcclusionMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _ParallaxMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - _SpecGlossMap:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_Lightmaps:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_LightmapsInd:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ - unity_ShadowMasks:
+ m_Texture: {fileID: 0}
+ m_Scale: {x: 1, y: 1}
+ m_Offset: {x: 0, y: 0}
+ m_Ints: []
+ m_Floats:
+ - _AddPrecomputedVelocity: 0
+ - _AlphaClip: 0
+ - _AlphaToMask: 0
+ - _Blend: 0
+ - _BlendModePreserveSpecular: 1
+ - _BumpScale: 1
+ - _ClearCoatMask: 0
+ - _ClearCoatSmoothness: 0
+ - _Cull: 2
+ - _Cutoff: 0.5
+ - _DetailAlbedoMapScale: 1
+ - _DetailNormalMapScale: 1
+ - _DstBlend: 0
+ - _DstBlendAlpha: 0
+ - _EnvironmentReflections: 1
+ - _GlossMapScale: 0
+ - _Glossiness: 0
+ - _GlossyReflections: 0
+ - _Metallic: 0
+ - _OcclusionStrength: 1
+ - _Parallax: 0.005
+ - _QueueOffset: 0
+ - _ReceiveShadows: 1
+ - _Smoothness: 0.5
+ - _SmoothnessTextureChannel: 0
+ - _SpecularHighlights: 1
+ - _SrcBlend: 1
+ - _SrcBlendAlpha: 1
+ - _Surface: 0
+ - _WorkflowMode: 1
+ - _XRMotionVectorsPass: 1
+ - _ZWrite: 1
+ m_Colors:
+ - _BaseColor: {r: 0.45, g: 0.62, b: 0.35, a: 1}
+ - _Color: {r: 0.45, g: 0.62, b: 0.34999996, a: 1}
+ - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+ - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
+ m_BuildTextureStacks: []
+ m_AllowLocking: 1
diff --git a/Assets/WL/SkillTest/WLSkillTestGround.mat.meta b/Assets/WL/SkillTest/WLSkillTestGround.mat.meta
new file mode 100644
index 000000000..2c066b9a2
--- /dev/null
+++ b/Assets/WL/SkillTest/WLSkillTestGround.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bb102d8fd6df38040a18663db0f2504f
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant: