BurningTimesAi/공유/대화로그/FirstRising/2026-09-22_코드최적화_인수인계.md

213 lines
16 KiB
Markdown
Raw Permalink Normal View History

# FirstRising 코드·시스템 최적화 — 인수인계 (2026-09-22)
BT18-FirstRising · 이전 세션: 로비 UI 2차(㉝~㊸) + steam 2회 병합 + 문서 구조 정비.
**이 문서만 읽고 시작하면 된다.** 다음 세션은 **Fable 모델로 코드·시스템 최적화**를 진행한다(PD 2026-09-22).
## ⚠ 전제 — 이 게임은 PC(Windows) 게임이고 **위젯 모드가 핵심 기능**이다
바탕화면에 투명 창으로 캐릭터만 띄우는 **데스크톱 위젯 모드**가 제품의 핵심이다. 모바일은 쓰지 않는다(SDK 전부 제거됨).
따라서 **모든 기능은 빌드에서 동작해야 하고, 위젯 모드에서도 동작해야 한다.** 에디터에서만 되는 구현은 결함이다.
---
## §0 다음 세션 첫 프롬프트 (그대로 복사해 쓰세요)
```
FirstRising(FR) 코드·시스템 최적화 작업을 진행한다.
인계서: E:\BurningTimes\공유\대화로그\FirstRising\2026-09-22_코드최적화_인수인계.md — 이것부터 읽어라.
인계서 §0-1 에 이번 세션에서 열 문서가 정리돼 있다. 그 밖의 문서는 열지 마라.
전제: 이 게임은 PC 게임이고 위젯 모드가 핵심이다. 에디터에서만 되는 구현은 결함이다.
이번 세션 기본 방침 (PD 2026-09-22 지시):
1. 최적화를 고려한 객체지향 설계가 기본이다. 매 프레임 씬 전체 탐색·할당 금지.
2. 인간 개발자 정섭 님(bluecell2) 코드를 기준선으로, 설계와 스타일을 최대한 맞춘다. (매우 중요한 협업 규칙)
참고 파일: Assets/Scripts/UI/Lobby/ViewportLayoutController.cs · Assets/Scripts/Entity/DynamicSpawnEntitySystem.cs
3. 게임 원본 소스 수정이 허용됐다. 단 수정 지점에 '날짜 + 이유' 한글 주석을 반드시 남긴다.
1순위: §3-1 "위젯 모드에서 플레이어가 공격하지 못하는 버그" 를 먼저 잡는다.
2순위: §4 기술 부채 정리.
먼저 원인을 실측으로 확인하고 수정안·비용을 보고해라. 승인 후 진행한다.
```
---
## §0-1 이번 세션에서 열 문서 (이것만 열면 된다)
문서 구조가 2026-09-22 에 **3층**으로 정비됐다 — ① `CLAUDE.md` = 규칙만(자동 주입) ② `Docs/INDEX.md` = 색인 ③ 인계서 = 이번에 볼 것.
| 언제 | 문서 |
|---|---|
| 항상 (자동으로 읽힘) | `CLAUDE.md` — 항상 지키는 규칙만 있다(4.1 KB). 설명은 없다 |
| 무엇을 볼지 모를 때 | `Docs/INDEX.md` — "하려는 일 / 증상" 두 표로 찾아 들어간다 |
| §3-1 공격 불가 버그 | `Docs/ECS_Mono_브리지.md` · `Docs/ECS_전투루프.md` · `Assets/Scripts/System/Windows/README_위젯모드.md` |
| §4 기술 부채 정리 | `Docs/싱글턴_규약.md` · `Docs/ECS_전투루프.md` + 정섭 님 코드 2개(§5) |
| 빌드가 필요할 때 | `Docs/빌드_실행.md` |
그 밖의 문서는 **열지 않는다.** 필요해지면 색인에서 찾는다.
> 위젯 관련 43줄은 원래 `CLAUDE.md` 안에 있었으나 **`Assets/Scripts/System/Windows/README_위젯모드.md` 로 옮겼다**(매 세션 자동으로 읽혀 낭비라는 PD 지적). 내용은 그대로다.
---
## §1 현재 상태
- 브랜치 `bt/work`, 최신 **`20640c4f15`**(= origin/steam `67ccbe71f9 "위젯 작업"` 병합본). origin/bt/work 는 `737b565cf7` 까지 올라가 있다 — **병합 2건이 아직 푸시 전**이다.
- 컴파일 에러 0. 모바일 SDK 전부 제거(2026-09-21 PD "모바일 플랫폼 미사용" 결정), 잔여 폴더·`FacebookSDK` 안에 있던 FantasyAxeAndShield 45개도 원위치 복구 완료.
- 로비 구조가 정섭 님 손에 두 번 바뀌었다. **경로를 박아 두면 계속 깨진다**:
- `Lobby/PlayScene` → **`Lobby/LobbyPlayScene`**(프리팹 `Assets/GameAssets/LobbyPlayScene.prefab` 로 분리)
- `Canvas` · `GameCanvas` 아래에 **`CenterHolder` 래퍼 추가** → 내 숨김 경로 17개가 전부 깨졌다
- → `TVLobbySetup` 이 **계층에서 찾아 절대경로를 써 넣도록** 고쳤다("BattlePage 아래의 ChapterInfo" 식으로 지정). 래퍼가 더 끼어도 Apply 한 번이면 따라간다.
- 우리 루트는 `Lobby/LobbyPlayScene/BT_Visual`. 로비 UI 재적용은 에디터 메뉴 **`BurningTimes/Lobby 메뉴/Apply`** 한 방(멱등).
정섭 님이 로비를 다시 올리면 **그 버전을 받고(`--theirs`) Apply 를 다시 실행**한다. 내 버전으로 덮으면 그의 새 UI 가 사라진다.
### 미커밋 상태로 남겨 둔 것 (PD 확인 대기)
| 파일 | 내용 | 왜 안 올렸나 |
|---|---|---|
| `CLAUDE.md` · `Docs/*` · `Assets/Scripts/System/Windows/README_위젯모드.md` | 문서 3층 구조 정비(15.7 KB → 4.1 KB) | 정섭 님과 공유하는 파일이라 PD 승인 대기 |
| `ProjectSettings/ProjectSettings.asset` | `gpuSkinning 1→0`, `meshDeformation 1→0`, `useFlipModelSwapchain 0→1` | 성능·렌더링 영향. 유니티가 플랫폼 전환 중 바꾼 것으로 보임 |
| `Packages/manifest.json`·`packages-lock.json` | `com.unity.postprocessing`·`com.unity.shadergraph` 자동 추가 | 유니티 자동 해석 |
| `Assets/Plugins/Android/settingsTemplate.gradle` · `Assets/Scenes/TestVisual.unity` | 리임포트 부산물 | 내 작업 아님 |
> `README_위젯모드.md` 에 `useFlipModelSwapchain` 은 **false 여야 창 투명화가 된다**고 적혀 있는데 지금 로컬은 1(켬)이다. §6-1 참고.
---
## §2 이전 세션에서 한 로비 UI 작업 (반영·일부 푸시 완료)
| 기능 | 파일 |
|---|---|
| 하단 메뉴 6개(성장·장비·특성·컨텐츠·길드·포탈) + 재터치 시 닫기 | `Visual/Scripts/TVBottomMenu.cs` |
| 스킬 슬롯 6 + 자동 버튼 + 상자(인벤토리) 버튼 | `Visual/Scripts/TVSkillBar.cs` |
| 닉네임 옆 전투력(임시값 9,110) | `Visual/Scripts/TVPlayerPower.cs` |
| 로비 UI 숨김(CanvasGroup 방식) | `Visual/Scripts/TVLobbyUIHider.cs` |
| 이동 중 공격 시 이동 속도 65% | `Visual/Scripts/TVAttackMoveSpeed.cs` |
| 지도(포탈로 열기) | `Visual/Scripts/TVMapButton.cs` |
| 위 전부를 씬에 쓰는 에디터 도구 | `Editor/TVLobbySetup.cs` |
핵심 교훈 3가지(다시 밟지 말 것):
1. **씬에 한 번 저장된 값은 C# 기본값을 바꿔도 안 바뀐다.** 배치·크기 값은 `TVLobbySetup` 이 `SerializedProperty` 로 직접 써야 한다. 이것 때문에 "고쳤는데 그대로"가 여러 차례 반복됐다.
2. **로비 UI 는 오브젝트를 끄면 안 된다.** `MainPage`(현재 `Lobby/Canvas/CenterHolder/MainPage`)에 로비 전체를 초기화하는 `MainPageUI` 싱글톤이 있어, 끄면 로비 기능이 통째로 죽는다. CanvasGroup 투명+클릭불가로만 가린다.
3. **계층 경로를 박아 두지 않는다.** 정섭 님이 래퍼를 넣을 때마다 깨진다. 에디터에서 찾아 절대경로로 기록하고, 실행 중에는 탐색하지 않는다.
---
## §3 정섭 님이 내 코드를 고친 이유 (반드시 이해하고 갈 것)
정섭 님 커밋 `afdcdfba82` "위젯 투명화" 가 내 `TVMonsterZones.cs` 를 고쳤다.
```
-#if UNITY_EDITOR
void Update() { ... }
- Fx.WorldManager.Instance.SpawnMonsterEntityInEditor(...)
+ Fx.WorldManager.Instance.SpawnMonsterEntity(...)
-#endif
```
**내 몬스터 배치 코드가 통째로 `#if UNITY_EDITOR` 안에 있었고, 에디터 전용 API(`SpawnMonsterEntityInEditor`)를 쓰고 있었다 → 빌드에서는 몬스터가 한 마리도 안 나온다.** PD 지적: "네 코드가 위젯 상태(빌드에서 동작하지 않는 임시 더미 코드 기반)로 만들어졌다."
정섭 님이 함께 한 일:
- `WorldManager.SpawnMonsterEntityInEditor` → **`SpawnMonsterEntity`** 로 바꾸고 `#endif` 밖으로 빼 빌드에 포함
- `WorldManager.InitPlayer` 에 `UIManager.HasInstance()` 가드 추가(위젯 모드처럼 UI 없이 도는 경우 대비) + 조이스틱 세로 잠금 연결
- 신규 **`Assets/Scripts/Entity/DynamicSpawnEntitySystem.cs`** — 런타임 동적 스폰을 ECS `SystemBase`(Burst, `TR_UpdateSystemGroup`)로 처리. `TestSystem.cs` 의 에디터 테스트 경로 60줄 제거
- 신규 `Assets/Scripts/UI/Lobby/ViewportLayoutController.cs` — 위젯 모드 뷰포트 레이아웃
- 위젯 투명화 지식 43줄 문서화 — 지금은 `Assets/Scripts/System/Windows/README_위젯모드.md` 에 있다(§0-1 참고)
이어서 `67ccbe71f9 "위젯 작업"`(내 파일은 건드리지 않음):
- 신규 `GamePlayCanvas.prefab` · `CharacterPageUI` · `GamePlayUI` · `ItemPage2UI` · `SkillPageUI`
- `DesktopWidgetManager` · `HideOnWidgetMode` · `PageUI` 수정, `Canvas`/`GameCanvas` 아래 `CenterHolder` 추가
→ 앞으로 **에디터 전용 경로에 기대는 구현을 하지 않는다.** `#if UNITY_EDITOR`, `...InEditor` API, `[ExecuteInEditMode]` 에 의존한 게임 동작은 빌드에서 죽는다.
### §3-1 [1순위] 위젯 모드에서 플레이어가 공격하지 못한다 (빌드 확인 · PD 2026-09-22)
**가장 유력한 원인** — `Assets/BurningTimes/Visual/Scripts/TVPlayerAttackRange.cs`
```csharp
// 84행
player.suppressCastSkill = !EnemyInRange; // 사거리 안에 적이 없으면 자동 발사를 막는다
```
이 구성 요소는 "사거리 안에 살아 있는 적이 없으면 무기 스킬 자동 발사를 멈춰 이동 모션만 나오게" 하려고 만든 **TestVisual 연출용**이다.
`EnemyInRange` 가 한 번도 true 가 되지 않으면 `suppressCastSkill` 이 계속 true 로 남아 **플레이어가 영영 공격하지 않는다.**
확인할 경로(전부):
1. 몬스터가 스폰되지 않음 — `TVMonsterZones` 가 같은 `#if UNITY_EDITOR` 함정이었다(정섭 님이 고침). **다른 곳에 같은 패턴이 더 있는지 먼저 훑을 것.**
2. 사거리 계산이 스킬 버퍼(`skillBufferElement.targetingRange`)에 의존한다 — 위젯 모드 초기화 순서에서 비어 있을 수 있다.
3. `rangeScale = 0.7f` 를 곱해 사거리를 줄여 둔다. 원본 복원은 `OnDisable` 경로(269행)에만 있다.
4. `TVSpawnFilter` 가 스폰을 거르고 있을 수 있다.
**수정 방향(제안)** — 이 기능은 연출 목적이므로 위젯/빌드에서는 기본으로 **끄거나**, `suppressCastSkill` 을 건드리지 않는 모드를 두는 것이 안전하다.
고칠 때 `Character.suppressCastSkill` 을 누가 켜고 끄는지 전부 추적하고(`TVPlayerAttackRange` 외에 `TVAutoPilot` 도 조이스틱 입력을 만진다), 어떤 경로로도 true 로 굳지 않게 보장할 것.
---
## §4 기술 부채 — 2순위 (PD 2026-09-22 지적)
PD: "업데이트마다 캐릭터하고 몬스터를 모든 노드를 돌면서 찾고 있는데, 모바일에서는 퍼포먼스가 엄청나게 떨어진다. PC라도 최적화가 엉망이다. **최적화까지 고려한 객체지향 설계가 기본**이어야 한다."
`Assets/BurningTimes/Visual/Scripts/` 실측(호출 지점 수):
| 유형 | 건수 | 대표 파일 |
|---|---|---|
| `ToComponentDataArray` / `ToEntityArray` (프레임마다 Temp 배열 할당) | 17 | `TVMonsterZones`(4) · `TVPlayerAttackRange`(3) · `TVMonsterLocomotion`(3) · `TVBossLights`(3) · `HitFeelDirector`(3) · `TVCameraProfile`(2) |
| `GetComponentsInChildren` (계층 전체 순회) | 14 | `TVCharacterSwap`(6) · `TVXRayGate`(2) · `TVMonsterLocomotion`(2) · `TVAutoPilot`(2) · `TVArcherGear`(2) |
| `CreateEntityQuery` (재사용 안 하고 반복 생성) | 9 | `HitFeelDirector`(3) · `TVPlayerAttackRange`(2) · `TVMonsterZones`(2) |
| `GameObject.Find` / `FindObjectsByType` | 다수 | `TVXRayGate` · `TVSpawnFilter` · `TVMapButton` · `TVPlayerPower` |
특히 확인된 것:
- `TVCameraProfile.BossNearby()` — **매 프레임 `ToComponentDataArray` 2개 할당 + 전체 몬스터 순회**
- `TVPlayerPower.LateUpdate()` — **매 프레임 `ForceMeshUpdate()` + `GetPreferredValues()`**(TMP 메시 재생성)
- `TVLobbyUIHider.Find()` — 대상을 못 찾으면 **열린 씬 전체 Transform 순회**(0.25초 주기). 경로는 이제 Apply 가 절대경로로 써 주므로 평소엔 타지 않지만, 대비책 자체가 비싸다
- `TVAttackMoveSpeed.LateUpdate()` — 매 프레임 `CalcFinalSpeed()` + ECS `SetComponentData`
**정리 방침**
- 질의·참조는 필드로 한 번 만들고 월드/씬이 바뀔 때만 재생성한다.
- 폴링 대신 이벤트·더티 플래그. 불가피하면 주기(0.2~0.5초)로.
- 값이 바뀔 때만 계산한다(TMP 갱신, 속도 쓰기 등).
- 한 MonoBehaviour 가 생성·배치·상태·게임연동을 다 하지 않게 나눈다.
- **원본 코드를 고쳐도 된다(§5). 우회보다 제자리 수정이 낫다.**
---
## §5 지켜야 할 규칙
**코드**
- **정섭 님 코딩 스타일에 맞춘다**(2026-09-22 PD "매우 중요한 협업 규칙"). 실측한 스타일:
- `namespace Fx` · 비공개 필드 `m_` · 상수 `k` 접두 · `[SerializeField]` 는 필드 윗줄에 단독
- 클래스 위에 한글 `<summary>` 로 **무엇을·왜**. 주석은 동작 설명이 아니라 판단 근거를 남긴다
- **`Update()` 를 쓰지 않는다** — 실제로 주석 처리해 두고 Start·버튼 콜백·상태 전이에서만 호출한다
- 참조는 `Start` 에서 한 번 캐싱, 버튼은 `onClick.AddListener`
- ECS: `[BurstCompile]` + `SystemBase` + `RequireForUpdate<T>`, `NativeList` 는 `OnCreate` 에서 `Persistent` 로 한 번, 요청은 버퍼에 모아 일괄 처리
- 새 코드를 쓰기 전에 **같은 영역의 정섭 님 파일을 먼저 읽고** 그 형태를 따른다
- **원본 게임 소스 수정 허용**(2026-09-22 PD). 단 고친 자리에 **한글로 날짜 + 이유 주석** 필수.
예) `// 2026-09-22 이동 중 공격 시 발이 미끄러져 보여 이동 속도를 낮춘다(PD 지시 BT18)`
**문서 (2026-09-22 PD 지시)**
- `CLAUDE.md` 에는 **항상 지켜야 하는 규칙만** 넣는다. 매 세션 자동으로 읽히므로 설명·절차를 넣으면 계속 비용이 된다.
- 설명·구조·절차는 **주제별 문서 + `Docs/INDEX.md` 색인 한 줄**로 넣는다.
- 세션 인계서에는 **"이번 세션에서 열 문서"(§0-1) 절을 반드시** 둔다. 다음 세션은 인계서가 가리키는 문서만 연다.
**작업 방식**
- **커밋·푸시는 PD 지시가 있을 때만.** 수정·검증 후 작업 트리에 두고 보고한다.
- **내가 Play 를 켜지 않는다.** 대기(폴링·백그라운드 감시)도 걸지 않는다. 검증이 필요하면 무엇을 볼지 한 줄로 말하고 PD 판단에 맡긴다.
- PD Play 중에는 `AssetDatabase.Refresh()` 를 부르지 않는다 — 도메인이 다시 로드되며 런타임 UI가 파괴된다.
- `AssetDatabase.SaveAssets()` 금지 — `SaveAssetIfDirty` 를 쓴다.
- Unity Auto Refresh 는 꺼져 있다(`kAutoRefresh=False`). 파일을 써 두고 내가 원할 때 Refresh 한다.
- 긴 에디터 작업은 `--detach` 로 돌린다(5초 넘기면 콘솔에 빨간 타임아웃 로그가 남는다).
- 정섭 님이 로비를 올리면 **그의 씬을 받고 Apply 를 다시 실행**한다(§1).
- 보고는 한국어·간결·경어, 호칭 **PD님**. 진행중·보류만 보고한다.
---
## §6 남은 이슈
1. **`useFlipModelSwapchain` 이 1(켬)로 바뀌어 있다** — `README_위젯모드.md` 요구값은 false(켜면 알파가 버려져 투명화가 깨진다). 되돌릴지 확인 필요.
2. `gpuSkinning` · `meshDeformation` 이 0으로 꺼졌다 — 캐릭터 많은 화면 성능에 영향. 의도인지 확인 필요.
3. 문서 3층 구조 정비분이 **미커밋**이다(`CLAUDE.md`·`Docs/*`·`README_위젯모드.md`). 정섭 님과 공유하는 파일이라 PD 승인 후 올린다.
4. 병합 2건(`c452c9aae4`·`20640c4f15`)이 **미푸시**다.
5. 전투력 숫자는 임시값 9,110 이다. 실제 수식이 정해지면 `TVPlayerPower.Power()` 만 바꾸면 된다.
6. 자동 버튼은 켜짐/꺼짐 표시만 하고 실제 자동 전투에 연결돼 있지 않다.
7. 스킬 슬롯 6개는 배치·쿨타임 표현만이고 실제 스킬에 연결돼 있지 않다.
8. TestVisual2 의 안개·주변광(RenderSettings)이 로비에는 없다(로비 기본값). 적용 여부 PD 결정 대기.