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

16 KiB

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

// 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 결정 대기.