WL #947 스킬 테스트 씬 신설 — Assets/WL/SkillTest(WLSkillTest.unity 코드 조립 · WLSkillTestBoot 부트: 매니저 프리팹 직접 배치 → 테이블 → 개발 로그인 → 맵 노드·카메라 리그 → InGameInfo/ProjectileInfo/PCInfo → SceneInfo.CurStageData 선채움(섬 씬 미로드) → PC·HUD → 무적 더미 · WASD/[ ]/R 키 · 상태 파일) · 원본 0줄 · 테이블 0줄

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-10-20 01:12:04 +09:00
parent d8f6cec099
commit d51fb12ed1
14 changed files with 2067 additions and 0 deletions

8
Assets/WL/SkillTest.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fbf99ff76a4410847858e3565d85eea1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fe25a56d1c4ad2845b39d29dcaac17ec
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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()); }
/// <summary>테스트 씬을 연다(Single). 현재 씬이 저장 안 된 상태면 열지 않고 알린다(K-WL-114).</summary>
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;
}
/// <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 "빈 씬으로 바꿈";
}
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>();
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<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); // NavMesh 는 부트가 콜라이더 기준 런타임 베이크
// 매니저(Title 의 Mgr 과 같은 구성)
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>();
// Title 이 Addressables 로 올리던 프리팹 → 씬에 직접
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;
}
// 원본 인게임 카메라(RealCamera · 태그 MainCamera)
var camP = AssetDatabase.LoadAssetAtPath<GameObject>(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<InGameTouchInfo>();
new GameObject("DungeonInfo").AddComponent<DungeonInfo>();
new GameObject("DropItemInfo").AddComponent<DropItemInfo>();
var gv = new GameObject("Global Volume");
gv.AddComponent<GlobalVolumeMgr>();
var vol = gv.AddComponent<Volume>();
vol.isGlobal = true; vol.enabled = false; // InGame.unity 실측: Volume 컴포넌트 꺼짐
var profPath = AssetDatabase.GUIDToAssetPath(VolumeProfileGuid);
if (!string.IsNullOrEmpty(profPath)) vol.sharedProfile = AssetDatabase.LoadAssetAtPath<VolumeProfile>(profPath);
// ProjectileInfo 는 씬에 두지 않는다 — 부트가 InGameInfo 뒤에 원본 프리팹으로 만든다(1차 Play: 예열 투사체가 InGameInfo.Ins null 로 NRE 127건)
// 부트
var bootGo = new GameObject("WLSkillTestBoot");
var boot = bootGo.AddComponent<WLSkillTestBoot>();
boot.inGameInfoPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(InGameInfoPrefab);
boot.pcInfoPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(PCInfoPrefab);
boot.projectileInfoPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(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();
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 87ce98ef730cc604bacdad3a67282092

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6c1e9723645442b48a842cdfe5980b98
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a26aaa0b1ac0c404096587f6ba76614c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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]
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bb979365e442a1241be2d7b5975bf741
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: e7b4ccbbf2b0ad0409dfdd76b3d350cd
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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<string> Laps = new List<string>();
/// <summary>PlayFab 호출 시각 기록(에디터 · 로딩 실측용 · 2차 Play 에서 로그인 52 s 의 내역을 보려고)</summary>
public static readonly List<string> PfLog = new List<string>();
/// <summary>0.5 s 넘게 멈춘 프레임 기록(시각@길이 · 메인 스레드가 무엇에 막혔는지 PfLog·Laps 와 맞춰 본다)</summary>
public static readonly List<string> Hangs = new List<string>();
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
/// <summary>진단: 프로파일러로 잡은 긴 프레임의 가장 무거운 호출 경로(멈춤의 정체)</summary>
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<int>();
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<TextAsset>(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; }
/// <summary>상태 파일(Library/ 아래 · 미추적) — 외부 스크립트는 Play 중 `eval` 대신 이 파일을 읽는다. 실측(8차 Play): Play 뒤 첫 `unity command eval/run_script` 가 EditorLoop 에서 약 35 s 메인 스레드를 멈춘다(스크립트 컴파일 준비).</summary>
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<UnityEngine.U2D.SpriteAtlas>("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<WLDungeonCameraRig>() == 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<NavMeshSurface>();
if (surf == null)
{
var ground = GameObject.Find("Ground");
if (ground == null) return;
surf = ground.AddComponent<NavMeshSurface>();
}
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<GameObject>(data.s_MonsterPrefab, h =>
{
if (h.Status == AsyncOperationStatus.Succeeded && h.Result != null)
{
var mob = DSUtil.Get_Clone<MobActor>(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<WLTrailTestDummy>();
if (keep == null) mob.gameObject.AddComponent<WLTrailTestDummy>().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;
}
/// <summary>더미를 PC 앞 기본 자리로 되돌린다(R 키 · 프로브).</summary>
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");
}
/// <summary>낀 무기를 인벤토리 무기 목록에서 앞뒤로 바꾼다(dir ±1 · 결과 한 줄).</summary>
public static string CycleWeapon(int dir)
{
if (!WLInventory.EnsureLoaded()) return "인벤토리 미로드";
var cur = WLInventory.EquippedInSlot(eFREquipmentType.Weapon);
var list = new List<int>(); var names = new List<string>(); 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);
}
/// <summary>보고·프로브용 한 줄.</summary>
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);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d2262201ce81fd5449555b2f0fa8dfed

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bb102d8fd6df38040a18663db0f2504f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant: