[WL-814d] 인게임 룩 토글: 카메라 모드 순환(원근/도트 3프리셋) · 배경 룩 훅 · HUD 버튼 2 (#814)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
81bb66a538
commit
03e3817cd2
|
|
@ -0,0 +1,34 @@
|
||||||
|
// WL-814d — WLLookModeSettings.asset 생성(C# 기본값 그대로 · 필드 누락 0).
|
||||||
|
// unity command run_script --file AgentScripts/WL814d_MakeAsset.cs --entry WL814d_MakeAsset.Run --project-path E:\NerdNavis\WL_wt\ui
|
||||||
|
// 🔴 게임 코드 아님(Assets 밖 도구). 손으로 YAML 을 쓰면 필드가 조용히 빠진다(811o 교훈).
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using WL.Look.Toggle;
|
||||||
|
|
||||||
|
public static class WL814d_MakeAsset
|
||||||
|
{
|
||||||
|
const string kDir = "Assets/WL/Look/Toggle/Resources/WL";
|
||||||
|
const string kPath = kDir + "/WLLookModeSettings.asset";
|
||||||
|
|
||||||
|
public static string Run()
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(Directory.GetParent(Application.dataPath).FullName, kDir));
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
|
||||||
|
var existing = AssetDatabase.LoadAssetAtPath<WLLookModeSettings>(kPath);
|
||||||
|
if (existing != null) return "이미 있음 — " + kPath + " · 모드 " + existing.ModeCount;
|
||||||
|
|
||||||
|
var so = ScriptableObject.CreateInstance<WLLookModeSettings>();
|
||||||
|
AssetDatabase.CreateAsset(so, kPath);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
WLLookModeSettings.ClearCache();
|
||||||
|
|
||||||
|
var loaded = Resources.Load<WLLookModeSettings>(WLLookModeSettings.ResourcesPath);
|
||||||
|
return "생성 " + kPath + " · Resources 로드=" + (loaded != null) +
|
||||||
|
" · 모드 " + (loaded != null ? loaded.ModeCount : 0) +
|
||||||
|
" · guid " + AssetDatabase.AssetPathToGUID(kPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,182 @@
|
||||||
|
# WL814d Probe 2026-09-11 00:19:58 (edit mode · Play 0 · 로그인 0)
|
||||||
|
playMode=False
|
||||||
|
현재 씬 = '' dirty(before)=False rootCount=0
|
||||||
|
|
||||||
|
── A. 에셋 · 계약 (814d SO · 814b SO · 811o SO)
|
||||||
|
[PASS] WLLookModeSettings.asset 로드(Resources/WL/WLLookModeSettings)
|
||||||
|
[PASS] enabled_ = 1
|
||||||
|
[PASS] 모드 4개 — 실측 4
|
||||||
|
| 모드 | 이름 | 리그 | SO구도 | cameraHeight | distance | distanceUp | orthoSize | pixelHeight | envLookOn |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
| 0 | 기본 | 원근 | O | 76 | 3 | 2.4 | 3.8 | 480 | 0 |
|
||||||
|
| 1 | 도트A | 직교 | - | 74 | 3.4 | 2.2 | 3.8 | 480 | 1 |
|
||||||
|
| 2 | 도트B | 직교 | - | 55 | 4.5 | 1.8 | 4.2 | 480 | 1 |
|
||||||
|
| 3 | 도트C | 직교 | - | 45 | 5.5 | 1.5 | 4.6 | 480 | 1 |
|
||||||
|
[PASS] 0번 = 원근 + 811o SO 구도 그대로(리그 해제)
|
||||||
|
[PASS] 1~3번 = 도트(직교 리그)
|
||||||
|
[PASS] 순환 0→1→2→3→0 (Wrap(4)=0 Wrap(-1)=3)
|
||||||
|
814b WLPixelCameraSettings: enabled=False orthoSize=3.8 pixelHeight=480 prefab=True
|
||||||
|
[PASS] 🔴 814b SO enabled = 0(F-23) — 강제 조립 API 가 필요한 이유(실측)
|
||||||
|
[PASS] 814b 리그 프리팹 배선됨(URP 패키지 해제 확인)
|
||||||
|
811o WLCameraFramingSettings: enabled=True framingEnabled=True h=76 d=3 up=2.4 poll=0.25s
|
||||||
|
[PASS] 811o 구도 오버라이드가 켜져 있다(런타임 프리셋의 전제)
|
||||||
|
|
||||||
|
── B. 임시 씬 — 원본 Main Camera(RealCamera 포함) + UICamera(스택 오버레이)
|
||||||
|
[PASS] 원본 카메라 프리팹 로드 Assets/ResWork/Prefabs/Ingame/Main Camera.prefab
|
||||||
|
[PASS] Camera + RealCamera 보유
|
||||||
|
[PASS] tag = MainCamera
|
||||||
|
[PASS] Camera.main 이 이 카메라로 해석된다(리그 강제 조립의 전제) — 실측 __WL814d_MainCamera
|
||||||
|
프리팹 arr_cCamData[0] h=65 min=3 max=3 up=2 · [1](보스 프리셋) h=78
|
||||||
|
원본 카메라 orthographic=False fov=60 cullingMask=95 depth=5
|
||||||
|
[PASS] 원본 스택 = [UICamera] (NewGameUI.cs:38 재현)
|
||||||
|
[PASS] InGameInfo.m_GameMode 리플렉션(프로브 전용 · 게임 코드 0)
|
||||||
|
[PASS] 인게임(Stage) 판정 — LookModeHub.InLobby=False
|
||||||
|
|
||||||
|
── C. HUD 버튼 — NewGameUI 프리팹(저장 0) · Safe Area · 좌표 표
|
||||||
|
[PASS] LoadPrefabContents(Assets/Res_Addr/MainUI/NewGameUI.prefab)
|
||||||
|
[PASS] IngameUIs/WL_HUD 경로 실측
|
||||||
|
[PASS] WL_HUD 가 SafeAreaFitter 아래 = Safe Area 안 (802b/813y 실측 재확인)
|
||||||
|
Bind: 연결 IngameUIs/WL_HUD · pos(-42.67, -835.56) size(355.56, 248.89) 버튼 200×64px unitsPerPx=1.7778 · sortingOrder=10
|
||||||
|
[PASS] WL_LookToggle 노드 생성(HideFlags.DontSave)
|
||||||
|
[PASS] 전용 Canvas sortingOrder=10 — 813q2 결과창 40 · 813z 부활 60 보다 아래
|
||||||
|
조립 직후 overrideSorting=False (IngameUIs 활성=False)
|
||||||
|
[PASS] IngameUIs 활성 → overrideSorting=True sortingOrder=10(중첩 캔버스로 인정 · Tick 이 다시 건다)
|
||||||
|
활성 첫 프레임 재배치: [LookToggleUi] bound=True visible=True sortingOrder=10 · 카메라 "카메라: 기본" 배경 "배경: OFF" · 클릭 0/0 라벨쓰기 2 · rect0 (x:856.00, y:470.00, width:200.00, height:64.00) rect1 (x:856.00, y:546.00, width:200.00, height:64.00) · 노드 pos(-42.67, -835.56) size(355.56, 248.89) SafeArea=True · showButtons=True top=470px right=24px
|
||||||
|
Layout: pos(-42.67, -835.56) size(355.56, 248.89) 버튼 200×64px unitsPerPx=1.7778
|
||||||
|
[LookToggleUi] bound=True visible=True sortingOrder=10 · 카메라 "카메라: 기본" 배경 "배경: OFF" · 클릭 0/0 라벨쓰기 2 · rect0 (x:856.00, y:470.00, width:200.00, height:64.00) rect1 (x:856.00, y:546.00, width:200.00, height:64.00) · 노드 pos(-42.67, -835.56) size(355.56, 248.89) SafeArea=True · showButtons=True top=470px right=24px
|
||||||
|
루트 캔버스 = NewGameUI scaler=ScaleWithScreenSize ref=(1920.00, 1080.00) match=Expand → unitsPerPx=1.7778
|
||||||
|
[PASS] unitsPerPx = 1.7778 (813q·802b 실측과 같은 계수) — 실측 1.7778
|
||||||
|
[PASS] 노드 폭 = 200 px × 1.7778 = 355.56 유닛 — 실측 355.56
|
||||||
|
[PASS] WL_LookToggle 노드 탐색
|
||||||
|
[PASS] 버튼 배경 2개 전부 raycastTarget = 터치 통과 0 — 실측 2/2
|
||||||
|
[PASS] 라벨은 raycastTarget 0(버튼이 먹는다) — 실측 0
|
||||||
|
[PASS] 노드 hideFlags = DontSave(프리팹에 굽지 않는다)
|
||||||
|
|
||||||
|
| 요소 | 소유 | 상단 y(px) | 하단 y(px) | 좌 x(px) | 우 x(px) |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 상단 재화 바 | 프리팹(#729) | 130 | 176 | (프리팹) | (프리팹) |
|
||||||
|
| 보스 HP 바 | 813c3 | 190 | 256 | 100 | 980 |
|
||||||
|
| 런 HUD | 813q | 272 | 394 | 260 | 820 |
|
||||||
|
| 보스 배너(1.2 s) | 813tj | 300 | 450 | 110 | 970 |
|
||||||
|
| **[카메라] 버튼** | **814d** | **470** | **534** | **856** | **1056** |
|
||||||
|
| **[배경] 버튼** | **814d** | **546** | **610** | **856** | **1056** |
|
||||||
|
|
||||||
|
[PASS] 버튼 크기 ≥ 96×48 px — 실측 200×64
|
||||||
|
[PASS] 보스 HP 바(≤256)와 겹침 0 · 여유 214 px
|
||||||
|
[PASS] 런 HUD(≤394)와 겹침 0 · 여유 76 px
|
||||||
|
[PASS] 보스 배너(≤450)와 겹침 0 · 여유 20 px
|
||||||
|
[PASS] 런 HUD 폭(우 820)보다 오른쪽 · 가로 여유 36 px
|
||||||
|
[PASS] 두 버튼이 화면 안 — 아래끝 610 px
|
||||||
|
결과창(813q2 sortingOrder 40) · 부활 팝업(813z 60) 은 이 버튼(10)보다 위 = 팝업이 뜨면 버튼이 가려진다(겹침 대신 덮임).
|
||||||
|
|
||||||
|
[참고] NewGameUI 캔버스 직속 노드(이름 · active):
|
||||||
|
- New Image (active=False children=0)
|
||||||
|
- Camera (active=True children=0)
|
||||||
|
- Floating Joystick (active=True children=1)
|
||||||
|
- CamRot (active=True children=0)
|
||||||
|
- WL_SafeArea_Back (active=True children=2)
|
||||||
|
- IngameUIs (active=False children=7)
|
||||||
|
- FarmUIs (active=False children=3)
|
||||||
|
- WL_SafeArea_Front (active=True children=3)
|
||||||
|
- TestMapUIMgr (active=True children=6)
|
||||||
|
|
||||||
|
── D. 모드 0→1→2→3→0 — 리그 · arr_cCamData · 원본 카메라 · 버튼 라벨
|
||||||
|
|
||||||
|
| 모드 | 이름 | 리그조립 | 원본 ortho | orthoSize | RT | arr[0] h/min/max/up | 라이브 h/max/up | 내림각 | 스택 | 카메라 라벨 | 배경 라벨 |
|
||||||
|
|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
|
[PASS] step 0 → 모드 0 (실측 0)
|
||||||
|
| 0 | 기본 | - | False | 5 | — | 76/3/3/2.4 | 76/3/2.4 | 36.8° | 1 | 카메라: 기본 | 배경: OFF |
|
||||||
|
[PASS] 모드 0 리그 조립 = False (실측 False)
|
||||||
|
[PASS] 모드 0 원본 카메라 orthographic = False (실측 False)
|
||||||
|
[PASS] 모드 0 런타임 프리셋 해제 = 811o SO 값으로 복귀
|
||||||
|
[PASS] 모드 0 구도 = 811o SO(h 76 · d 3) — 실측 76/3
|
||||||
|
[PASS] 모드 0 URP 스택 = [UICamera] 1개(리그 해제 후 원복) — 실측 1
|
||||||
|
[PASS] 모드 0 카메라 라벨 = "카메라: 기본" (실측 "카메라: 기본")
|
||||||
|
[PASS] 보스 프리셋 arr_cCamData[1] 무변경 — h 78(시작 78)
|
||||||
|
[PASS] step 1 → 모드 1 (실측 1)
|
||||||
|
| 1 | 도트A | O | True | 3.8 | 640×480 | 74/3.4/3.4/2.2 | 74/3.4/2.2 | 32.7° | 2 | 카메라: 도트A | 배경: ON |
|
||||||
|
[PASS] 모드 1 리그 조립 = True (실측 True)
|
||||||
|
[PASS] 모드 1 원본 카메라 orthographic = True (실측 True)
|
||||||
|
[PASS] 모드 1 orthographicSize = 3.8 (실측 3.8)
|
||||||
|
[PASS] 모드 1 RT 세로 = pixelHeight 480 (실측 480)
|
||||||
|
[PASS] 모드 1 구도 = h 74 · d 3.4 · up 2.2 (실측 74/3.4/2.2)
|
||||||
|
[PASS] 모드 1 URP 스택 = [ViewCamera, UICamera] 2개(UI 소실 0) — 실측 2
|
||||||
|
[PASS] 모드 1 UICamera 가 스택 맨 뒤(UI 가 맨 위에 그려진다)
|
||||||
|
[PASS] 모드 1 카메라 라벨 = "카메라: 도트A" (실측 "카메라: 도트A")
|
||||||
|
[PASS] 보스 프리셋 arr_cCamData[1] 무변경 — h 78(시작 78)
|
||||||
|
[PASS] step 2 → 모드 2 (실측 2)
|
||||||
|
| 2 | 도트B | O | True | 4.2 | 640×480 | 55/4.5/4.5/1.8 | 55/4.5/1.8 | 13.4° | 2 | 카메라: 도트B | 배경: ON |
|
||||||
|
[PASS] 모드 2 리그 조립 = True (실측 True)
|
||||||
|
[PASS] 모드 2 원본 카메라 orthographic = True (실측 True)
|
||||||
|
[PASS] 모드 2 orthographicSize = 4.2 (실측 4.2)
|
||||||
|
[PASS] 모드 2 RT 세로 = pixelHeight 480 (실측 480)
|
||||||
|
[PASS] 모드 2 구도 = h 55 · d 4.5 · up 1.8 (실측 55/4.5/1.8)
|
||||||
|
[PASS] 모드 2 URP 스택 = [ViewCamera, UICamera] 2개(UI 소실 0) — 실측 2
|
||||||
|
[PASS] 모드 2 UICamera 가 스택 맨 뒤(UI 가 맨 위에 그려진다)
|
||||||
|
[PASS] 모드 2 카메라 라벨 = "카메라: 도트B" (실측 "카메라: 도트B")
|
||||||
|
[PASS] 보스 프리셋 arr_cCamData[1] 무변경 — h 78(시작 78)
|
||||||
|
[PASS] step 3 → 모드 3 (실측 3)
|
||||||
|
| 3 | 도트C | O | True | 4.6 | 640×480 | 45/5.5/5.5/1.5 | 45/5.5/1.5 | 2.6° | 2 | 카메라: 도트C | 배경: ON |
|
||||||
|
[PASS] 모드 3 리그 조립 = True (실측 True)
|
||||||
|
[PASS] 모드 3 원본 카메라 orthographic = True (실측 True)
|
||||||
|
[PASS] 모드 3 orthographicSize = 4.6 (실측 4.6)
|
||||||
|
[PASS] 모드 3 RT 세로 = pixelHeight 480 (실측 480)
|
||||||
|
[PASS] 모드 3 구도 = h 45 · d 5.5 · up 1.5 (실측 45/5.5/1.5)
|
||||||
|
[PASS] 모드 3 URP 스택 = [ViewCamera, UICamera] 2개(UI 소실 0) — 실측 2
|
||||||
|
[PASS] 모드 3 UICamera 가 스택 맨 뒤(UI 가 맨 위에 그려진다)
|
||||||
|
[PASS] 모드 3 카메라 라벨 = "카메라: 도트C" (실측 "카메라: 도트C")
|
||||||
|
[PASS] 보스 프리셋 arr_cCamData[1] 무변경 — h 78(시작 78)
|
||||||
|
[PASS] step 4 → 모드 0 (실측 0)
|
||||||
|
| 0 | 기본 | - | False | 5 | — | 76/3/3/2.4 | 76/3/2.4 | 36.8° | 1 | 카메라: 기본 | 배경: OFF |
|
||||||
|
[PASS] 모드 0 리그 조립 = False (실측 False)
|
||||||
|
[PASS] 모드 0 원본 카메라 orthographic = False (실측 False)
|
||||||
|
[PASS] 모드 0 런타임 프리셋 해제 = 811o SO 값으로 복귀
|
||||||
|
[PASS] 모드 0 구도 = 811o SO(h 76 · d 3) — 실측 76/3
|
||||||
|
[PASS] 모드 0 URP 스택 = [UICamera] 1개(리그 해제 후 원복) — 실측 1
|
||||||
|
[PASS] 모드 0 카메라 라벨 = "카메라: 기본" (실측 "카메라: 기본")
|
||||||
|
[PASS] 보스 프리셋 arr_cCamData[1] 무변경 — h 78(시작 78)
|
||||||
|
|
||||||
|
── D-2. 배경 룩 버튼(814c 연동 훅) — 플래그 + 이벤트만
|
||||||
|
[PASS] [배경] 탭 → 플래그 뒤집힘 False → True
|
||||||
|
[PASS] EnvLookChanged 1회 발행(814c 구독 지점) — 실측 1회 값=True
|
||||||
|
[PASS] 배경 라벨 = "배경: ON"
|
||||||
|
[PASS] 다시 탭 → 원래대로 · 이벤트 2회
|
||||||
|
클릭 카운트: 카메라 4 · 배경 2 · 라벨 쓰기 10회
|
||||||
|
[PASS] envFollowsCameraMode 1 → 도트 모드에서 배경 룩 자동 ON (실측 True · 이벤트 1회)
|
||||||
|
[PASS] 기본 모드로 돌아오면 배경 룩 자동 OFF (실측 False)
|
||||||
|
|
||||||
|
── E. 세션 유지 — 로비 강등 · 인게임 복원 · 맵 재진입 재조립
|
||||||
|
[PASS] 인게임(Stage) 판정
|
||||||
|
[PASS] 인게임 모드 2 적용 — applied=2 리그=True h=55 ortho=4.2
|
||||||
|
[PASS] 로비 → 카메라 기본 복귀(적용 0 · 리그 해제) · 고른 모드 2 유지 — 실측 mode=2 applied=0 리그=False ortho=False h=76
|
||||||
|
[PASS] 인게임 재진입 → 모드 2 자동 복원 — ortho=4.2 h=55 (로비강등 1회)
|
||||||
|
[PASS] 맵 이탈 → 리그 해제되지만 오버라이드 플래그는 유지(다음 진입에서 같은 값으로 재조립) — 실측 True
|
||||||
|
[PASS] 맵 재진입 → 같은 값으로 재조립 ortho=4.2
|
||||||
|
[LookMode] 고른모드=2(도트B) 적용모드=2 배경룩=True · 리그조립=True 리그오버라이드=True · 구도적용=True 런타임프리셋=True(h 55 · d 4.5 · up 1.8) · 적용 9회 · 모드변경 7 · 배경변경 7 · 로비강등 1
|
||||||
|
|
||||||
|
── F. GC — 전환 외 프레임당 할당 0
|
||||||
|
[PASS] 허브 Tick + UI Tick ×200 → Δ0 B (gen0 233→233)
|
||||||
|
[PASS] 라벨 쓰기 0회(값이 그대로면 안 쓴다) — 실측 0
|
||||||
|
[PASS] 적용 0회(모드가 그대로면 두 기능을 건드리지 않는다) — 실측 0
|
||||||
|
[PASS] 대조군 16 MB 할당 감지 Δ16781312 B = 계기 감도 확인
|
||||||
|
|
||||||
|
── G. C8 — WLLookModeSettings 를 끄면 814b/811o 가 원래대로
|
||||||
|
[PASS] 끄기 전 = 도트 모드 · 두 오버라이드 on
|
||||||
|
[PASS] WLLookModeSettings.Enabled = false
|
||||||
|
[PASS] 814b 리그 오버라이드 0 · 리그 해제 = SO(enabled 0) 원래 동작
|
||||||
|
[PASS] 811o 런타임 프리셋 0 = SO 값 경로 그대로
|
||||||
|
[PASS] arr_cCamData[0] = 811o SO 값 h 76 / d 3 / up 2.4 — 실측 76/3/2.4
|
||||||
|
[PASS] 원본 카메라 원근 복귀 — orthographic=False
|
||||||
|
[PASS] 버튼 숨김 — visible=False
|
||||||
|
[PASS] 적용 모드 0 고정 — 실측 0
|
||||||
|
[PASS] showButtons = 0 → 버튼만 숨김(모드 전환은 그대로) — visible=False
|
||||||
|
[PASS] 되돌리면 다시 보인다
|
||||||
|
|
||||||
|
── Z. 정리 — 오버라이드 해제 · 노드 파괴 · 프리팹 저장 0
|
||||||
|
리그 조립=False 오버라이드=False · 구도 런타임프리셋=False 적용=False
|
||||||
|
teardown 완료
|
||||||
|
남은 카메라 0개 · InGameInfo.Ins=False
|
||||||
|
NewGameUI.prefab 저장 0(UnloadPrefabContents(false) 기본) · 에셋 쓰기 0
|
||||||
|
|
||||||
|
RESULT PASS (pass=106 fail=0)
|
||||||
|
씬 정리 후: dirty=False rootCount=0 (저장 0)
|
||||||
|
|
@ -0,0 +1,542 @@
|
||||||
|
// WL-814d 프로브 — 인게임 룩 토글(카메라 모드 순환 + 배경 룩 훅) 실측 (에디트 모드 · Play 0 · 로그인 0).
|
||||||
|
// unity command run_script --file AgentScripts/WL814d_Probe.cs --entry WL814d_Probe.Edit --project-path E:\NerdNavis\WL_wt\ui
|
||||||
|
// 산출물: <worktree>/AgentScripts/WL814d_PROBE.txt
|
||||||
|
// 🔴 NewGameUI.prefab 은 LoadPrefabContents 로 열고 **저장하지 않는다**(UnloadPrefabContents(false)).
|
||||||
|
// 🔴 씬에 만드는 것은 전부 HideFlags.DontSave + DestroyImmediate · 씬 저장 0 · 에셋 쓰기 0.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering.Universal;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using WL.CameraFraming;
|
||||||
|
using WL.Look.Toggle;
|
||||||
|
using WL.PixelArt;
|
||||||
|
using WL.UI;
|
||||||
|
|
||||||
|
public static class WL814d_Probe
|
||||||
|
{
|
||||||
|
const string kUiPrefab = "Assets/Res_Addr/MainUI/NewGameUI.prefab";
|
||||||
|
const string kCamPrefab = "Assets/ResWork/Prefabs/Ingame/Main Camera.prefab";
|
||||||
|
const string kOut = "AgentScripts/WL814d_PROBE.txt";
|
||||||
|
|
||||||
|
static StringBuilder _o;
|
||||||
|
static int _pass, _fail;
|
||||||
|
|
||||||
|
static void L(string s) { _o.AppendLine(s); }
|
||||||
|
static void H(string s) { _o.AppendLine(); _o.AppendLine("── " + s); }
|
||||||
|
static void N(string s) { _o.AppendLine(" " + s); }
|
||||||
|
static bool Chk(bool ok, string what)
|
||||||
|
{
|
||||||
|
if (ok) { _pass++; _o.AppendLine(" [PASS] " + what); }
|
||||||
|
else { _fail++; _o.AppendLine(" [FAIL] " + what); }
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
static string Root { get { return Directory.GetParent(Application.dataPath).FullName; } }
|
||||||
|
|
||||||
|
// 프로브가 만든 것
|
||||||
|
static GameObject _uiRoot, _camGo, _uiCamGo, _infoGo;
|
||||||
|
static Camera _srcCam, _uiCam;
|
||||||
|
static RealCamera _realCam;
|
||||||
|
|
||||||
|
public static string Edit()
|
||||||
|
{
|
||||||
|
_o = new StringBuilder(); _pass = _fail = 0;
|
||||||
|
L("# WL814d Probe " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " (edit mode · Play 0 · 로그인 0)");
|
||||||
|
L("playMode=" + Application.isPlaying);
|
||||||
|
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
|
L("현재 씬 = '" + scene.name + "' dirty(before)=" + scene.isDirty + " rootCount=" + scene.rootCount);
|
||||||
|
|
||||||
|
var savedRt = WLLookModeSettings.RuntimeDisabled;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
A_Asset();
|
||||||
|
B_Scene();
|
||||||
|
C_Nodes();
|
||||||
|
D_ModeCycle();
|
||||||
|
E_Session();
|
||||||
|
F_Gc();
|
||||||
|
G_C8();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_fail++;
|
||||||
|
L(" [FAIL] 예외 " + e.GetType().Name + " : " + e.Message);
|
||||||
|
L(e.StackTrace);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
WLLookModeSettings.RuntimeDisabled = savedRt;
|
||||||
|
Z_Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
L("");
|
||||||
|
L("RESULT " + (_fail == 0 ? "PASS" : "FAIL") + " (pass=" + _pass + " fail=" + _fail + ")");
|
||||||
|
var after = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||||||
|
L("씬 정리 후: dirty=" + after.isDirty + " rootCount=" + after.rootCount + " (저장 0)");
|
||||||
|
string path = Path.Combine(Root, kOut);
|
||||||
|
File.WriteAllText(path, _o.ToString(), new UTF8Encoding(false));
|
||||||
|
return _o.ToString() + "\n(파일: " + path + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── A. 에셋 · 계약
|
||||||
|
static void A_Asset()
|
||||||
|
{
|
||||||
|
H("A. 에셋 · 계약 (814d SO · 814b SO · 811o SO)");
|
||||||
|
WLLookModeSettings.ClearCache();
|
||||||
|
var s = WLLookModeSettings.Instance;
|
||||||
|
Chk(s != null, "WLLookModeSettings.asset 로드(Resources/WL/WLLookModeSettings)");
|
||||||
|
if (s == null) return;
|
||||||
|
Chk(s.enabled_ && WLLookModeSettings.Enabled, "enabled_ = 1");
|
||||||
|
Chk(s.ModeCount == 4, "모드 4개 — 실측 " + s.ModeCount);
|
||||||
|
L(" | 모드 | 이름 | 리그 | SO구도 | cameraHeight | distance | distanceUp | orthoSize | pixelHeight | envLookOn |");
|
||||||
|
L(" |---|---|---|---|---|---|---|---|---|---|");
|
||||||
|
for (int i = 0; i < s.ModeCount; i++)
|
||||||
|
{
|
||||||
|
var p = s.Preset(i);
|
||||||
|
L(" | " + i + " | " + p.name + " | " + (p.pixelRig ? "직교" : "원근") + " | " + (p.useSoFraming ? "O" : "-") +
|
||||||
|
" | " + p.cameraHeight + " | " + p.distance + " | " + p.distanceUp +
|
||||||
|
" | " + p.orthoSize + " | " + p.pixelHeight + " | " + (p.envLookOn ? "1" : "0") + " |");
|
||||||
|
}
|
||||||
|
Chk(!s.Preset(0).pixelRig && s.Preset(0).useSoFraming, "0번 = 원근 + 811o SO 구도 그대로(리그 해제)");
|
||||||
|
Chk(s.Preset(1).pixelRig && s.Preset(2).pixelRig && s.Preset(3).pixelRig, "1~3번 = 도트(직교 리그)");
|
||||||
|
Chk(s.Wrap(4) == 0 && s.Wrap(-1) == 3, "순환 0→1→2→3→0 (Wrap(4)=" + s.Wrap(4) + " Wrap(-1)=" + s.Wrap(-1) + ")");
|
||||||
|
|
||||||
|
var px = WLPixelCameraSettings.Instance;
|
||||||
|
N("814b WLPixelCameraSettings: enabled=" + (px != null ? px.enabled.ToString() : "?") +
|
||||||
|
" orthoSize=" + (px != null ? px.orthoSize.ToString() : "?") +
|
||||||
|
" pixelHeight=" + (px != null ? px.pixelHeight.ToString() : "?") +
|
||||||
|
" prefab=" + (px != null && px.pixelCameraSystemPrefab != null));
|
||||||
|
Chk(px != null && !px.enabled, "🔴 814b SO enabled = 0(F-23) — 강제 조립 API 가 필요한 이유(실측)");
|
||||||
|
Chk(px != null && px.pixelCameraSystemPrefab != null, "814b 리그 프리팹 배선됨(URP 패키지 해제 확인)");
|
||||||
|
|
||||||
|
var fr = WLCameraFramingSettings.Instance;
|
||||||
|
N("811o WLCameraFramingSettings: enabled=" + (fr != null ? fr.enabled.ToString() : "?") +
|
||||||
|
" framingEnabled=" + (fr != null ? fr.framingEnabled.ToString() : "?") +
|
||||||
|
" h=" + (fr != null ? fr.cameraHeight.ToString() : "?") +
|
||||||
|
" d=" + (fr != null ? fr.distance.ToString() : "?") +
|
||||||
|
" up=" + (fr != null ? fr.distanceUp.ToString() : "?") +
|
||||||
|
" poll=" + (fr != null ? fr.framingPollSeconds.ToString() : "?") + "s");
|
||||||
|
Chk(fr != null && fr.enabled && fr.framingEnabled, "811o 구도 오버라이드가 켜져 있다(런타임 프리셋의 전제)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── B. 씬(원본 카메라 · UI 카메라)
|
||||||
|
static void B_Scene()
|
||||||
|
{
|
||||||
|
H("B. 임시 씬 — 원본 Main Camera(RealCamera 포함) + UICamera(스택 오버레이)");
|
||||||
|
var pf = AssetDatabase.LoadAssetAtPath<GameObject>(kCamPrefab);
|
||||||
|
Chk(pf != null, "원본 카메라 프리팹 로드 " + kCamPrefab);
|
||||||
|
if (pf == null) return;
|
||||||
|
|
||||||
|
_camGo = UnityEngine.Object.Instantiate(pf);
|
||||||
|
_camGo.name = "__WL814d_MainCamera";
|
||||||
|
_camGo.hideFlags = HideFlags.DontSave; // 씬 저장 0 · Camera.main 은 찾을 수 있어야 한다
|
||||||
|
_srcCam = _camGo.GetComponent<Camera>();
|
||||||
|
_realCam = _camGo.GetComponent<RealCamera>();
|
||||||
|
Chk(_srcCam != null && _realCam != null, "Camera + RealCamera 보유");
|
||||||
|
Chk(_camGo.tag == "MainCamera", "tag = MainCamera");
|
||||||
|
Chk(Camera.main == _srcCam, "Camera.main 이 이 카메라로 해석된다(리그 강제 조립의 전제) — 실측 " +
|
||||||
|
(Camera.main != null ? Camera.main.name : "null"));
|
||||||
|
|
||||||
|
// 원본 프리팹 실측값을 arr_cCamData 에 확인/보강(에디트 모드는 Awake 미실행 → 부작용 0)
|
||||||
|
if (_realCam.arr_cCamData == null || _realCam.arr_cCamData.Length < 2)
|
||||||
|
{
|
||||||
|
_realCam.arr_cCamData = new cCamData[2];
|
||||||
|
_realCam.arr_cCamData[0] = new cCamData { minDistance = 3f, maxDistance = 3f, DistanceUp = 2f, cameraHeight = 65f };
|
||||||
|
_realCam.arr_cCamData[1] = new cCamData { minDistance = 2f, maxDistance = 3f, DistanceUp = 3.5f, cameraHeight = 78f };
|
||||||
|
}
|
||||||
|
N("프리팹 arr_cCamData[0] h=" + _realCam.arr_cCamData[0].cameraHeight +
|
||||||
|
" min=" + _realCam.arr_cCamData[0].minDistance + " max=" + _realCam.arr_cCamData[0].maxDistance +
|
||||||
|
" up=" + _realCam.arr_cCamData[0].DistanceUp +
|
||||||
|
" · [1](보스 프리셋) h=" + _realCam.arr_cCamData[1].cameraHeight);
|
||||||
|
N("원본 카메라 orthographic=" + _srcCam.orthographic + " fov=" + _srcCam.fieldOfView +
|
||||||
|
" cullingMask=" + _srcCam.cullingMask + " depth=" + _srcCam.depth);
|
||||||
|
|
||||||
|
// UICamera 를 스택에 넣는다 — 원본 NewGameUI.cs:38 이 런타임에 하는 일과 같다(UI 소실 검증용)
|
||||||
|
_uiCamGo = new GameObject("__WL814d_UICamera", typeof(Camera));
|
||||||
|
_uiCamGo.hideFlags = HideFlags.DontSave;
|
||||||
|
_uiCam = _uiCamGo.GetComponent<Camera>();
|
||||||
|
_uiCam.orthographic = true; _uiCam.depth = 6f; _uiCam.cullingMask = 32; _uiCam.clearFlags = CameraClearFlags.Nothing;
|
||||||
|
var uiData = _uiCam.GetUniversalAdditionalCameraData();
|
||||||
|
if (uiData != null) uiData.renderType = CameraRenderType.Overlay;
|
||||||
|
var srcData = _srcCam.GetUniversalAdditionalCameraData();
|
||||||
|
if (srcData != null && srcData.cameraStack != null) { srcData.cameraStack.Clear(); srcData.cameraStack.Add(_uiCam); }
|
||||||
|
Chk(srcData != null && srcData.cameraStack.Count == 1 && srcData.cameraStack[0] == _uiCam,
|
||||||
|
"원본 스택 = [UICamera] (NewGameUI.cs:38 재현)");
|
||||||
|
|
||||||
|
_bossPreset = _realCam.arr_cCamData[1].cameraHeight;
|
||||||
|
|
||||||
|
// 가짜 InGameInfo — 로비/인게임 판정만 바꾼다(에디트 모드 Awake 미실행 · Ins 는 순수 static 필드)
|
||||||
|
_infoGo = new GameObject("__WL814d_InGameInfo");
|
||||||
|
_infoGo.hideFlags = HideFlags.DontSave;
|
||||||
|
_info = _infoGo.AddComponent<InGameInfo>();
|
||||||
|
InGameInfo.Ins = _info;
|
||||||
|
_modeField = typeof(InGameInfo).GetField("m_GameMode",
|
||||||
|
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||||
|
Chk(_modeField != null, "InGameInfo.m_GameMode 리플렉션(프로브 전용 · 게임 코드 0)");
|
||||||
|
if (_modeField != null) _modeField.SetValue(_info, eGameMode.Stage);
|
||||||
|
Chk(!LookModeHub.InLobby, "인게임(Stage) 판정 — LookModeHub.InLobby=" + LookModeHub.InLobby);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float _bossPreset;
|
||||||
|
static InGameInfo _info;
|
||||||
|
static System.Reflection.FieldInfo _modeField;
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── C. 버튼 노드 · 좌표
|
||||||
|
static void C_Nodes()
|
||||||
|
{
|
||||||
|
H("C. HUD 버튼 — NewGameUI 프리팹(저장 0) · Safe Area · 좌표 표");
|
||||||
|
_uiRoot = PrefabUtility.LoadPrefabContents(kUiPrefab);
|
||||||
|
Chk(_uiRoot != null, "LoadPrefabContents(" + kUiPrefab + ")");
|
||||||
|
if (_uiRoot == null) return;
|
||||||
|
|
||||||
|
var ingame = WLVignetteUtil.FindUiPath(_uiRoot.transform, "IngameUIs");
|
||||||
|
var hud = WLVignetteUtil.FindUiPath(_uiRoot.transform, "IngameUIs/WL_HUD");
|
||||||
|
Chk(hud != null, "IngameUIs/WL_HUD 경로 실측");
|
||||||
|
if (hud != null)
|
||||||
|
Chk(hud.GetComponentInParent<SafeAreaFitter>(true) != null || hud.GetComponent<SafeAreaFitter>() != null,
|
||||||
|
"WL_HUD 가 SafeAreaFitter 아래 = Safe Area 안 (802b/813y 실측 재확인)");
|
||||||
|
|
||||||
|
N("Bind: " + WLLookToggleUi.Bind(_uiRoot.transform));
|
||||||
|
Chk(WLLookToggleUi.Bound, "WL_LookToggle 노드 생성(HideFlags.DontSave)");
|
||||||
|
var s = WLLookModeSettings.Instance;
|
||||||
|
Chk(WLLookToggleUi.SortingOrder == s.sortingOrder,
|
||||||
|
"전용 Canvas sortingOrder=" + WLLookToggleUi.SortingOrder + " — 813q2 결과창 40 · 813z 부활 60 보다 아래");
|
||||||
|
// 🔴 Unity 는 부모 캔버스 계층이 비활성이면 overrideSorting 을 되돌린다.
|
||||||
|
// 프리팹 저장 상태의 IngameUIs 는 active=False 라 조립 시점에는 false 다 → 인게임(활성)에서 켜지는지 실측한다.
|
||||||
|
N("조립 직후 overrideSorting=" + WLLookToggleUi.OverrideSorting + " (IngameUIs 활성=" +
|
||||||
|
(ingame != null ? ingame.gameObject.activeSelf.ToString() : "?") + ")");
|
||||||
|
if (ingame != null)
|
||||||
|
{
|
||||||
|
bool wasOn = ingame.gameObject.activeSelf;
|
||||||
|
ingame.gameObject.SetActive(true); // 프리팹 미리보기 안에서만 · 저장 0
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
Chk(WLLookToggleUi.OverrideSorting && WLLookToggleUi.SortingOrder == s.sortingOrder,
|
||||||
|
"IngameUIs 활성 → overrideSorting=" + WLLookToggleUi.OverrideSorting +
|
||||||
|
" sortingOrder=" + WLLookToggleUi.SortingOrder + "(중첩 캔버스로 인정 · Tick 이 다시 건다)");
|
||||||
|
N("활성 첫 프레임 재배치: " + WLLookToggleUi.Dump());
|
||||||
|
ingame.gameObject.SetActive(wasOn);
|
||||||
|
}
|
||||||
|
N("Layout: " + WLLookToggleUi.ApplyLayout());
|
||||||
|
N(WLLookToggleUi.Dump());
|
||||||
|
// px → 캔버스 유닛 계수는 NewGameUI 루트 캔버스(CanvasScaler)에서 와야 한다(813q 실측 1.7778).
|
||||||
|
var rootCanvas = _uiRoot.GetComponent<Canvas>();
|
||||||
|
var scaler = rootCanvas != null ? rootCanvas.GetComponent<CanvasScaler>() : null;
|
||||||
|
float u0 = WLVignetteUtil.UnitsPerPx(rootCanvas);
|
||||||
|
N("루트 캔버스 = " + (rootCanvas != null ? rootCanvas.name : "없음") +
|
||||||
|
" scaler=" + (scaler != null ? scaler.uiScaleMode + " ref=" + scaler.referenceResolution + " match=" + scaler.screenMatchMode : "없음") +
|
||||||
|
" → unitsPerPx=" + u0.ToString("F4"));
|
||||||
|
Chk(Mathf.Abs(u0 - 1.7778f) < 0.001f, "unitsPerPx = 1.7778 (813q·802b 실측과 같은 계수) — 실측 " + u0.ToString("F4"));
|
||||||
|
var lookRt = FindNode(_uiRoot.transform, "WL_LookToggle") as RectTransform;
|
||||||
|
Chk(lookRt != null && Mathf.Abs(lookRt.sizeDelta.x - s.buttonSizePx.x * u0) < 0.01f,
|
||||||
|
"노드 폭 = 200 px × " + u0.ToString("F4") + " = " + (s.buttonSizePx.x * u0).ToString("F2") +
|
||||||
|
" 유닛 — 실측 " + (lookRt != null ? lookRt.sizeDelta.x.ToString("F2") : "-"));
|
||||||
|
|
||||||
|
// 터치 통과 0 — 버튼 배경은 raycastTarget · 라벨은 아님
|
||||||
|
var node = FindNode(_uiRoot.transform, "WL_LookToggle");
|
||||||
|
Chk(node != null, "WL_LookToggle 노드 탐색");
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
var bgs = node.GetComponentsInChildren<Image>(true);
|
||||||
|
int rc = 0; for (int i = 0; i < bgs.Length; i++) if (bgs[i].raycastTarget) rc++;
|
||||||
|
Chk(bgs.Length == 2 && rc == 2, "버튼 배경 2개 전부 raycastTarget = 터치 통과 0 — 실측 " + rc + "/" + bgs.Length);
|
||||||
|
var lbs = node.GetComponentsInChildren<TextMeshProUGUI>(true);
|
||||||
|
int lrc = 0; for (int i = 0; i < lbs.Length; i++) if (lbs[i].raycastTarget) lrc++;
|
||||||
|
Chk(lrc == 0, "라벨은 raycastTarget 0(버튼이 먹는다) — 실측 " + lrc);
|
||||||
|
Chk(node.gameObject.hideFlags == HideFlags.DontSave, "노드 hideFlags = DontSave(프리팹에 굽지 않는다)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 좌표 표 (px · 세로 1080×1920 Safe Area 기준 · 좌상단 원점)
|
||||||
|
var hs = WLHudLayoutSettings.Instance;
|
||||||
|
var rs = WLRunUiSettings.Instance;
|
||||||
|
var bs = WLBossUiSettings.Instance;
|
||||||
|
float barTop = hs != null ? hs.bossBarTopMarginPx : 190f;
|
||||||
|
float barBottom = hs != null ? barTop + hs.bossBarSizePx.y + hs.bossBarNameFontPx + hs.bossBarNameGapPx : 256f;
|
||||||
|
float barHalfW = hs != null ? hs.bossBarSizePx.x * 0.5f : 440f;
|
||||||
|
float runTop = rs != null ? rs.runHudTopMarginPx : 272f;
|
||||||
|
float runH = rs != null ? rs.timerFontPx + rs.zoneGaugeTopGapPx + rs.zoneCellHeightPx + rs.phaseTopGapPx + rs.phaseFontPx : 122f;
|
||||||
|
float runHalfW = rs != null ? rs.runHudWidthPx * 0.5f : 280f;
|
||||||
|
float banTop = bs != null ? bs.bannerTopMarginPx : 300f;
|
||||||
|
float banBottom = bs != null ? banTop + bs.bannerSizePx.y : 450f;
|
||||||
|
float banHalfW = bs != null ? bs.bannerSizePx.x * 0.5f : 430f;
|
||||||
|
|
||||||
|
var r0 = WLLookToggleUi.ButtonRectPx(0);
|
||||||
|
var r1 = WLLookToggleUi.ButtonRectPx(1);
|
||||||
|
L("");
|
||||||
|
L(" | 요소 | 소유 | 상단 y(px) | 하단 y(px) | 좌 x(px) | 우 x(px) |");
|
||||||
|
L(" |---|---|---|---|---|---|");
|
||||||
|
L(" | 상단 재화 바 | 프리팹(#729) | 130 | 176 | (프리팹) | (프리팹) |");
|
||||||
|
L(" | 보스 HP 바 | 813c3 | " + barTop + " | " + barBottom + " | " + (540f - barHalfW) + " | " + (540f + barHalfW) + " |");
|
||||||
|
L(" | 런 HUD | 813q | " + runTop + " | " + (runTop + runH) + " | " + (540f - runHalfW) + " | " + (540f + runHalfW) + " |");
|
||||||
|
L(" | 보스 배너(1.2 s) | 813tj | " + banTop + " | " + banBottom + " | " + (540f - banHalfW) + " | " + (540f + banHalfW) + " |");
|
||||||
|
L(" | **[카메라] 버튼** | **814d** | **" + r0.y + "** | **" + (r0.y + r0.height) + "** | **" + r0.x + "** | **" + (r0.x + r0.width) + "** |");
|
||||||
|
L(" | **[배경] 버튼** | **814d** | **" + r1.y + "** | **" + (r1.y + r1.height) + "** | **" + r1.x + "** | **" + (r1.x + r1.width) + "** |");
|
||||||
|
L("");
|
||||||
|
Chk(r0.width >= 96f && r0.height >= 48f, "버튼 크기 ≥ 96×48 px — 실측 " + r0.width + "×" + r0.height);
|
||||||
|
Chk(r0.y >= barBottom, "보스 HP 바(≤" + barBottom + ")와 겹침 0 · 여유 " + (r0.y - barBottom) + " px");
|
||||||
|
Chk(r0.y >= runTop + runH, "런 HUD(≤" + (runTop + runH) + ")와 겹침 0 · 여유 " + (r0.y - (runTop + runH)) + " px");
|
||||||
|
Chk(r0.y >= banBottom, "보스 배너(≤" + banBottom + ")와 겹침 0 · 여유 " + (r0.y - banBottom) + " px");
|
||||||
|
Chk(r0.x >= 540f + runHalfW, "런 HUD 폭(우 " + (540f + runHalfW) + ")보다 오른쪽 · 가로 여유 " + (r0.x - (540f + runHalfW)) + " px");
|
||||||
|
Chk(r1.y + r1.height <= 1920f, "두 버튼이 화면 안 — 아래끝 " + (r1.y + r1.height) + " px");
|
||||||
|
N("결과창(813q2 sortingOrder 40) · 부활 팝업(813z 60) 은 이 버튼(" + WLLookToggleUi.SortingOrder +
|
||||||
|
")보다 위 = 팝업이 뜨면 버튼이 가려진다(겹침 대신 덮임).");
|
||||||
|
|
||||||
|
// 프리팹 상단 노드 실측 덤프(참고)
|
||||||
|
L("");
|
||||||
|
L(" [참고] NewGameUI 캔버스 직속 노드(이름 · active):");
|
||||||
|
var canvasT = _uiRoot.transform;
|
||||||
|
for (int i = 0; i < canvasT.childCount; i++)
|
||||||
|
{
|
||||||
|
var c = canvasT.GetChild(i);
|
||||||
|
L(" - " + c.name + " (active=" + c.gameObject.activeSelf + " children=" + c.childCount + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Transform FindNode(Transform root, string name)
|
||||||
|
{
|
||||||
|
var all = root.GetComponentsInChildren<Transform>(true);
|
||||||
|
for (int i = 0; i < all.Length; i++) if (all[i].name == name) return all[i];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── D. 모드 순환
|
||||||
|
static void D_ModeCycle()
|
||||||
|
{
|
||||||
|
H("D. 모드 0→1→2→3→0 — 리그 · arr_cCamData · 원본 카메라 · 버튼 라벨");
|
||||||
|
var s = WLLookModeSettings.Instance;
|
||||||
|
if (s == null || _srcCam == null || _realCam == null) { Chk(false, "전제 준비 실패"); return; }
|
||||||
|
|
||||||
|
LookModeHub.ResetAll();
|
||||||
|
CameraFramingOverride.ResetDiagnostics();
|
||||||
|
WLLookToggleUi.ResetState();
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
|
||||||
|
L("");
|
||||||
|
L(" | 모드 | 이름 | 리그조립 | 원본 ortho | orthoSize | RT | arr[0] h/min/max/up | 라이브 h/max/up | 내림각 | 스택 | 카메라 라벨 | 배경 라벨 |");
|
||||||
|
L(" |---|---|---|---|---|---|---|---|---|---|---|---|");
|
||||||
|
|
||||||
|
for (int step = 0; step <= 4; step++)
|
||||||
|
{
|
||||||
|
int want = step % 4;
|
||||||
|
if (step > 0) WLLookToggleUi.ClickCamera(); // 실제 버튼 onClick 경로
|
||||||
|
Chk(LookModeHub.CameraMode == want, "step " + step + " → 모드 " + want + " (실측 " + LookModeHub.CameraMode + ")");
|
||||||
|
|
||||||
|
// 811o 는 폴링으로 적용한다 — 에디트 모드에는 러너가 없으므로 경계 1회를 강제한다(런타임과 같은 Apply 경로).
|
||||||
|
CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
|
||||||
|
var p = s.Preset(want);
|
||||||
|
var d = _realCam.arr_cCamData[0];
|
||||||
|
var srcData = _srcCam.GetUniversalAdditionalCameraData();
|
||||||
|
int stackN = srcData != null && srcData.cameraStack != null ? srcData.cameraStack.Count : -1;
|
||||||
|
var mgr = PixelCameraRig.Manager;
|
||||||
|
string rt = mgr != null ? mgr.GameResolution.x + "×" + mgr.GameResolution.y : "—";
|
||||||
|
L(" | " + want + " | " + p.name + " | " + (PixelCameraRig.IsAssembled ? "O" : "-") +
|
||||||
|
" | " + _srcCam.orthographic + " | " + _srcCam.orthographicSize.ToString("0.###") +
|
||||||
|
" | " + rt +
|
||||||
|
" | " + d.cameraHeight + "/" + d.minDistance + "/" + d.maxDistance + "/" + d.DistanceUp +
|
||||||
|
" | " + _realCam.cameraHeight + "/" + _realCam.maxDistance + "/" + _realCam.DistanceUp +
|
||||||
|
" | " + CameraFramingOverride.DownAngleDeg(d.cameraHeight, d.maxDistance, d.DistanceUp).ToString("0.0") + "°" +
|
||||||
|
" | " + stackN +
|
||||||
|
" | " + WLLookToggleUi.CameraLabel + " | " + WLLookToggleUi.EnvLabel + " |");
|
||||||
|
|
||||||
|
Chk(PixelCameraRig.IsAssembled == p.pixelRig,
|
||||||
|
"모드 " + want + " 리그 조립 = " + p.pixelRig + " (실측 " + PixelCameraRig.IsAssembled + ")");
|
||||||
|
Chk(_srcCam.orthographic == p.pixelRig,
|
||||||
|
"모드 " + want + " 원본 카메라 orthographic = " + p.pixelRig + " (실측 " + _srcCam.orthographic + ")");
|
||||||
|
if (p.pixelRig)
|
||||||
|
{
|
||||||
|
Chk(Mathf.Abs(_srcCam.orthographicSize - p.orthoSize) < 0.001f,
|
||||||
|
"모드 " + want + " orthographicSize = " + p.orthoSize + " (실측 " + _srcCam.orthographicSize.ToString("0.####") + ")");
|
||||||
|
Chk(mgr != null && mgr.GameResolution.y == p.pixelHeight,
|
||||||
|
"모드 " + want + " RT 세로 = pixelHeight " + p.pixelHeight + " (실측 " + (mgr != null ? mgr.GameResolution.y : -1) + ")");
|
||||||
|
Chk(Mathf.Abs(d.cameraHeight - p.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - p.distance) < 0.001f &&
|
||||||
|
Mathf.Abs(d.DistanceUp - p.distanceUp) < 0.001f,
|
||||||
|
"모드 " + want + " 구도 = h " + p.cameraHeight + " · d " + p.distance + " · up " + p.distanceUp +
|
||||||
|
" (실측 " + d.cameraHeight + "/" + d.maxDistance + "/" + d.DistanceUp + ")");
|
||||||
|
Chk(stackN == 2, "모드 " + want + " URP 스택 = [ViewCamera, UICamera] 2개(UI 소실 0) — 실측 " + stackN);
|
||||||
|
Chk(srcData != null && srcData.cameraStack[stackN - 1] == _uiCam, "모드 " + want + " UICamera 가 스택 맨 뒤(UI 가 맨 위에 그려진다)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var fr = WLCameraFramingSettings.Instance;
|
||||||
|
Chk(!CameraFramingOverride.RuntimePresetOn, "모드 0 런타임 프리셋 해제 = 811o SO 값으로 복귀");
|
||||||
|
Chk(fr != null && Mathf.Abs(d.cameraHeight - fr.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - fr.distance) < 0.001f,
|
||||||
|
"모드 0 구도 = 811o SO(h " + (fr != null ? fr.cameraHeight : 0f) + " · d " + (fr != null ? fr.distance : 0f) +
|
||||||
|
") — 실측 " + d.cameraHeight + "/" + d.maxDistance);
|
||||||
|
Chk(stackN == 1, "모드 0 URP 스택 = [UICamera] 1개(리그 해제 후 원복) — 실측 " + stackN);
|
||||||
|
}
|
||||||
|
Chk(WLLookToggleUi.CameraLabel == string.Format(s.cameraLabelFormat, p.name),
|
||||||
|
"모드 " + want + " 카메라 라벨 = \"" + string.Format(s.cameraLabelFormat, p.name) + "\" (실측 \"" + WLLookToggleUi.CameraLabel + "\")");
|
||||||
|
Chk(_realCam.arr_cCamData[1].cameraHeight == _bossPreset,
|
||||||
|
"보스 프리셋 arr_cCamData[1] 무변경 — h " + _realCam.arr_cCamData[1].cameraHeight + "(시작 " + _bossPreset + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 배경 룩 버튼
|
||||||
|
H("D-2. 배경 룩 버튼(814c 연동 훅) — 플래그 + 이벤트만");
|
||||||
|
int evt = 0; bool last = false;
|
||||||
|
System.Action<bool> sub = v => { evt++; last = v; };
|
||||||
|
LookModeHub.EnvLookChanged += sub;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bool before = LookModeHub.EnvLookOn;
|
||||||
|
WLLookToggleUi.ClickEnv(); WLLookToggleUi.Tick();
|
||||||
|
Chk(LookModeHub.EnvLookOn != before, "[배경] 탭 → 플래그 뒤집힘 " + before + " → " + LookModeHub.EnvLookOn);
|
||||||
|
Chk(evt == 1 && last == LookModeHub.EnvLookOn, "EnvLookChanged 1회 발행(814c 구독 지점) — 실측 " + evt + "회 값=" + last);
|
||||||
|
Chk(WLLookToggleUi.EnvLabel == string.Format(WLLookModeSettings.Instance.envLabelFormat,
|
||||||
|
LookModeHub.EnvLookOn ? WLLookModeSettings.Instance.envOnText : WLLookModeSettings.Instance.envOffText),
|
||||||
|
"배경 라벨 = \"" + WLLookToggleUi.EnvLabel + "\"");
|
||||||
|
WLLookToggleUi.ClickEnv(); WLLookToggleUi.Tick();
|
||||||
|
Chk(LookModeHub.EnvLookOn == before && evt == 2, "다시 탭 → 원래대로 · 이벤트 2회");
|
||||||
|
N("클릭 카운트: 카메라 " + WLLookToggleUi.CameraClicks + " · 배경 " + WLLookToggleUi.EnvClicks +
|
||||||
|
" · 라벨 쓰기 " + WLLookToggleUi.LabelWrites + "회");
|
||||||
|
|
||||||
|
// envFollowsCameraMode — 도트 모드로 바꾸면 배경 룩이 따라 켜진다
|
||||||
|
evt = 0;
|
||||||
|
LookModeHub.SetCameraMode(0); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
LookModeHub.SetCameraMode(1); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
Chk(WLLookModeSettings.Instance.envFollowsCameraMode && LookModeHub.EnvLookOn,
|
||||||
|
"envFollowsCameraMode 1 → 도트 모드에서 배경 룩 자동 ON (실측 " + LookModeHub.EnvLookOn + " · 이벤트 " + evt + "회)");
|
||||||
|
LookModeHub.SetCameraMode(0); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
Chk(!LookModeHub.EnvLookOn, "기본 모드로 돌아오면 배경 룩 자동 OFF (실측 " + LookModeHub.EnvLookOn + ")");
|
||||||
|
}
|
||||||
|
finally { LookModeHub.EnvLookChanged -= sub; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── E. 세션 유지 · 로비 복귀
|
||||||
|
static void E_Session()
|
||||||
|
{
|
||||||
|
H("E. 세션 유지 — 로비 강등 · 인게임 복원 · 맵 재진입 재조립");
|
||||||
|
if (_modeField == null || _info == null) { Chk(false, "가짜 InGameInfo 준비 실패 — 건너뜀"); return; }
|
||||||
|
var info = _info; var fi = _modeField;
|
||||||
|
|
||||||
|
fi.SetValue(info, eGameMode.Stage);
|
||||||
|
Chk(!LookModeHub.InLobby, "인게임(Stage) 판정");
|
||||||
|
LookModeHub.SetCameraMode(2);
|
||||||
|
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
var d = _realCam.arr_cCamData[0];
|
||||||
|
Chk(LookModeHub.AppliedMode == 2 && PixelCameraRig.IsAssembled,
|
||||||
|
"인게임 모드 2 적용 — applied=" + LookModeHub.AppliedMode + " 리그=" + PixelCameraRig.IsAssembled +
|
||||||
|
" h=" + d.cameraHeight + " ortho=" + _srcCam.orthographicSize.ToString("0.###"));
|
||||||
|
|
||||||
|
// 로비로 — 고른 모드는 유지, 적용만 0
|
||||||
|
fi.SetValue(info, eGameMode.Lobby);
|
||||||
|
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
d = _realCam.arr_cCamData[0];
|
||||||
|
Chk(LookModeHub.CameraMode == 2 && LookModeHub.AppliedMode == 0 && !PixelCameraRig.IsAssembled,
|
||||||
|
"로비 → 카메라 기본 복귀(적용 0 · 리그 해제) · 고른 모드 2 유지 — 실측 mode=" + LookModeHub.CameraMode +
|
||||||
|
" applied=" + LookModeHub.AppliedMode + " 리그=" + PixelCameraRig.IsAssembled +
|
||||||
|
" ortho=" + _srcCam.orthographic + " h=" + d.cameraHeight);
|
||||||
|
|
||||||
|
// 인게임 재진입 — 저절로 복원
|
||||||
|
fi.SetValue(info, eGameMode.Stage);
|
||||||
|
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
d = _realCam.arr_cCamData[0];
|
||||||
|
var p2 = WLLookModeSettings.Instance.Preset(2);
|
||||||
|
Chk(LookModeHub.AppliedMode == 2 && PixelCameraRig.IsAssembled &&
|
||||||
|
Mathf.Abs(_srcCam.orthographicSize - p2.orthoSize) < 0.001f && Mathf.Abs(d.cameraHeight - p2.cameraHeight) < 0.001f,
|
||||||
|
"인게임 재진입 → 모드 2 자동 복원 — ortho=" + _srcCam.orthographicSize.ToString("0.###") +
|
||||||
|
" h=" + d.cameraHeight + " (로비강등 " + LookModeHub.LobbyDemotes + "회)");
|
||||||
|
|
||||||
|
// 맵 재진입(리그가 걷혔다가 다시 조립되는 경로) — 814b PollBoundary 가 하는 일을 손으로
|
||||||
|
PixelCameraRig.Teardown("맵 이탈(프로브)");
|
||||||
|
Chk(!PixelCameraRig.IsAssembled && PixelCameraRig.RuntimeOverrideOn,
|
||||||
|
"맵 이탈 → 리그 해제되지만 오버라이드 플래그는 유지(다음 진입에서 같은 값으로 재조립) — 실측 " +
|
||||||
|
PixelCameraRig.RuntimeOverrideOn);
|
||||||
|
Chk(PixelCameraRig.ProbeAssemble(_srcCam) && Mathf.Abs(_srcCam.orthographicSize - p2.orthoSize) < 0.001f,
|
||||||
|
"맵 재진입 → 같은 값으로 재조립 ortho=" + _srcCam.orthographicSize.ToString("0.###"));
|
||||||
|
N(LookModeHub.Dump());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── F. GC
|
||||||
|
static void F_Gc()
|
||||||
|
{
|
||||||
|
H("F. GC — 전환 외 프레임당 할당 0");
|
||||||
|
LookModeHub.UiTick = null; // 러너 훅 대신 직접 부른다
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
LookModeHub.Tick(); // 안정화 1회
|
||||||
|
|
||||||
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||||||
|
long before = GC.GetTotalMemory(false);
|
||||||
|
int g0 = GC.CollectionCount(0);
|
||||||
|
int writes = WLLookToggleUi.LabelWrites, applies = LookModeHub.Applies;
|
||||||
|
for (int i = 0; i < 200; i++) { LookModeHub.Tick(); WLLookToggleUi.Tick(); }
|
||||||
|
long after = GC.GetTotalMemory(false);
|
||||||
|
Chk(after - before == 0, "허브 Tick + UI Tick ×200 → Δ" + (after - before) + " B (gen0 " + g0 + "→" + GC.CollectionCount(0) + ")");
|
||||||
|
Chk(WLLookToggleUi.LabelWrites == writes, "라벨 쓰기 0회(값이 그대로면 안 쓴다) — 실측 " + (WLLookToggleUi.LabelWrites - writes));
|
||||||
|
Chk(LookModeHub.Applies == applies, "적용 0회(모드가 그대로면 두 기능을 건드리지 않는다) — 실측 " + (LookModeHub.Applies - applies));
|
||||||
|
|
||||||
|
// 대조군 — 계기 감도 확인
|
||||||
|
long b2 = GC.GetTotalMemory(false);
|
||||||
|
var junk = new byte[16 * 1024 * 1024];
|
||||||
|
junk[0] = 1;
|
||||||
|
long a2 = GC.GetTotalMemory(false);
|
||||||
|
Chk(a2 - b2 > 15000000, "대조군 16 MB 할당 감지 Δ" + (a2 - b2) + " B = 계기 감도 확인");
|
||||||
|
junk = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── G. C8
|
||||||
|
static void G_C8()
|
||||||
|
{
|
||||||
|
H("G. C8 — WLLookModeSettings 를 끄면 814b/811o 가 원래대로");
|
||||||
|
var fr = WLCameraFramingSettings.Instance;
|
||||||
|
LookModeHub.SetCameraMode(1);
|
||||||
|
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
Chk(PixelCameraRig.RuntimeOverrideOn && CameraFramingOverride.RuntimePresetOn, "끄기 전 = 도트 모드 · 두 오버라이드 on");
|
||||||
|
|
||||||
|
WLLookModeSettings.RuntimeDisabled = true;
|
||||||
|
Chk(!WLLookModeSettings.Enabled, "WLLookModeSettings.Enabled = false");
|
||||||
|
LookModeHub.Tick();
|
||||||
|
CameraFramingOverride.ProbeApply(_realCam);
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
var d = _realCam.arr_cCamData[0];
|
||||||
|
Chk(!PixelCameraRig.RuntimeOverrideOn && !PixelCameraRig.IsAssembled, "814b 리그 오버라이드 0 · 리그 해제 = SO(enabled 0) 원래 동작");
|
||||||
|
Chk(!CameraFramingOverride.RuntimePresetOn, "811o 런타임 프리셋 0 = SO 값 경로 그대로");
|
||||||
|
Chk(fr != null && Mathf.Abs(d.cameraHeight - fr.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - fr.distance) < 0.001f &&
|
||||||
|
Mathf.Abs(d.DistanceUp - fr.distanceUp) < 0.001f,
|
||||||
|
"arr_cCamData[0] = 811o SO 값 h " + (fr != null ? fr.cameraHeight : 0f) + " / d " + (fr != null ? fr.distance : 0f) +
|
||||||
|
" / up " + (fr != null ? fr.distanceUp : 0f) + " — 실측 " + d.cameraHeight + "/" + d.maxDistance + "/" + d.DistanceUp);
|
||||||
|
Chk(!_srcCam.orthographic, "원본 카메라 원근 복귀 — orthographic=" + _srcCam.orthographic);
|
||||||
|
Chk(!WLLookToggleUi.Visible, "버튼 숨김 — visible=" + WLLookToggleUi.Visible);
|
||||||
|
Chk(LookModeHub.AppliedMode == 0, "적용 모드 0 고정 — 실측 " + LookModeHub.AppliedMode);
|
||||||
|
|
||||||
|
// showButtons 만 끄기 — 모드는 그대로 두고 버튼만 숨긴다(시연 뒤 off)
|
||||||
|
WLLookModeSettings.RuntimeDisabled = false;
|
||||||
|
var s = WLLookModeSettings.Instance;
|
||||||
|
bool saved = s.showButtons;
|
||||||
|
s.showButtons = false;
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
Chk(!WLLookToggleUi.Visible, "showButtons = 0 → 버튼만 숨김(모드 전환은 그대로) — visible=" + WLLookToggleUi.Visible);
|
||||||
|
s.showButtons = saved;
|
||||||
|
WLLookToggleUi.Tick();
|
||||||
|
Chk(WLLookToggleUi.Visible, "되돌리면 다시 보인다");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────── Z. 정리
|
||||||
|
static void Z_Cleanup()
|
||||||
|
{
|
||||||
|
H("Z. 정리 — 오버라이드 해제 · 노드 파괴 · 프리팹 저장 0");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LookModeHub.ResetAll();
|
||||||
|
LookModeHub.UiTick = null;
|
||||||
|
PixelCameraRig.Teardown("probe end");
|
||||||
|
CameraFramingOverride.ProbeRestore();
|
||||||
|
CameraFramingOverride.ResetDiagnostics();
|
||||||
|
N("리그 조립=" + PixelCameraRig.IsAssembled + " 오버라이드=" + PixelCameraRig.RuntimeOverrideOn +
|
||||||
|
" · 구도 런타임프리셋=" + CameraFramingOverride.RuntimePresetOn + " 적용=" + CameraFramingOverride.IsApplied);
|
||||||
|
N(WLLookToggleUi.Teardown());
|
||||||
|
if (_uiRoot != null) { PrefabUtility.UnloadPrefabContents(_uiRoot); _uiRoot = null; }
|
||||||
|
if (_infoGo != null) { InGameInfo.Ins = null; UnityEngine.Object.DestroyImmediate(_infoGo); _infoGo = null; }
|
||||||
|
if (_uiCamGo != null) { UnityEngine.Object.DestroyImmediate(_uiCamGo); _uiCamGo = null; }
|
||||||
|
if (_camGo != null) { UnityEngine.Object.DestroyImmediate(_camGo); _camGo = null; }
|
||||||
|
var strays = UnityEngine.Object.FindObjectsByType<Camera>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
|
N("남은 카메라 " + strays.Length + "개 · InGameInfo.Ins=" + (InGameInfo.Ins != null));
|
||||||
|
N("NewGameUI.prefab 저장 0(UnloadPrefabContents(false) 기본) · 에셋 쓰기 0");
|
||||||
|
}
|
||||||
|
catch (Exception e) { N("정리 중 예외 " + e.GetType().Name + " " + e.Message); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -75,6 +75,25 @@ namespace WL.CameraFraming
|
||||||
static RealCamera s_cam;
|
static RealCamera s_cam;
|
||||||
static float s_wHeight, s_wMin, s_wMax, s_wUp; // 우리가 쓴 값(외부 덮어쓰기 감지)
|
static float s_wHeight, s_wMin, s_wMax, s_wUp; // 우리가 쓴 값(외부 덮어쓰기 감지)
|
||||||
|
|
||||||
|
// ── WL-814d 런타임 프리셋 — 룩 토글이 SO(cameraHeight/distance/distanceUp) 대신 얹는 값.
|
||||||
|
// 적용 시점은 그대로다(다음 폴링 · 킬캠/보스 캠이 돌면 그 뒤). 아무도 부르지 않으면 기존 동작과 같다.
|
||||||
|
static bool s_rtOn, s_rtDirty; static float s_rtH, s_rtD, s_rtUp;
|
||||||
|
/// <summary>룩 토글이 지금 구도 값을 얹고 있는가(진단).</summary>
|
||||||
|
public static bool RuntimePresetOn { get { return s_rtOn; } }
|
||||||
|
/// <summary>WL-814d — SO 값 대신 이 구도 값을 쓴다(다음 폴링에 반영).</summary>
|
||||||
|
public static void SetRuntimePreset(float cameraHeight, float distance, float distanceUp)
|
||||||
|
{
|
||||||
|
if (s_rtOn && cameraHeight == s_rtH && distance == s_rtD && distanceUp == s_rtUp) return;
|
||||||
|
s_rtOn = true; s_rtH = cameraHeight; s_rtD = distance; s_rtUp = distanceUp;
|
||||||
|
s_rtDirty = true; s_nextPoll = 0f;
|
||||||
|
}
|
||||||
|
/// <summary>WL-814d — 런타임 프리셋을 걷고 SO 값으로 돌아간다.</summary>
|
||||||
|
public static void ClearRuntimePreset()
|
||||||
|
{
|
||||||
|
if (!s_rtOn) return;
|
||||||
|
s_rtOn = false; s_rtDirty = true; s_nextPoll = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
// 틱
|
// 틱
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -108,10 +127,11 @@ namespace WL.CameraFraming
|
||||||
int map = MyValue.MyChoiceMapData != null ? MyValue.MyChoiceMapData.n_MapID : int.MinValue;
|
int map = MyValue.MyChoiceMapData != null ? MyValue.MyChoiceMapData.n_MapID : int.MinValue;
|
||||||
bool pcChanged = !ReferenceEquals(pc, s_lastPc);
|
bool pcChanged = !ReferenceEquals(pc, s_lastPc);
|
||||||
bool mapChanged = map != s_lastMapId;
|
bool mapChanged = map != s_lastMapId;
|
||||||
if (!pcChanged && !mapChanged) return;
|
if (!pcChanged && !mapChanged && !s_rtDirty) return; // WL-814d — 런타임 프리셋이 바뀌면 경계가 아니어도 다시 쓴다
|
||||||
|
|
||||||
if (!cfg.framingInLobby && info.Get_GameMode() == eGameMode.Lobby)
|
if (!cfg.framingInLobby && info.Get_GameMode() == eGameMode.Lobby)
|
||||||
{
|
{
|
||||||
|
s_rtDirty = false;
|
||||||
s_lastPc = pc; s_lastMapId = map;
|
s_lastPc = pc; s_lastMapId = map;
|
||||||
Boundaries++; SkippedLobby++;
|
Boundaries++; SkippedLobby++;
|
||||||
LastReason = "lobby";
|
LastReason = "lobby";
|
||||||
|
|
@ -134,8 +154,8 @@ namespace WL.CameraFraming
|
||||||
}
|
}
|
||||||
|
|
||||||
s_lastPc = pc; s_lastMapId = map;
|
s_lastPc = pc; s_lastMapId = map;
|
||||||
Boundaries++;
|
if (pcChanged || mapChanged) Boundaries++; // WL-814d — 프리셋 변경만으로는 경계를 세지 않는다
|
||||||
LastReason = pcChanged ? "pc" : ("map " + map);
|
LastReason = pcChanged ? "pc" : (mapChanged ? "map " + map : "preset");
|
||||||
|
|
||||||
Apply(cfg, cam);
|
Apply(cfg, cam);
|
||||||
}
|
}
|
||||||
|
|
@ -155,9 +175,11 @@ namespace WL.CameraFraming
|
||||||
OriginHeight = d.cameraHeight; OriginMin = d.minDistance; OriginMax = d.maxDistance; OriginUp = d.DistanceUp;
|
OriginHeight = d.cameraHeight; OriginMin = d.minDistance; OriginMax = d.maxDistance; OriginUp = d.DistanceUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float h = Mathf.Clamp(cfg.cameraHeight, 50f, 85f);
|
// WL-814d — 런타임 프리셋이 걸려 있으면 SO 대신 그 값(안전 범위는 넓게 · 도트 프리셋은 45° 까지 내려간다).
|
||||||
float dist = Mathf.Clamp(cfg.distance, 1f, 8f);
|
s_rtDirty = false;
|
||||||
float up = cfg.distanceUp >= 0f ? cfg.distanceUp : OriginUp;
|
float h = s_rtOn ? Mathf.Clamp(s_rtH, 10f, 89f) : Mathf.Clamp(cfg.cameraHeight, 50f, 85f);
|
||||||
|
float dist = s_rtOn ? Mathf.Clamp(s_rtD, 0.5f, 20f) : Mathf.Clamp(cfg.distance, 1f, 8f);
|
||||||
|
float up = s_rtOn ? s_rtUp : (cfg.distanceUp >= 0f ? cfg.distanceUp : OriginUp);
|
||||||
|
|
||||||
d.cameraHeight = h;
|
d.cameraHeight = h;
|
||||||
d.minDistance = dist;
|
d.minDistance = dist;
|
||||||
|
|
@ -262,7 +284,7 @@ namespace WL.CameraFraming
|
||||||
{
|
{
|
||||||
Boundaries = Applied = Restored = SkippedLobby = SkippedBusy = SkippedNoCam = 0;
|
Boundaries = Applied = Restored = SkippedLobby = SkippedBusy = SkippedNoCam = 0;
|
||||||
LastReason = LastLog = "";
|
LastReason = LastLog = "";
|
||||||
s_lastPc = null; s_lastMapId = int.MinValue; s_nextPoll = 0f; s_faulted = false;
|
s_lastPc = null; s_lastMapId = int.MinValue; s_nextPoll = 0f; s_faulted = false; s_rtOn = s_rtDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>프로브용: 러너 없이 1회 폴링(강제 틱).</summary>
|
/// <summary>프로브용: 러너 없이 1회 폴링(강제 틱).</summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 30c06fc33fc5d414fa1edd4b434ae818
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,278 @@
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// LookModeHub.cs — 인게임 룩 모드 허브: 카메라 모드 순환 + 배경 룩 플래그 (WL-814d · #814)
|
||||||
|
//
|
||||||
|
// PD 제안(2026-09-10) 「토글 형태로 카메라를 바꿔서 볼 수 있게」 · 발주서 WL-814d §1-1·§1-2·§1-4.
|
||||||
|
//
|
||||||
|
// ■ 이 파일이 하는 것 — 원본 훅 0 · 새 Manager/Singleton 0(정적 + 러너 GameObject 1개)
|
||||||
|
// ① 앱 세션 동안 「지금 어떤 룩인가」를 들고 있는다(CameraMode 0~N · EnvLookOn).
|
||||||
|
// ② 모드가 바뀌면 값을 **두 기존 기능에 런타임으로 얹는다**(둘 다 이번에 추가한 API · 기존 경로 무변경):
|
||||||
|
// · 811o `CameraFramingOverride.SetRuntimePreset(h, d, up)` / `ClearRuntimePreset()`
|
||||||
|
// → 원본 `RealCamera.arr_cCamData[0]` 구도를 SO 값 대신 프리셋 값으로 (폴링 0.25 s 안에 반영 ·
|
||||||
|
// 킬캠/보스 캠이 도는 동안에는 그 뒤로 미뤄진다 = 811o 의 SkippedBusy 규칙 그대로)
|
||||||
|
// · 814b `PixelCameraRig.SetRuntimeOverride(on, orthoSize, pixelHeight)`
|
||||||
|
// → SO `enabled 0`(F-23) 이어도 on 이면 리그를 조립·유지, off 면 해체
|
||||||
|
// ③ 배경 룩(814c)은 **플래그 + 이벤트만** 여기서 든다 — 렌더러/에셋은 814c 소유라 이 태스크가 건드리지 않는다.
|
||||||
|
// 814c `EnvLook` 이 `EnvLookChanged` 를 구독하면 그대로 이어진다(발주서 §1-4 · 완료보고 §연동 규약).
|
||||||
|
//
|
||||||
|
// ■ 왜 「값을 얹기」인가
|
||||||
|
// 814b 리그와 811o 구도는 각자 SO 를 갖고 각자 맵 진입 경계에서 값을 적용한다. 허브가 카메라를 직접
|
||||||
|
// 만지면 그 둘과 싸운다. 그래서 허브는 **두 기능의 입력값만 바꾸고** 적용 시점·복원은 원래 주인에게 맡긴다.
|
||||||
|
// → 맵 재진입·런 재시작에서 리그 재조립·구도 재적용이 저절로 일어난다(각 기능의 PollBoundary).
|
||||||
|
//
|
||||||
|
// ■ 로비/타이틀
|
||||||
|
// `defaultInLobby` 가 켜져 있으면 로비에서는 **적용 모드만** 0 으로 내리고 고른 모드(CameraMode)는 그대로 둔다
|
||||||
|
// → 인게임에 다시 들어가면 저절로 복원된다. (814b 리그도 `assembleInLobby 0` 이라 로비에서 스스로 걷는다.)
|
||||||
|
//
|
||||||
|
// ■ GC 0 — 틱은 비교뿐이다(문자열·람다·클로저 0). 로그 문자열은 만들기 전에 verboseLog 로 막는다(811b FIX-3).
|
||||||
|
//
|
||||||
|
// ■ C8 롤백 — `WLLookModeSettings.enabled_ = 0` → 적용 모드 0 · 신규 API 호출 0 = 814b/811o 원래 동작 100 %.
|
||||||
|
//
|
||||||
|
// 🔴 어셈블리 주의: Assets/WL/Look/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
using WL.CameraFraming;
|
||||||
|
using WL.PixelArt;
|
||||||
|
|
||||||
|
namespace WL.Look.Toggle
|
||||||
|
{
|
||||||
|
/// <summary>룩 모드(카메라 프리셋 + 배경 룩) 허브. 정적 · 상태는 앱 세션 동안 유지된다.</summary>
|
||||||
|
public static class LookModeHub
|
||||||
|
{
|
||||||
|
static WLLookModeSettings St { get { return WLLookModeSettings.Instance; } }
|
||||||
|
|
||||||
|
// ── 상태 ──────────────────────────────────────────────────────────────
|
||||||
|
static int s_mode = -1; // PD 가 고른 모드(로비에 가도 유지)
|
||||||
|
static int s_applied = 0; // 실제로 두 기능에 얹혀 있는 모드
|
||||||
|
static bool s_env;
|
||||||
|
static bool s_booted;
|
||||||
|
|
||||||
|
/// <summary>PD 가 고른 카메라 모드(0 = 기본 원근 · 1~ = 도트 프리셋). 로비에 가도 이 값은 그대로.</summary>
|
||||||
|
public static int CameraMode { get { EnsureBoot(); return s_mode; } }
|
||||||
|
|
||||||
|
/// <summary>지금 실제로 카메라에 얹혀 있는 모드(로비에서는 0 으로 내려간다).</summary>
|
||||||
|
public static int AppliedMode { get { return s_applied; } }
|
||||||
|
|
||||||
|
/// <summary>배경 룩(814c) on/off 플래그 — 이 태스크는 플래그와 이벤트만 든다.</summary>
|
||||||
|
public static bool EnvLookOn { get { EnsureBoot(); return s_env; } }
|
||||||
|
|
||||||
|
/// <summary>지금 모드가 도트(직교 리그) 모드인가.</summary>
|
||||||
|
public static bool IsPixelMode
|
||||||
|
{
|
||||||
|
get { var p = St != null ? St.Preset(CameraMode) : null; return p != null && p.pixelRig; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>버튼에 찍을 지금 모드 이름.</summary>
|
||||||
|
public static string CameraModeName { get { var s = St; return s != null ? s.ModeName(CameraMode) : "기본"; } }
|
||||||
|
|
||||||
|
// ── 이벤트 (814c · UI 가 구독한다 · 인자 = 바뀐 값) ────────────────────
|
||||||
|
/// <summary>카메라 모드가 바뀌었다(PD 선택 기준 · 로비 강등은 알리지 않는다).</summary>
|
||||||
|
public static event System.Action<int> CameraModeChanged;
|
||||||
|
|
||||||
|
/// <summary>배경 룩 플래그가 바뀌었다 — 🔴 814c `EnvLook` 이 여기에 붙는다(발주서 §1-4).</summary>
|
||||||
|
public static event System.Action<bool> EnvLookChanged;
|
||||||
|
|
||||||
|
/// <summary>UI(WLLookToggleUi)가 매 프레임 얻어가는 훅 — 러너를 두 개 만들지 않기 위한 배선.</summary>
|
||||||
|
public static System.Action UiTick;
|
||||||
|
|
||||||
|
// ── 진단(프로브가 읽는다 · 실측만) ────────────────────────────────────
|
||||||
|
public static int Applies, ModeChanges, EnvChanges, LobbyDemotes, Restores;
|
||||||
|
public static string LastLog = "";
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
// 공개 조작
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>모드를 고른다(범위를 넘으면 순환). 같은 모드면 아무 것도 하지 않는다.</summary>
|
||||||
|
public static string SetCameraMode(int mode)
|
||||||
|
{
|
||||||
|
EnsureBoot();
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return "WLLookModeSettings 에셋 없음";
|
||||||
|
int m = s.Wrap(mode);
|
||||||
|
if (m == s_mode) return "모드 변화 없음(" + m + ")";
|
||||||
|
s_mode = m;
|
||||||
|
ModeChanges++;
|
||||||
|
|
||||||
|
if (s.envFollowsCameraMode)
|
||||||
|
{
|
||||||
|
var p = s.Preset(m);
|
||||||
|
if (p != null) SetEnvLook(p.envLookOn);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cb = CameraModeChanged;
|
||||||
|
if (cb != null) cb(m);
|
||||||
|
|
||||||
|
return Push(WantedMode(), "모드 선택");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>다음 모드로 순환한다(버튼이 부르는 것 — 0 → 1 → 2 → 3 → 0).</summary>
|
||||||
|
public static string NextCameraMode() { return SetCameraMode(CameraMode + 1); }
|
||||||
|
|
||||||
|
/// <summary>배경 룩 플래그를 바꾼다(814c 가 구독한다). 이 태스크에서 렌더러는 건드리지 않는다.</summary>
|
||||||
|
public static string SetEnvLook(bool on)
|
||||||
|
{
|
||||||
|
EnsureBoot();
|
||||||
|
if (on == s_env) return "배경 룩 변화 없음(" + on + ")";
|
||||||
|
s_env = on;
|
||||||
|
EnvChanges++;
|
||||||
|
var cb = EnvLookChanged;
|
||||||
|
if (cb != null) cb(on);
|
||||||
|
return Log("배경 룩 " + (on ? "ON" : "OFF"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>배경 룩을 뒤집는다(버튼이 부르는 것).</summary>
|
||||||
|
public static string ToggleEnvLook() { return SetEnvLook(!EnvLookOn); }
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
// 틱 · 적용
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>러너가 매 프레임 부른다(에디트 모드에서는 프로브가 직접 부를 수 있다). 비교뿐 = GC 0.</summary>
|
||||||
|
public static void Tick()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null || !WLLookModeSettings.Enabled)
|
||||||
|
{
|
||||||
|
if (s_applied != 0) Push(0, "off"); // C8 — 즉시 원래 동작으로
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EnsureBoot();
|
||||||
|
int want = WantedMode();
|
||||||
|
if (want != s_applied) Push(want, want == 0 ? "로비" : "인게임");
|
||||||
|
|
||||||
|
var ui = UiTick;
|
||||||
|
if (ui != null) ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>지금 얹혀야 하는 모드 — 로비/타이틀에서는 0(고른 모드는 그대로 둔다).</summary>
|
||||||
|
static int WantedMode()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return 0;
|
||||||
|
if (!s.defaultInLobby) return s_mode;
|
||||||
|
return InLobby ? 0 : s_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>로비·타이틀인가. `InGameInfo.Ins` 는 순수 static 필드라 에디트 모드에서도 부작용 0(실측).</summary>
|
||||||
|
public static bool InLobby
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var info = InGameInfo.Ins;
|
||||||
|
if (DSUtil.CheckNull(info)) return true; // 아직 인게임이 아니다
|
||||||
|
return info.Get_GameMode() == eGameMode.Lobby;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모드 값을 두 기존 기능에 얹는다(적용 시점·복원은 그 둘이 원래 하던 대로).</summary>
|
||||||
|
static string Push(int mode, string why)
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return "WLLookModeSettings 에셋 없음";
|
||||||
|
|
||||||
|
bool on = WLLookModeSettings.Enabled;
|
||||||
|
var p = on ? s.Preset(mode) : null;
|
||||||
|
if (!on) mode = 0;
|
||||||
|
|
||||||
|
if (p == null || p.useSoFraming) CameraFramingOverride.ClearRuntimePreset();
|
||||||
|
else CameraFramingOverride.SetRuntimePreset(p.cameraHeight, p.distance, p.distanceUp);
|
||||||
|
|
||||||
|
string rig = p != null && p.pixelRig
|
||||||
|
? PixelCameraRig.SetRuntimeOverride(true, p.orthoSize, p.pixelHeight)
|
||||||
|
: PixelCameraRig.SetRuntimeOverride(false, 0f, 0);
|
||||||
|
|
||||||
|
if (mode == 0 && s_applied != 0) Restores++;
|
||||||
|
if (mode == 0 && why == "로비") LobbyDemotes++;
|
||||||
|
s_applied = mode;
|
||||||
|
Applies++;
|
||||||
|
|
||||||
|
if (s.verboseLog)
|
||||||
|
return Log("모드 " + mode + "(" + s.ModeName(mode) + ") 적용 — " + why + " · 리그: " + rig);
|
||||||
|
LastLog = why;
|
||||||
|
return "모드 " + mode + " 적용(" + why + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnsureBoot()
|
||||||
|
{
|
||||||
|
if (s_booted) return;
|
||||||
|
s_booted = true;
|
||||||
|
var s = St;
|
||||||
|
s_mode = s != null ? s.Wrap(s.startMode) : 0;
|
||||||
|
s_env = s != null && s.envLookOnAtStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Log(string msg)
|
||||||
|
{
|
||||||
|
LastLog = msg;
|
||||||
|
var s = St;
|
||||||
|
if (s != null && s.verboseLog) Debug.Log("[LookMode] " + msg);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
// 프로브 · 러너
|
||||||
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// <summary>프로브: 상태·카운터를 시작값으로 되돌리고 두 기능의 오버라이드도 해제한다.</summary>
|
||||||
|
public static string ResetAll()
|
||||||
|
{
|
||||||
|
s_booted = false;
|
||||||
|
EnsureBoot();
|
||||||
|
s_applied = -1;
|
||||||
|
Applies = ModeChanges = EnvChanges = LobbyDemotes = Restores = 0;
|
||||||
|
LastLog = "";
|
||||||
|
CameraFramingOverride.ClearRuntimePreset();
|
||||||
|
PixelCameraRig.SetRuntimeOverride(false, 0f, 0);
|
||||||
|
s_applied = 0;
|
||||||
|
return "리셋 — 모드 " + s_mode + " · 배경 " + s_env;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>프로브: 로비 판정을 건너뛰고 지금 고른 모드를 그대로 얹는다.</summary>
|
||||||
|
public static string ForceApply() { EnsureBoot(); return Push(s_mode, "probe"); }
|
||||||
|
|
||||||
|
public static string Dump()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
return "[LookMode] 고른모드=" + s_mode + "(" + (s != null ? s.ModeName(s_mode) : "-") + ")"
|
||||||
|
+ " 적용모드=" + s_applied + " 배경룩=" + s_env
|
||||||
|
+ " · 리그조립=" + PixelCameraRig.IsAssembled + " 리그오버라이드=" + PixelCameraRig.RuntimeOverrideOn
|
||||||
|
+ " · 구도적용=" + CameraFramingOverride.IsApplied
|
||||||
|
+ " 런타임프리셋=" + CameraFramingOverride.RuntimePresetOn
|
||||||
|
+ "(h " + CameraFramingOverride.AppliedHeight.ToString("0.##")
|
||||||
|
+ " · d " + CameraFramingOverride.AppliedDistance.ToString("0.##")
|
||||||
|
+ " · up " + CameraFramingOverride.AppliedUp.ToString("0.##") + ")"
|
||||||
|
+ " · 적용 " + Applies + "회 · 모드변경 " + ModeChanges + " · 배경변경 " + EnvChanges
|
||||||
|
+ " · 로비강등 " + LobbyDemotes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GameObject s_runner;
|
||||||
|
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||||
|
static void Boot()
|
||||||
|
{
|
||||||
|
if (s_runner != null) return;
|
||||||
|
if (!WLLookModeSettings.Enabled) return; // C8 — 러너 자체가 뜨지 않는다
|
||||||
|
EnsureBoot();
|
||||||
|
s_runner = new GameObject("[WL814d] LookModeRunner");
|
||||||
|
s_runner.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
s_runner.AddComponent<LookModeRunner>();
|
||||||
|
Object.DontDestroyOnLoad(s_runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void OnRunnerGone()
|
||||||
|
{
|
||||||
|
CameraFramingOverride.ClearRuntimePreset();
|
||||||
|
PixelCameraRig.SetRuntimeOverride(false, 0f, 0);
|
||||||
|
s_applied = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>룩 토글의 시간 축(허브 + UI). GameObject 1개 · 코루틴 0.</summary>
|
||||||
|
internal sealed class LookModeRunner : MonoBehaviour
|
||||||
|
{
|
||||||
|
void Update() { LookModeHub.Tick(); }
|
||||||
|
void OnApplicationQuit() { LookModeHub.OnRunnerGone(); }
|
||||||
|
void OnDestroy() { LookModeHub.OnRunnerGone(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 42bdfde1a98527a47853bc4baa1f4f68
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4159c58a063a01a438dd2b10f1d11d45
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b56f4f2577f267b4d9ca5b7bd388a3f6
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
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: 5787ec37785d76c42beb852ba605db1d, type: 3}
|
||||||
|
m_Name: WLLookModeSettings
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::WL.Look.Toggle.WLLookModeSettings
|
||||||
|
enabled_: 1
|
||||||
|
verboseLog: 0
|
||||||
|
startMode: 0
|
||||||
|
keepModeAcrossMaps: 1
|
||||||
|
defaultInLobby: 1
|
||||||
|
envFollowsCameraMode: 1
|
||||||
|
envLookOnAtStart: 0
|
||||||
|
presets:
|
||||||
|
- name: "\uAE30\uBCF8"
|
||||||
|
pixelRig: 0
|
||||||
|
useSoFraming: 1
|
||||||
|
cameraHeight: 76
|
||||||
|
distance: 3
|
||||||
|
distanceUp: 2.4
|
||||||
|
orthoSize: 3.8
|
||||||
|
pixelHeight: 480
|
||||||
|
envLookOn: 0
|
||||||
|
- name: "\uB3C4\uD2B8A"
|
||||||
|
pixelRig: 1
|
||||||
|
useSoFraming: 0
|
||||||
|
cameraHeight: 74
|
||||||
|
distance: 3.4
|
||||||
|
distanceUp: 2.2
|
||||||
|
orthoSize: 3.8
|
||||||
|
pixelHeight: 480
|
||||||
|
envLookOn: 1
|
||||||
|
- name: "\uB3C4\uD2B8B"
|
||||||
|
pixelRig: 1
|
||||||
|
useSoFraming: 0
|
||||||
|
cameraHeight: 55
|
||||||
|
distance: 4.5
|
||||||
|
distanceUp: 1.8
|
||||||
|
orthoSize: 4.2
|
||||||
|
pixelHeight: 480
|
||||||
|
envLookOn: 1
|
||||||
|
- name: "\uB3C4\uD2B8C"
|
||||||
|
pixelRig: 1
|
||||||
|
useSoFraming: 0
|
||||||
|
cameraHeight: 45
|
||||||
|
distance: 5.5
|
||||||
|
distanceUp: 1.5
|
||||||
|
orthoSize: 4.6
|
||||||
|
pixelHeight: 480
|
||||||
|
envLookOn: 1
|
||||||
|
showButtons: 1
|
||||||
|
buttonParentPath: IngameUIs/WL_HUD
|
||||||
|
ownCanvas: 1
|
||||||
|
sortingOrder: 10
|
||||||
|
topMarginPx: 470
|
||||||
|
rightMarginPx: 24
|
||||||
|
buttonSizePx: {x: 200, y: 64}
|
||||||
|
buttonGapPx: 12
|
||||||
|
buttonFontPx: 26
|
||||||
|
buttonBgColor: {r: 0.06, g: 0.07, b: 0.09, a: 0.82}
|
||||||
|
buttonBgOnColor: {r: 0.16, g: 0.42, b: 0.86, a: 0.9}
|
||||||
|
buttonTextColor: {r: 0.95, g: 0.96, b: 0.98, a: 1}
|
||||||
|
cameraLabelFormat: "\uCE74\uBA54\uB77C: {0}"
|
||||||
|
envLabelFormat: "\uBC30\uACBD: {0}"
|
||||||
|
envOnText: ON
|
||||||
|
envOffText: OFF
|
||||||
|
buttonEdge:
|
||||||
|
enabled: 1
|
||||||
|
outlineWidth: 0.24
|
||||||
|
outlineColor: {r: 0.05, g: 0.03, b: 0.02, a: 1}
|
||||||
|
shadowOffset: {x: 0.4, y: -0.4}
|
||||||
|
shadowColor: {r: 0, g: 0, b: 0, a: 0.75}
|
||||||
|
shadowSoftness: 0.18
|
||||||
|
faceDilate: 0.06
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ba1e55fec246b7a409635129001f5716
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,221 @@
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// WLLookModeSettings.cs — 인게임 룩 토글(카메라 모드 · 배경 룩)의 값 단일 출처 (WL-814d · #814)
|
||||||
|
//
|
||||||
|
// PD 제안(2026-09-10) 「토글 형태로 카메라를 바꿔서 볼 수 있게」 · 발주서 WL-814d §1.
|
||||||
|
//
|
||||||
|
// ■ 왜 이 에셋인가 (C45)
|
||||||
|
// 프리셋 값(투영 · 궤도 각 · 거리 · 직교 반높이 · 픽셀 높이)과 버튼 좌표·문구를 코드 상수로 두지 않는다.
|
||||||
|
// 게임이 읽는 값은 `Resources/WL/WLLookModeSettings.asset` 이다(PD 가 인스펙터에서 바꾼다).
|
||||||
|
// 🔴 여기 있는 값은 **비교용 프리셋**이다. 확정 값은 814b `WLPixelCameraSettings`(직교·픽셀)와
|
||||||
|
// 811o `WLCameraFramingSettings`(구도)가 계속 소유한다 — 이 파일은 그 둘에 **런타임으로 얹기만** 한다.
|
||||||
|
//
|
||||||
|
// ■ 좌표계 (px)
|
||||||
|
// 813c/813q 와 같은 **세로 1080×1920 기준 화면 픽셀**. px → 캔버스 유닛 환산은
|
||||||
|
// `WLHudLayoutSettings.UnitsPerPx(canvas)` 하나만 쓴다(산식 중복 0).
|
||||||
|
//
|
||||||
|
// ■ C8 롤백
|
||||||
|
// `enabled_ = 0` → 버튼 0 · 모드 0 고정 · 814b/811o 신규 API 호출 0 = 두 기능 원래 동작 100 %.
|
||||||
|
//
|
||||||
|
// 🔴 어셈블리 주의: Assets/WL/Look/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace WL.Look.Toggle
|
||||||
|
{
|
||||||
|
/// <summary>룩 모드 하나 = 카메라 프리셋 1줄(발주서 §1-1 표).</summary>
|
||||||
|
[System.Serializable]
|
||||||
|
public sealed class WLLookModePreset
|
||||||
|
{
|
||||||
|
[Tooltip("버튼에 찍히는 이름(예: 기본 · 도트A).")]
|
||||||
|
public string name = "기본";
|
||||||
|
|
||||||
|
[Tooltip("켜면 814b 픽셀 카메라 리그(직교 · 저해상도 RT)를 강제로 조립한다. 끄면 원근(리그 해제).")]
|
||||||
|
public bool pixelRig = false;
|
||||||
|
|
||||||
|
[Tooltip("켜면 아래 구도 3값을 쓰지 않고 811o WLCameraFramingSettings 의 SO 값을 그대로 쓴다(= 지금 구도). 0번 기본 모드용.")]
|
||||||
|
public bool useSoFraming = false;
|
||||||
|
|
||||||
|
[Tooltip("811o 궤도 파라미터 cameraHeight(도). 클수록 위에서 내려다본다.")]
|
||||||
|
public float cameraHeight = 74f;
|
||||||
|
|
||||||
|
[Tooltip("811o 궤도 거리(m) — min/max 에 같이 들어간다.")]
|
||||||
|
public float distance = 3.4f;
|
||||||
|
|
||||||
|
[Tooltip("811o 궤도 중심 높이 DistanceUp(m).")]
|
||||||
|
public float distanceUp = 2.2f;
|
||||||
|
|
||||||
|
[Tooltip("814b 직교 반높이 orthoSize(m). pixelRig 가 꺼져 있으면 쓰이지 않는다.")]
|
||||||
|
public float orthoSize = 3.8f;
|
||||||
|
|
||||||
|
[Tooltip("814b 저해상도 RT 세로 픽셀 수. pixelRig 가 꺼져 있으면 쓰이지 않는다.")]
|
||||||
|
public int pixelHeight = 480;
|
||||||
|
|
||||||
|
[Tooltip("envFollowsCameraMode 가 켜져 있을 때 이 모드에서 배경 룩(814c)을 켤지.")]
|
||||||
|
public bool envLookOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CreateAssetMenu(fileName = "WLLookModeSettings", menuName = "WL/Look Mode Settings", order = 35)]
|
||||||
|
public sealed class WLLookModeSettings : ScriptableObject
|
||||||
|
{
|
||||||
|
/// <summary>Resources 경로 — 런타임 로드 키.</summary>
|
||||||
|
public const string ResourcesPath = "WL/WLLookModeSettings";
|
||||||
|
|
||||||
|
private static WLLookModeSettings s_instance;
|
||||||
|
private static bool s_tried;
|
||||||
|
|
||||||
|
/// <summary>에셋이 없으면 null(호출부는 null 이면 전부 건너뛴다 = C8 롤백).</summary>
|
||||||
|
public static WLLookModeSettings Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (s_instance == null && !s_tried)
|
||||||
|
{
|
||||||
|
s_tried = true;
|
||||||
|
s_instance = Resources.Load<WLLookModeSettings>(ResourcesPath);
|
||||||
|
}
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>에디터에서 에셋을 새로 만들거나 값을 바꾼 뒤 캐시를 버린다.</summary>
|
||||||
|
public static void ClearCache() { s_instance = null; s_tried = false; }
|
||||||
|
|
||||||
|
/// <summary>프로브·QA 용 런타임 스위치(에셋을 건드리지 않고 끈다).</summary>
|
||||||
|
public static bool RuntimeDisabled;
|
||||||
|
|
||||||
|
/// <summary>룩 토글 축이 살아 있는가 = 에셋 존재 · enabled_ · 런타임 스위치 off.</summary>
|
||||||
|
public static bool Enabled
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RuntimeDisabled) return false;
|
||||||
|
var s = Instance;
|
||||||
|
return s != null && s.enabled_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 공통 ──────────────────────────────────────────────────────────────
|
||||||
|
[Header("공통 (C8 롤백)")]
|
||||||
|
[Tooltip("끄면 버튼 0 · 모드 0 고정 · 814b/811o 신규 API 호출 0 = 두 기능 원래 동작 100 %.")]
|
||||||
|
public bool enabled_ = true;
|
||||||
|
|
||||||
|
[Tooltip("켜면 모드 전환마다 Console 에 1줄 남긴다(문자열은 이 값으로 막고 만든다 · GC 주의).")]
|
||||||
|
public bool verboseLog = false;
|
||||||
|
|
||||||
|
// ── 모드 ──────────────────────────────────────────────────────────────
|
||||||
|
[Header("카메라 모드 (0 = 기본 원근 · 1~3 = 도트 프리셋)")]
|
||||||
|
[Tooltip("앱을 켰을 때 시작 모드. 0 = 지금과 같은 원근.")]
|
||||||
|
public int startMode = 0;
|
||||||
|
|
||||||
|
[Tooltip("맵을 다시 들어가도 같은 모드를 유지한다(앱 세션 동안).")]
|
||||||
|
public bool keepModeAcrossMaps = true;
|
||||||
|
|
||||||
|
[Tooltip("로비·타이틀에서는 카메라를 기본(0)으로 되돌리고, 인게임에 다시 들어가면 고른 모드를 복원한다.")]
|
||||||
|
public bool defaultInLobby = true;
|
||||||
|
|
||||||
|
[Tooltip("도트 모드로 바뀔 때 배경 룩(814c)을 프리셋의 envLookOn 값으로 함께 바꾼다.")]
|
||||||
|
public bool envFollowsCameraMode = true;
|
||||||
|
|
||||||
|
[Tooltip("배경 룩(814c) 시작 상태.")]
|
||||||
|
public bool envLookOnAtStart = false;
|
||||||
|
|
||||||
|
[Tooltip("모드 배열 — 버튼이 0 → 1 → 2 → … → 0 으로 순환한다. 0번은 기본(원근)이어야 한다.")]
|
||||||
|
public WLLookModePreset[] presets = new WLLookModePreset[]
|
||||||
|
{
|
||||||
|
// 0번은 「지금 화면 그대로」 = 811o SO 구도 · 리그 해제(useSoFraming 1 이라 아래 3값은 참고용).
|
||||||
|
new WLLookModePreset { name = "기본", pixelRig = false, useSoFraming = true, cameraHeight = 76f, distance = 3.0f, distanceUp = 2.4f, orthoSize = 3.8f, pixelHeight = 480, envLookOn = false },
|
||||||
|
new WLLookModePreset { name = "도트A", pixelRig = true, useSoFraming = false, cameraHeight = 74f, distance = 3.4f, distanceUp = 2.2f, orthoSize = 3.8f, pixelHeight = 480, envLookOn = true },
|
||||||
|
new WLLookModePreset { name = "도트B", pixelRig = true, useSoFraming = false, cameraHeight = 55f, distance = 4.5f, distanceUp = 1.8f, orthoSize = 4.2f, pixelHeight = 480, envLookOn = true },
|
||||||
|
new WLLookModePreset { name = "도트C", pixelRig = true, useSoFraming = false, cameraHeight = 45f, distance = 5.5f, distanceUp = 1.5f, orthoSize = 4.6f, pixelHeight = 480, envLookOn = true },
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── HUD 버튼 ──────────────────────────────────────────────────────────
|
||||||
|
[Header("HUD 버튼 (813q RunHud 방식 · 프리팹 0 · Safe Area 안)")]
|
||||||
|
[Tooltip("끄면 버튼 노드를 만들지 않는다(시연이 끝나면 여기만 끈다 — 모드 전환은 코드/프로브로 계속 가능).")]
|
||||||
|
public bool showButtons = true;
|
||||||
|
|
||||||
|
[Tooltip("버튼을 붙일 부모 노드의 UI 루트 기준 경로(813y·813q 실측 = IngameUIs/WL_HUD · SafeAreaFitter 아래).")]
|
||||||
|
public string buttonParentPath = "IngameUIs/WL_HUD";
|
||||||
|
|
||||||
|
[Tooltip("전용 Canvas(overrideSorting)로 띄운다 — 813q 결과창(40)·813z 부활 팝업(60)보다 아래.")]
|
||||||
|
public bool ownCanvas = true;
|
||||||
|
|
||||||
|
[Tooltip("전용 Canvas 의 sortingOrder. 결과창 40 · 부활 60 보다 낮게 둔다.")]
|
||||||
|
public int sortingOrder = 10;
|
||||||
|
|
||||||
|
[Tooltip("Safe Area 상단에서 버튼 위쪽까지 여백(px). 813c 보스 바(190~256)·813q 런 HUD(272~394)·813tj 보스 배너(300~450) 아래.")]
|
||||||
|
public float topMarginPx = 470f;
|
||||||
|
|
||||||
|
[Tooltip("Safe Area 오른쪽에서 버튼 오른쪽 끝까지 여백(px).")]
|
||||||
|
public float rightMarginPx = 24f;
|
||||||
|
|
||||||
|
[Tooltip("버튼 하나의 크기(px · 발주서 하한 96×48). 폭 200 이면 런 HUD 폭 560(중앙 ±280) 오른쪽으로 36 px 비켜 있다.")]
|
||||||
|
public Vector2 buttonSizePx = new Vector2(200f, 64f);
|
||||||
|
|
||||||
|
[Tooltip("두 버튼 사이 세로 간격(px).")]
|
||||||
|
public float buttonGapPx = 12f;
|
||||||
|
|
||||||
|
[Tooltip("버튼 글자 크기(px).")]
|
||||||
|
public float buttonFontPx = 26f;
|
||||||
|
|
||||||
|
[Tooltip("버튼 배경색(꺼짐 상태).")]
|
||||||
|
public Color buttonBgColor = new Color(0.06f, 0.07f, 0.09f, 0.82f);
|
||||||
|
|
||||||
|
[Tooltip("버튼 배경색(켜짐 상태 = 도트 모드 · 배경 룩 ON).")]
|
||||||
|
public Color buttonBgOnColor = new Color(0.16f, 0.42f, 0.86f, 0.9f);
|
||||||
|
|
||||||
|
[Tooltip("버튼 글자색.")]
|
||||||
|
public Color buttonTextColor = new Color(0.95f, 0.96f, 0.98f, 1f);
|
||||||
|
|
||||||
|
[Tooltip("카메라 버튼 문구 — {0} 에 프리셋 이름이 들어간다.")]
|
||||||
|
public string cameraLabelFormat = "카메라: {0}";
|
||||||
|
|
||||||
|
[Tooltip("배경 버튼 문구 — {0} 에 아래 두 문구가 들어간다.")]
|
||||||
|
public string envLabelFormat = "배경: {0}";
|
||||||
|
|
||||||
|
[Tooltip("배경 룩 켜짐 문구.")]
|
||||||
|
public string envOnText = "ON";
|
||||||
|
|
||||||
|
[Tooltip("배경 룩 꺼짐 문구.")]
|
||||||
|
public string envOffText = "OFF";
|
||||||
|
|
||||||
|
[Tooltip("버튼 글자 테두리(813y WLTextFxUtil.ApplyEdge 재사용).")]
|
||||||
|
public WL.UI.WLTextEdge buttonEdge = new WL.UI.WLTextEdge
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
outlineWidth = 0.24f,
|
||||||
|
outlineColor = new Color(0.05f, 0.03f, 0.02f, 1f),
|
||||||
|
shadowOffset = new Vector2(0.4f, -0.4f),
|
||||||
|
shadowColor = new Color(0f, 0f, 0f, 0.75f),
|
||||||
|
shadowSoftness = 0.18f,
|
||||||
|
faceDilate = 0.06f,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── 조회 ──────────────────────────────────────────────────────────────
|
||||||
|
/// <summary>모드 개수(배열이 비어 있으면 1 = 기본만).</summary>
|
||||||
|
public int ModeCount { get { return presets != null && presets.Length > 0 ? presets.Length : 1; } }
|
||||||
|
|
||||||
|
/// <summary>모드 번호를 배열 범위로 감싼다(0 → 1 → … → 0 순환).</summary>
|
||||||
|
public int Wrap(int mode)
|
||||||
|
{
|
||||||
|
int n = ModeCount;
|
||||||
|
int m = mode % n;
|
||||||
|
return m < 0 ? m + n : m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모드 프리셋(없으면 null).</summary>
|
||||||
|
public WLLookModePreset Preset(int mode)
|
||||||
|
{
|
||||||
|
if (presets == null || presets.Length == 0) return null;
|
||||||
|
return presets[Wrap(mode)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>모드 이름(없으면 번호).</summary>
|
||||||
|
public string ModeName(int mode)
|
||||||
|
{
|
||||||
|
var p = Preset(mode);
|
||||||
|
return p != null && !string.IsNullOrEmpty(p.name) ? p.name : mode.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5787ec37785d76c42beb852ba605db1d
|
||||||
|
|
@ -95,6 +95,29 @@ namespace WL.PixelArt
|
||||||
static WLPixelCameraSettings Cfg { get { return WLPixelCameraSettings.Instance; } }
|
static WLPixelCameraSettings Cfg { get { return WLPixelCameraSettings.Instance; } }
|
||||||
static bool Active { get { return WLPixelCameraSettings.Enabled; } }
|
static bool Active { get { return WLPixelCameraSettings.Enabled; } }
|
||||||
|
|
||||||
|
// ── WL-814d 런타임 오버라이드 — 룩 토글이 SO `enabled 0`(F-23) 이어도 리그를 강제로 켠다(값도 SO 보다 우선).
|
||||||
|
// 아무도 부르지 않으면 s_rtOn = false 라 기존 동작과 완전히 같다.
|
||||||
|
static bool s_rtOn; static float s_rtOrtho = 3.8f; static int s_rtPx = 480;
|
||||||
|
/// <summary>룩 토글이 지금 리그를 강제로 켜고 있는가(진단).</summary>
|
||||||
|
public static bool RuntimeOverrideOn { get { return s_rtOn; } }
|
||||||
|
static float OrthoOf(WLPixelCameraSettings c) { return s_rtOn ? s_rtOrtho : c.orthoSize; }
|
||||||
|
static int PxOf(WLPixelCameraSettings c) { return s_rtOn ? s_rtPx : c.pixelHeight; }
|
||||||
|
/// <summary>WL-814d — 룩 토글이 부른다. on 이면 이 값으로 즉시 조립, off 면 해체. 값이 그대로면 무동작.</summary>
|
||||||
|
public static string SetRuntimeOverride(bool on, float orthoSize, int pixelHeight)
|
||||||
|
{
|
||||||
|
int px = Mathf.Max(3, pixelHeight);
|
||||||
|
if (on == s_rtOn && (!on || (orthoSize == s_rtOrtho && px == s_rtPx))) return "변화 없음(on=" + on + ")";
|
||||||
|
var cam = s_src != null ? s_src : Camera.main;
|
||||||
|
if (IsAssembled) Teardown("814d 전환");
|
||||||
|
s_rtOn = on; s_faulted = false; s_nextPoll = 0f;
|
||||||
|
if (on) { s_rtOrtho = orthoSize; s_rtPx = px; }
|
||||||
|
var cfg = Cfg;
|
||||||
|
if ((!on && !Active) || cfg == null) return "오버라이드 해제(on=" + on + ")";
|
||||||
|
if (Application.isPlaying) Boot(); // SO enabled 0 이면 러너가 없다 — 여기서 띄운다
|
||||||
|
if (cam == null) return "Camera.main 없음 — 다음 경계에서 조립";
|
||||||
|
return Assemble(cfg, cam) ? "조립 ortho=" + OrthoOf(cfg).ToString("0.###") + " px=" + PxOf(cfg) : "조립 실패";
|
||||||
|
}
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
// 틱 (러너가 매 프레임 부른다 · 에디트 모드에서는 프로브가 직접 부를 수 있다)
|
// 틱 (러너가 매 프레임 부른다 · 에디트 모드에서는 프로브가 직접 부를 수 있다)
|
||||||
// ─────────────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -105,7 +128,7 @@ namespace WL.PixelArt
|
||||||
if (s_faulted) return;
|
if (s_faulted) return;
|
||||||
|
|
||||||
// C8 — 기능이 꺼지면 즉시 원상복구.
|
// C8 — 기능이 꺼지면 즉시 원상복구.
|
||||||
if (!Active) { if (IsAssembled) Teardown("off"); return; }
|
if (!Active && !s_rtOn) { if (IsAssembled) Teardown("off"); return; } // WL-814d 오버라이드는 SO 를 넘는다
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -303,7 +326,7 @@ namespace WL.PixelArt
|
||||||
|
|
||||||
// ── Critter 값
|
// ── Critter 값
|
||||||
float aspect = ScreenAspect();
|
float aspect = ScreenAspect();
|
||||||
int px = Mathf.Max(3, cfg.pixelHeight);
|
int px = Mathf.Max(3, PxOf(cfg));
|
||||||
s_mgr.FollowedTransform = s_proxy;
|
s_mgr.FollowedTransform = s_proxy;
|
||||||
s_mgr.VoxelGridMovement = cfg.voxelGridMovement;
|
s_mgr.VoxelGridMovement = cfg.voxelGridMovement;
|
||||||
s_mgr.SubpixelAdjustments = cfg.subpixelAdjustments;
|
s_mgr.SubpixelAdjustments = cfg.subpixelAdjustments;
|
||||||
|
|
@ -332,7 +355,7 @@ namespace WL.PixelArt
|
||||||
|
|
||||||
// ── 원본 카메라 = 화면 Base 로 남되 월드는 그리지 않는다(투영만 픽셀 카메라와 맞춘다)
|
// ── 원본 카메라 = 화면 Base 로 남되 월드는 그리지 않는다(투영만 픽셀 카메라와 맞춘다)
|
||||||
s_src.orthographic = true;
|
s_src.orthographic = true;
|
||||||
s_src.orthographicSize = cfg.orthoSize;
|
s_src.orthographicSize = OrthoOf(cfg);
|
||||||
s_src.cullingMask = cfg.mainCameraCullingMask;
|
s_src.cullingMask = cfg.mainCameraCullingMask;
|
||||||
if (cfg.mainCameraSolidBlack)
|
if (cfg.mainCameraSolidBlack)
|
||||||
{
|
{
|
||||||
|
|
@ -350,7 +373,7 @@ namespace WL.PixelArt
|
||||||
Assembled++;
|
Assembled++;
|
||||||
s_hasLastRot = false;
|
s_hasLastRot = false;
|
||||||
s_snapSuspended = false;
|
s_snapSuspended = false;
|
||||||
LastLog = "조립 — ortho " + cfg.orthoSize.ToString("0.###")
|
LastLog = "조립 — ortho " + OrthoOf(cfg).ToString("0.###")
|
||||||
+ " · RT " + s_mgr.GameResolution.x + "×" + s_mgr.GameResolution.y
|
+ " · RT " + s_mgr.GameResolution.x + "×" + s_mgr.GameResolution.y
|
||||||
+ " · zoom " + s_mgr.GameCameraZoom.ToString("0.####")
|
+ " · zoom " + s_mgr.GameCameraZoom.ToString("0.####")
|
||||||
+ " · layer " + layer;
|
+ " · layer " + layer;
|
||||||
|
|
@ -362,8 +385,8 @@ namespace WL.PixelArt
|
||||||
public static float GameZoomFor(WLPixelCameraSettings cfg)
|
public static float GameZoomFor(WLPixelCameraSettings cfg)
|
||||||
{
|
{
|
||||||
if (cfg == null) return 5f;
|
if (cfg == null) return 5f;
|
||||||
if (!cfg.compensateViewZoom) return cfg.orthoSize;
|
if (!cfg.compensateViewZoom) return OrthoOf(cfg);
|
||||||
return cfg.orthoSize / CanvasOnScreenLimit(cfg.pixelHeight);
|
return OrthoOf(cfg) / CanvasOnScreenLimit(PxOf(cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Critter 가 캔버스 가장자리를 넘지 않으려고 쓰는 줌 한계 = 1 − 2/pixelHeight.</summary>
|
/// <summary>Critter 가 캔버스 가장자리를 넘지 않으려고 쓰는 줌 한계 = 1 − 2/pixelHeight.</summary>
|
||||||
|
|
@ -538,7 +561,7 @@ namespace WL.PixelArt
|
||||||
Teardown("reset");
|
Teardown("reset");
|
||||||
Boundaries = Assembled = Restored = SkippedLobby = SkippedNoCam = SkippedNoPrefab = Faults = 0;
|
Boundaries = Assembled = Restored = SkippedLobby = SkippedNoCam = SkippedNoPrefab = Faults = 0;
|
||||||
LastReason = LastLog = "";
|
LastReason = LastLog = "";
|
||||||
s_faulted = false;
|
s_faulted = false; s_rtOn = false; // WL-814d 오버라이드도 함께 걷는다
|
||||||
s_lastPc = null; s_lastMapId = int.MinValue; s_nextPoll = 0f;
|
s_lastPc = null; s_lastMapId = int.MinValue; s_nextPoll = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -558,7 +581,7 @@ namespace WL.PixelArt
|
||||||
static void Boot()
|
static void Boot()
|
||||||
{
|
{
|
||||||
if (s_runner != null) return;
|
if (s_runner != null) return;
|
||||||
if (!Active) return; // C8 — 러너 자체가 뜨지 않는다
|
if (!Active && !s_rtOn) return; // C8 — 러너 자체가 뜨지 않는다(WL-814d 오버라이드는 예외)
|
||||||
s_runner = new GameObject("[WL814b] PixelCameraRigRunner");
|
s_runner = new GameObject("[WL814b] PixelCameraRigRunner");
|
||||||
s_runner.hideFlags = HideFlags.HideAndDontSave;
|
s_runner.hideFlags = HideFlags.HideAndDontSave;
|
||||||
s_runner.AddComponent<PixelCameraRigRunner>();
|
s_runner.AddComponent<PixelCameraRigRunner>();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// WLLookToggleUi.cs — 인게임 룩 토글 HUD 버튼 2개 (WL-814d · #814)
|
||||||
|
//
|
||||||
|
// PD 제안(2026-09-10) 「토글 형태로 카메라를 바꿔서 볼 수 있게」 · 발주서 WL-814d §1-3.
|
||||||
|
//
|
||||||
|
// ■ 프리팹 diff 0 (813q RunHud · 813y 방식 그대로)
|
||||||
|
// 노드를 프리팹에 굽지 않는다 — 런타임 생성(`HideFlags.DontSave`).
|
||||||
|
// 부모는 `IngameUIs/WL_HUD`(802b/813y 실측 SafeAreaFitter 아래 = Safe Area 안 · 경로는 SO).
|
||||||
|
// 루트에 **전용 Canvas(overrideSorting) + GraphicRaycaster** 를 달아 서브 캔버스로 띄운다
|
||||||
|
// (813q2 결과창 40 · 813z 부활 팝업 60 보다 낮은 sortingOrder → 팝업이 뜨면 그 아래로 가려진다).
|
||||||
|
//
|
||||||
|
// ■ 좌표 (px · 세로 1080×1920 · 전부 SO)
|
||||||
|
// 상단 우측. 기본값은 813c 보스 HP 바(190~256) · 813q 런 HUD(272~394) · 813tj 보스 배너(300~450)
|
||||||
|
// **아래**(topMarginPx 470)이고, 가로는 런 HUD 폭(560 = 중앙 ±280) 오른쪽으로 비켜 있다.
|
||||||
|
// px → 캔버스 유닛 환산은 `WLHudLayoutSettings.UnitsPerPx(canvas)` 하나만 쓴다(산식 중복 0).
|
||||||
|
//
|
||||||
|
// ■ GC 0 (전환 외 프레임당 할당 0)
|
||||||
|
// 라벨은 **모드/배경 값이 바뀐 프레임에만** 다시 쓴다(문자열 생성도 그때만).
|
||||||
|
// Tick 은 int/bool 비교 2개뿐이다.
|
||||||
|
//
|
||||||
|
// ■ C8 롤백 — `WLLookModeSettings.enabled_ = 0` 또는 `showButtons = 0` → 노드 0 · 표시 0.
|
||||||
|
// 🔴 어셈블리 주의: Assets/WL/UI/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using WL.Look.Toggle;
|
||||||
|
|
||||||
|
namespace WL.UI
|
||||||
|
{
|
||||||
|
/// <summary>룩 토글 버튼 2개(카메라 모드 순환 · 배경 룩 on/off). 정적 · 노드는 런타임 생성.</summary>
|
||||||
|
public static class WLLookToggleUi
|
||||||
|
{
|
||||||
|
// ── 노드 ──────────────────────────────────────────────────────────────
|
||||||
|
private static Transform s_uiRoot;
|
||||||
|
private static RectTransform s_root;
|
||||||
|
private static Canvas s_canvas;
|
||||||
|
private static GraphicRaycaster s_raycaster;
|
||||||
|
private static Button s_camButton, s_envButton;
|
||||||
|
private static Image s_camBg, s_envBg;
|
||||||
|
private static TextMeshProUGUI s_camLabel, s_envLabel;
|
||||||
|
private static TMP_FontAsset s_font;
|
||||||
|
|
||||||
|
// ── 표시 캐시(변할 때만 쓴다 = GC 0) ──────────────────────────────────
|
||||||
|
private static int s_shownMode = int.MinValue;
|
||||||
|
private static bool s_shownEnv, s_shownEnvValid;
|
||||||
|
// 🔴 Bind 시점의 `IngameUIs` 는 아직 비활성일 수 있다 → 그때 잰 px 계수는 틀린다(캔버스 rect 가 안 잡힌다).
|
||||||
|
// 실제로 화면에 붙는 첫 프레임에 **한 번만** 다시 배치한다(그 뒤로는 bool 비교 1개).
|
||||||
|
private static bool s_layoutDone;
|
||||||
|
|
||||||
|
// ── 진단(프로브가 읽는다 · 실측만) ────────────────────────────────────
|
||||||
|
public static int CameraClicks, EnvClicks, LabelWrites;
|
||||||
|
public static bool Bound { get { return s_root != null; } }
|
||||||
|
public static bool Visible { get { return s_root != null && s_root.gameObject.activeSelf; } }
|
||||||
|
public static string CameraLabel { get { return s_camLabel != null ? s_camLabel.text : ""; } }
|
||||||
|
public static string EnvLabel { get { return s_envLabel != null ? s_envLabel.text : ""; } }
|
||||||
|
public static int SortingOrder { get { return s_canvas != null ? s_canvas.sortingOrder : -1; } }
|
||||||
|
/// <summary>🔴 중첩 캔버스로 인정됐는가. Unity 는 **부모 캔버스가 활성일 때만** overrideSorting 을 유지한다
|
||||||
|
/// (인게임 `IngameUIs` 는 프리팹 저장 상태가 비활성이라 조립 시점엔 false 일 수 있다 → Tick 이 다시 건다).</summary>
|
||||||
|
public static bool OverrideSorting { get { return s_canvas != null && s_canvas.overrideSorting; } }
|
||||||
|
/// <summary>버튼 rect(px · Safe Area 좌상단 기준 · 실측 보고용). i = 0 카메라 · 1 배경.</summary>
|
||||||
|
public static Rect ButtonRectPx(int i)
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return new Rect();
|
||||||
|
float w = s.buttonSizePx.x, h = s.buttonSizePx.y;
|
||||||
|
float left = 1080f - s.rightMarginPx - w; // 기준 폭 1080 (813c 좌표계)
|
||||||
|
float top = s.topMarginPx + (i == 1 ? h + s.buttonGapPx : 0f);
|
||||||
|
return new Rect(left, top, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static WLLookModeSettings St { get { return WLLookModeSettings.Instance; } }
|
||||||
|
|
||||||
|
// ── 부팅 (노드 0 · 프리팹 diff 0) ─────────────────────────────────────
|
||||||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||||
|
private static void Boot()
|
||||||
|
{
|
||||||
|
if (!WLLookModeSettings.Enabled) return; // C8 — 훅조차 걸지 않는다
|
||||||
|
LookModeHub.UiTick = OnUiTick; // 러너는 허브 것 하나만 쓴다
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>허브 러너가 매 프레임 부른다(바인딩 + 라벨 갱신).</summary>
|
||||||
|
private static void OnUiTick()
|
||||||
|
{
|
||||||
|
var ui = NewGameUI.Ins;
|
||||||
|
if (ui != null && !ReferenceEquals(ui.transform, s_uiRoot)) Bind(ui.transform);
|
||||||
|
Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 조립 (런타임 생성 · 프리팹 diff 0) ────────────────────────────────
|
||||||
|
/// <summary>UI 루트(NewGameUI)에서 부모를 찾아 버튼 노드를 만든다. 이미 물려 있으면 그대로.</summary>
|
||||||
|
public static string Bind(Transform uiRoot)
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return "WLLookModeSettings 에셋 없음";
|
||||||
|
if (!WLLookModeSettings.Enabled || !s.showButtons) return "off — 조립 생략";
|
||||||
|
if (uiRoot == null) return "uiRoot=null";
|
||||||
|
if (s_root != null && s_uiRoot == uiRoot) return "이미 연결됨 — " + s_root.name;
|
||||||
|
|
||||||
|
s_uiRoot = uiRoot;
|
||||||
|
var prt = WLVignetteUtil.FindUiPath(uiRoot, s.buttonParentPath) as RectTransform;
|
||||||
|
if (prt == null) return "부모 없음 — 경로 \"" + s.buttonParentPath + "\"";
|
||||||
|
if (s_font == null) s_font = RunHud.BorrowFont(uiRoot);
|
||||||
|
|
||||||
|
if (s_root == null || s_root.parent != prt)
|
||||||
|
{
|
||||||
|
var go = new GameObject("WL_LookToggle", typeof(RectTransform));
|
||||||
|
go.layer = WLTextFxUtil.UILayer;
|
||||||
|
go.hideFlags = HideFlags.DontSave; // 씬·프리팹에 굽지 않는다
|
||||||
|
s_root = (RectTransform)go.transform;
|
||||||
|
s_root.SetParent(prt, false);
|
||||||
|
s_camButton = NewButton(s_root, "btn_look_camera", out s_camBg, out s_camLabel, OnCameraClick);
|
||||||
|
s_envButton = NewButton(s_root, "btn_look_env", out s_envBg, out s_envLabel, OnEnvClick);
|
||||||
|
}
|
||||||
|
EnsureCanvas(s);
|
||||||
|
s_shownMode = int.MinValue; s_shownEnvValid = false; // 다음 Tick 이 라벨을 반드시 쓰게 한다
|
||||||
|
s_layoutDone = false;
|
||||||
|
string layout = ApplyLayout();
|
||||||
|
Tick();
|
||||||
|
return "연결 " + s.buttonParentPath + " · " + layout + " · sortingOrder=" + SortingOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>전용 Canvas(overrideSorting) + GraphicRaycaster — 813q2 결과창과 같은 방식.</summary>
|
||||||
|
private static void EnsureCanvas(WLLookModeSettings s)
|
||||||
|
{
|
||||||
|
if (s == null || s_root == null || !s.ownCanvas) return;
|
||||||
|
if (s_canvas == null)
|
||||||
|
{
|
||||||
|
s_canvas = s_root.GetComponent<Canvas>();
|
||||||
|
if (s_canvas == null) s_canvas = s_root.gameObject.AddComponent<Canvas>();
|
||||||
|
}
|
||||||
|
if (!s_canvas.overrideSorting) s_canvas.overrideSorting = true;
|
||||||
|
s_canvas.sortingOrder = s.sortingOrder;
|
||||||
|
if (s_raycaster == null)
|
||||||
|
{
|
||||||
|
s_raycaster = s_root.GetComponent<GraphicRaycaster>();
|
||||||
|
if (s_raycaster == null) s_raycaster = s_root.gameObject.AddComponent<GraphicRaycaster>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Button NewButton(RectTransform parent, string name, out Image bg, out TextMeshProUGUI label,
|
||||||
|
UnityEngine.Events.UnityAction onClick)
|
||||||
|
{
|
||||||
|
var t = WLVignetteUtil.NewChild(parent, name);
|
||||||
|
bg = t.GetComponent<Image>();
|
||||||
|
if (bg == null) bg = t.gameObject.AddComponent<Image>();
|
||||||
|
bg.raycastTarget = true; // 🔴 버튼이 터치를 먹는다(통과 0)
|
||||||
|
var b = t.GetComponent<Button>();
|
||||||
|
if (b == null) b = t.gameObject.AddComponent<Button>();
|
||||||
|
b.targetGraphic = bg;
|
||||||
|
b.onClick.RemoveListener(onClick);
|
||||||
|
b.onClick.AddListener(onClick);
|
||||||
|
label = WLTextFxUtil.NewText(t, "Label", s_font, TextAlignmentOptions.Center);
|
||||||
|
WLTextFxUtil.Stretch(label.rectTransform);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>SO 값으로 배치한다(px → 유닛 환산은 813c 산식 하나만 쓴다).</summary>
|
||||||
|
public static string ApplyLayout()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null) return "WLLookModeSettings 에셋 없음 — 배치 건너뜀";
|
||||||
|
if (s_root == null) return "노드 없음 — Bind 먼저";
|
||||||
|
|
||||||
|
// 🔴 우리 노드에는 전용 서브 캔버스가 붙어 있다 → `GetComponentInParent<Canvas>` 는 **자기 자신**을 준다.
|
||||||
|
// px 계수는 NewGameUI 루트 캔버스(CanvasScaler 보유)에서 와야 하므로 최상위 캔버스를 직접 찾는다.
|
||||||
|
float u = WLVignetteUtil.UnitsPerPx(RootCanvasOf(s_root));
|
||||||
|
float w = s.buttonSizePx.x * u, h = s.buttonSizePx.y * u, gap = s.buttonGapPx * u;
|
||||||
|
|
||||||
|
// 루트 = Safe Area 우상단 기준(anchor/pivot 1,1) → 노치·둥근 모서리 안쪽에서 시작한다.
|
||||||
|
s_root.anchorMin = s_root.anchorMax = new Vector2(1f, 1f);
|
||||||
|
s_root.pivot = new Vector2(1f, 1f);
|
||||||
|
s_root.anchoredPosition = new Vector2(-s.rightMarginPx * u, -s.topMarginPx * u);
|
||||||
|
s_root.sizeDelta = new Vector2(w, h * 2f + gap);
|
||||||
|
s_root.localScale = Vector3.one;
|
||||||
|
|
||||||
|
LayoutButton(s_camButton, s_camLabel, 0f, w, h, u, s);
|
||||||
|
LayoutButton(s_envButton, s_envLabel, -(h + gap), w, h, u, s);
|
||||||
|
|
||||||
|
return "pos" + s_root.anchoredPosition + " size" + s_root.sizeDelta +
|
||||||
|
" 버튼 " + s.buttonSizePx.x + "×" + s.buttonSizePx.y + "px unitsPerPx=" + u.ToString("F4");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>이 노드가 속한 **최상위** 캔버스(자기 서브 캔버스가 아니라 NewGameUI 루트 캔버스 · 비활성 계층에서도 맞다).</summary>
|
||||||
|
private static Canvas RootCanvasOf(Transform t)
|
||||||
|
{
|
||||||
|
Canvas top = null;
|
||||||
|
for (var p = t != null ? t.parent : null; p != null; p = p.parent)
|
||||||
|
{
|
||||||
|
var c = p.GetComponent<Canvas>();
|
||||||
|
if (c != null) top = c;
|
||||||
|
}
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LayoutButton(Button b, TextMeshProUGUI label, float y, float w, float h, float u, WLLookModeSettings s)
|
||||||
|
{
|
||||||
|
if (b == null) return;
|
||||||
|
var rt = (RectTransform)b.transform;
|
||||||
|
rt.anchorMin = rt.anchorMax = new Vector2(1f, 1f);
|
||||||
|
rt.pivot = new Vector2(1f, 1f);
|
||||||
|
rt.anchoredPosition = new Vector2(0f, y);
|
||||||
|
rt.sizeDelta = new Vector2(w, h);
|
||||||
|
rt.localScale = Vector3.one;
|
||||||
|
if (label != null)
|
||||||
|
{
|
||||||
|
label.fontSize = s.buttonFontPx * u;
|
||||||
|
label.color = s.buttonTextColor;
|
||||||
|
label.alignment = TextAlignmentOptions.Center;
|
||||||
|
WLTextFxUtil.ApplyEdge(label, s.buttonEdge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 클릭 ──────────────────────────────────────────────────────────────
|
||||||
|
private static void OnCameraClick() { CameraClicks++; LookModeHub.NextCameraMode(); Tick(); }
|
||||||
|
private static void OnEnvClick() { EnvClicks++; LookModeHub.ToggleEnvLook(); Tick(); }
|
||||||
|
|
||||||
|
/// <summary>프로브·QA: 버튼을 코드로 누른다(실제 onClick 경로 그대로).</summary>
|
||||||
|
public static string ClickCamera() { if (s_camButton == null) return "버튼 없음"; s_camButton.onClick.Invoke(); return CameraLabel; }
|
||||||
|
public static string ClickEnv() { if (s_envButton == null) return "버튼 없음"; s_envButton.onClick.Invoke(); return EnvLabel; }
|
||||||
|
|
||||||
|
// ── 매 프레임 — 값이 바뀐 프레임에만 라벨을 쓴다 ──────────────────────
|
||||||
|
public static void Tick()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
if (s == null || s_root == null) return;
|
||||||
|
bool show = WLLookModeSettings.Enabled && s.showButtons;
|
||||||
|
if (s_root.gameObject.activeSelf != show) s_root.gameObject.SetActive(show);
|
||||||
|
if (!show) return;
|
||||||
|
// 🔴 부모 캔버스(IngameUIs)가 켜지는 순간까지 Unity 가 overrideSorting 을 되돌린다 → 켜질 때까지 다시 건다.
|
||||||
|
if (s_canvas != null && s.ownCanvas && !s_canvas.overrideSorting)
|
||||||
|
{ s_canvas.overrideSorting = true; s_canvas.sortingOrder = s.sortingOrder; }
|
||||||
|
// 화면에 실제로 붙는 첫 프레임에 한 번만 다시 배치한다(Bind 때는 캔버스 rect 가 없어 px 계수가 틀릴 수 있다).
|
||||||
|
if (!s_layoutDone && s_root.gameObject.activeInHierarchy) { s_layoutDone = true; ApplyLayout(); }
|
||||||
|
|
||||||
|
int mode = LookModeHub.CameraMode;
|
||||||
|
if (mode != s_shownMode)
|
||||||
|
{
|
||||||
|
s_shownMode = mode;
|
||||||
|
var p = s.Preset(mode);
|
||||||
|
if (s_camLabel != null) s_camLabel.SetText(string.Format(s.cameraLabelFormat, s.ModeName(mode)));
|
||||||
|
if (s_camBg != null) s_camBg.color = (p != null && p.pixelRig) ? s.buttonBgOnColor : s.buttonBgColor;
|
||||||
|
LabelWrites++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool env = LookModeHub.EnvLookOn;
|
||||||
|
if (!s_shownEnvValid || env != s_shownEnv)
|
||||||
|
{
|
||||||
|
s_shownEnv = env; s_shownEnvValid = true;
|
||||||
|
if (s_envLabel != null) s_envLabel.SetText(string.Format(s.envLabelFormat, env ? s.envOnText : s.envOffText));
|
||||||
|
if (s_envBg != null) s_envBg.color = env ? s.buttonBgOnColor : s.buttonBgColor;
|
||||||
|
LabelWrites++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 프로브 · 진단 ─────────────────────────────────────────────────────
|
||||||
|
public static string ResetState()
|
||||||
|
{
|
||||||
|
CameraClicks = EnvClicks = LabelWrites = 0;
|
||||||
|
s_shownMode = int.MinValue; s_shownEnvValid = false; s_layoutDone = false;
|
||||||
|
return "상태 초기화";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>노드까지 되돌린다(프로브 종료용).</summary>
|
||||||
|
public static string Teardown()
|
||||||
|
{
|
||||||
|
if (s_root != null)
|
||||||
|
{
|
||||||
|
if (Application.isPlaying) Object.Destroy(s_root.gameObject);
|
||||||
|
else Object.DestroyImmediate(s_root.gameObject);
|
||||||
|
}
|
||||||
|
s_root = null; s_canvas = null; s_raycaster = null;
|
||||||
|
s_camButton = s_envButton = null; s_camBg = s_envBg = null;
|
||||||
|
s_camLabel = s_envLabel = null; s_uiRoot = null;
|
||||||
|
ResetState();
|
||||||
|
return "teardown 완료";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Dump()
|
||||||
|
{
|
||||||
|
var s = St;
|
||||||
|
var r0 = ButtonRectPx(0); var r1 = ButtonRectPx(1);
|
||||||
|
return "[LookToggleUi] bound=" + Bound + " visible=" + Visible + " sortingOrder=" + SortingOrder +
|
||||||
|
" · 카메라 \"" + CameraLabel + "\" 배경 \"" + EnvLabel + "\"" +
|
||||||
|
" · 클릭 " + CameraClicks + "/" + EnvClicks + " 라벨쓰기 " + LabelWrites +
|
||||||
|
" · rect0 " + r0 + " rect1 " + r1 +
|
||||||
|
(s_root != null ? " · 노드 pos" + s_root.anchoredPosition + " size" + s_root.sizeDelta +
|
||||||
|
" SafeArea=" + (s_root.GetComponentInParent<SafeAreaFitter>(true) != null) : "") +
|
||||||
|
(s != null ? " · showButtons=" + s.showButtons + " top=" + s.topMarginPx + "px right=" + s.rightMarginPx + "px" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ff7a66491ab27c746a11062920310a6c
|
||||||
Loading…
Reference in New Issue