# FirstRising 비주얼 개선 — 세션 3 인수인계서 (2026-09-19) > 작성: 총괄PM · PD 지시 "다음 세션에서 작업 이어갈 수 있도록 세션 공유하고 이 세션은 작업 마무리". 이 문서 한 장으로 다음 세션을 시작한다. > 이전 인계서: `2026-09-19_비주얼개선_자율작업_인수인계.md`(세션 2 · Phase 1~10 상세) · `2026-09-19_비주얼개선_세션인수인계.md`(세션 1 · 원칙·프롬프트 원문). 대화 원문 요약: `2026-09-19.md` §5. ## 0. 다음 세션 첫 프롬프트 ``` FirstRising TestVisual 비주얼·타격감 작업을 이어서 진행해. 인수인계서 E:\BurningTimes\공유\대화로그\FirstRising\2026-09-19_비주얼개선_세션3_인수인계.md 를 먼저 읽고, §2 원칙을 지키면서 §6 남은 작업 중 내가 지시하는 것부터 진행해. 지시가 없으면 §6-1 성능 실측부터 해. 작업은 C:\BurningTimes\FirstRising 의 bt/work 브랜치에서 하고, 시작 전에 브랜치·Unity 연결 상태를 확인해. ``` ## 1. 현재 상태 한눈에 - 레포 `C:\BurningTimes\FirstRising` · 브랜치 **`bt/work` = `9a6bf3c02a`**(세션 3: 성능 실측 수정 `72d7124497` + 원격 병합 `8453d1d5fe` + 크래시 수정, 미push) · origin/master = origin/bt/work = `7d393378c4`(bluecell2 가 master↔bt/work 병합, TestVisual 커밋이 master 에 포함됨) - **원격 변경 영향**: 게임 `TestMonstersController` 가 이제 테이블의 보스 13·엘리트 9 를 플레이어 기준 격자에 스폰 → TestVisual "보스 1마리·상단 공터" 설정 무효(PD 결정 대기, 대화로그 `2026-09-19.md` §7) - 이 PC 저장소 `core.autocrlf=input`(pull 차단 재발 방지, 세션 3). Play 잔여(`CharacterFresnelEffect.mat`·`NanumBarunGothic SDF.asset`)는 pull 전에 `git restore` 로 되돌리면 됨 - 다른 작업자 실행 안내: `Assets/BurningTimes/Visual/README_TestVisual.md`(Unity 6000.3.9f1 · `Assets/Scenes/TestVisual.unity` Play · Game 뷰 1080×1920) - 게임 코드·원본 데이터 무수정 — 모든 연출은 TestVisual 씬 + `Assets/BurningTimes/Visual/` 안에서만 동작 - Unity CLI 연결(`com.unity.pipeline` 0.6.0-exp.1, 공개 레지스트리) — `unity command --project-path C:\BurningTimes\FirstRising <명령>` - 로컬 미커밋 파일(커밋 금지 · 작업 무관): `CharacterFresnelEffect.mat`(게임 FresnelRenderFeature 가 Play 중 기록) · `NanumBarunGothic SDF.asset`(동적 폰트 아틀라스) · `Coffin01.mat`(URP 자동 변환) · `TagManager.asset`(기존 로컬 변경) · 줄바꿈만 다른 설정 파일들 · `Assets/P09_Modular_Humanoid/`(구매 결정 전) · `Assets/_Recovery/` · `output/` ## 2. PD 지시·원칙 (누적 · 모두 유효) - 목표: 현재 에셋 + 셰이더·조명·후처리·VFX·카메라로 디아블로식 핵앤슬래시 "고퀄리티 3D 액션 RPG 비주얼 + 타격감" - **TestVisual 에서 먼저 모두 끝낸 뒤 원본 반영은 PD 지시 시.** 원본 URP·맵을 테스트에 쓸 땐 복사본으로 - 게임 코드·원본 에셋·ProjectSettings 무수정 · **커밋은 PD 지시 시에만**(지시가 있으면 `bt/work` 에만 — 2026-09-19 PD 지적 "내가 지시하기 전까진 커밋하지 말라고 했잖아") · **push·master 반영도 PD 지시 시만**(원격 bt/work 도 새 커밋 자동 push 금지) - **세션 전환 권고·요구 금지**(C40 훅 경고가 떠도 작업 계속) · 허락 대기·파일 보고로 작업을 멈추지 말 것 - 감사 에이전트 호출 금지(BT19-Org) · 무거운 에이전트 호출은 비용·선택지 먼저 보고(C50) - 모델·LoRA·필터 자율 다운로드 허락(자체 원칙: `.safetensors` 만, 실행파일·pickle 금지) · ComfyUI `C:\Comfyui`, 참고 pinterest·civitai - 기록: PD 지시 로그 BT18 ⑨ 줄 · 대화로그 `2026-09-19.md` §5 (안건 종료 시 1회, 간결) ## 3. 이번 세션 완료 (bt/work 커밋) | 커밋 | 내용 | |---|---| | `516fb88f28` `4e955f04d1` | 보스 1마리(EvilUndead7 idx 71) · 2배 크기 · 렌더 해상도 0.75 를 TestVisual 전용으로 이동(`WorldManager.cs` 변경은 `9026d835ce` 로 원복) | | `5fc02132f2` | 보스 근접 카메라 후퇴 · 플레이어 무적(`TVTestCheats`) · P09 여전사 외형 교체(`TVCharacterSwap`, lilToon→URP Lit 복사본) | | `6dd1563c6e` | 보스 스폰 → 묘지 위쪽 문 밖 공터 (2,-47) · 교체 캐릭터 몸 방향 버그 수정(HumanPoseHandler 몸통 값은 루트의 부모 기준) | | `058eac18a8` | 원래 캐릭터 복귀 · `TVMonsterZones`(구역 5곳 5~8마리, 순찰→피격 추격→10초 재생성, 5~8타 HP) · `TVPlayerAttackRange`(사거리 ×0.7=6.3 m, 적 없으면 공격 중지) · 평소 카메라 16 m/FOV 50 | | `c8125d3857` | WL 타격감 이식(`HitFeelDirector` 재작성 + `TVPresentationHook`): 등급별 히트스톱·흔들림·펀치·경직·넉백 + PD 조정 | | `06565f107c` | `TVDamageNumbers` 금색 외곽선 데미지 숫자(게임 숫자 스프라이트는 이 씬에서만 끔) · 베기 줄기·별 섬광 | | `9a66b66e60` | `TV_AnimatedProxy` 셰이더 + `TVMonsterProxies`(몬스터 실제 그림자·보스 슈퍼 아머 외곽선) · `TVBossLights` · `TVMonsterLocomotion`(보폭 실측·발 미끄러짐 제거) · `TVPreDamageHook`(플레이어 피격 숫자) · 이동 방향 | | `f61aca517c` | `README_TestVisual.md`(다른 작업자 실행 안내) → `git push -u origin bt/work` | PD 최종 조정값(씬 저장됨): 피격 붉은 섬광 세기 0.1(0.03 s 유지·0.06 s 소멸) · 경직 0.09/0.06/0.04 s(히트 모션 유지) · 넉백 0.2 m(모션 없는 몬스터 0.12 m, 같은 대상 0.35 s 간격) · 보스 슈퍼 아머(경직·밀림·섬광 없음, 외곽선 0.03 m) · 사거리 원 0.012 m·알파 0.12 · 데미지 숫자 72 px·외곽선 0.13·dilate −0.15 · 보스 이동 약 7 m/s(보폭 × 0.55, 슬로모션 방지) ## 4. TestVisual 구성 — `BT_Visual` 오브젝트 | 컴포넌트 | 역할 | |---|---| | `VisualProfileSwitcher` | Play 중에만 전용 URP(`Visual/Settings/URP_TestVisual*`) 로 교체, 렌더 0.75 | | `HitFeelDirector` | 타격감 전체(등급·히트스톱·흔들림·펀치·섬광·경직·넉백·불꽃·처치 조명·보스 슬로모·플레이어 피격) · 이벤트 `Damaged`·`PlayerDamaged` | | `TVDamageNumbers` | 데미지 숫자(TMP, Black Han Sans, `Materials/UI/TV_DamageNumber.mat`) | | `TVPlayerAttackRange` | 사거리 배율·공격 중지(`Character.suppressCastSkill`)·대상 해제·사거리 원 | | `MonsterZones`(`TVMonsterZones`) | 구역 Zone1~5 · 몬스터 번호·수·HP · 순찰 | | `TVMonsterLocomotion` | 보폭 표(우클릭 Measure Stride Speeds) · 이동 속도 보폭×0.55~1.6 · 재생 속도 = 속도 ÷ 보폭 | | `TVMonsterProxies` | 몬스터 메시 복사본(그림자만 / 보스는 그림자+외곽선) | | `TVBossLights` · `TVCameraProfile` · `TVBossScale` | 보스 키·림 조명 · 카메라 16 m/보스 18.5 m · 보스 2배 | | `TVTestCheats` · `TVCharacterSwap`(꺼짐) | 무적 · P09 교체 | | 기타 | `TVEntityBlobShadows` · `TVShadowCasterSanitizer`(그림자 패스 없는 캐스터 끄기 — 크래시 방지) · `TVLightFlicker` · `TVFollowTarget` · Volume · 키 라이트 | ## 5. 기술 메모 (다음 작업에 필요한 실측 사실) - **ECS 순서**: `GpuEcsAnimatorSystemGroup`(Simulation, Transform 전) → `TR_UpdateSystemGroup`(CharacterSystem = 몬스터 AI, DamageSystem) → Presentation. 훅 2개(DisableAutoCreation, TestVisual 에서만 생성): `TVPresentationHook`(Presentation 맨 앞 — 피해 적용 뒤·GPU 업로드 전) · `TVPreDamageHook`(DamageSystem 직전) - 렌더 직전 훅에서 바꾼 애니 재생 속도는 다음 프레임 애니메이터가 게임 AI 보다 먼저 쓴다 → 게임이 매 프레임 1 로 덮어써도 유지됨 - 몬스터 AI: 항상 플레이어 추격 · `FrozenMoveComponent.frozen=1` 이면 이동 안 함 · frozen 아니면 플레이어와 19.3 m(`maxBoundDistance` 17.3+2) 넘을 때 근처로 순간이동 · `SpawnMonsterEntityInEditor(..., blind=1)` 이면 기절 상태로 생성(에디터 전용 API) · HP 는 스테이지 가중치로 결정(인자 hp 무시) - 몬스터 GPU 애니메이션: **Walk(1) 칸은 전부 빈 클립** — Run(2)만. 실제 이동 클립은 오버라이드 컨트롤러의 run/walk/fly 클립(미라 walk3_inplace 등) · 보폭(몸 크기 1): 미라 0.31 · 스켈레톤 1.92 · 좀비 1.82 · EU5 0.85 · EU 1.17 · EU2 0.58 · EU6 0.53 · 보스 3.74 · 레이스(비행) 제외 - 몬스터 셰이더 `URP_Lit_AnimatedMesh`: 그림자 패스는 LOD 1000 서브셰이더에만, 게임은 Midium(셰이더 LOD 999) → 그림자 없음. 애니메이션 = `GpuEcsAnimatorMobile.cginc` `AnimateBlend_half`(uv1 = 본 인덱스, `_AnimatedBoneMatrices`, 인스턴스 `_AnimationState`) · 피격 색 = 발광 += `_RimAmount`×(1−N·V)×`_RimColor`, 게임 피격 색은 HDR 빨강 (3,0,0) - 메시 복사본: `em.Instantiate(메시 엔티티)` 는 `GpuEcsAnimatedMeshComponent` 를 그대로 가져가 원본과 같이 움직임 · `RegisterMaterial/RegisterMesh` 후 `MaterialMeshInfo` 교체 · 원본 LinkedEntityGroup 밖이라 쓰러짐·비활성 때 직접 삭제해야 함 - 플레이어: 자동 공격 = `PlayerInputSystem.UseSkill`(적 없어도 발사) · 사거리 = 현재 무기 스킬 `targetingRange`(9 m) · 무적이면 DamageSystem 이 피해 요청을 버림(`takeDamageCount` 만 증가) - 시간: 게임 팝업(`PopupBase`)이 timeScale 0 → 닫을 때 1 로 복원 · 히트스톱은 0.0001 을 써서 구분 - `HumanPoseHandler` 의 bodyPosition/Rotation 은 루트의 **부모** 기준 - 크래시 이력: 그림자 캐스터인데 활성 서브셰이더에 ShadowCaster 패스가 없는 엔티티 → SRP Batcher 네이티브 크래시 → Sanitizer 로 방지 중. 새 셰이더에는 반드시 ShadowCaster 패스를 넣을 것. **판정은 LOD 999 기준**(`9a6bf3c02a`): 에디터 Play 는 LOD 1000/무제한으로 시작해 늦게 999 로 내려가므로, 현재 LOD 로 판정·캐시하면 몬스터 셰이더(LOD 1000 에만 ShadowCaster)가 통과해 크래시(09-19 18:11) - 재질 조회: 게임 `CharacterAbnormalMaterialJob` 이 상태 이상 뒤 원래 재질을 **런타임 등록 ID** 로 써 넣는다 → 그런 엔티티는 `RenderMeshArray.GetMaterial` 이 null. 원래 재질은 `Fx.AbnormalMaterialComponent.originalBatchMaterialID` → `EntitiesGraphicsSystem.GetMaterial(id)` 로 찾는다(`72d7124497`) ## 6. 남은 작업 · PD 결정 대기 1. ~~성능 실측~~ **완료(세션 3, `72d7124497`)** — 결과·남은 결정은 대화로그 `2026-09-19.md` §6. 초과·근접: 묘지 Draw 250~270 · SetPass 전투 최대 79 · 렌더 0.75. PD 결정 대기: 품질 등급(Low/Medium/High) 분리 · 실기기 측정 경로(TestVisual 은 에디터 전용 스폰이라 기기 빌드 불가). 측정 도우미 조각: ProfilerRecorder 를 `AppDomain.SetData` 에 보관해 여러 eval 에 걸쳐 수집 · 스킬 선택 팝업은 `EditorApplication.update` 콜백으로 `CommonSkillSelectUI.OnSelectSkill(availableSkillJsons[0])` 자동 선택 · Frame Debugger 는 에디터 창 최소화 상태에선 캡처 0 → 기능 토글 방식 사용 2. PD 피드백 대기: 보스 이동 약 7 m/s(너무 빠르면 `TVMonsterLocomotion.minMoveFactor` 낮추기 또는 보스 크기 조정) · 붉은 섬광 세기·숫자 크기 등 체감 조정 3. P09 구매 여부(PD) — 구매 시 에셋 커밋 여부 결정 4. 원본(게임 본편) 반영 — PD 지시 시: 표준 PostProcessData(또는 UberPost 그레이딩 복원)·Volume · 맵 LightingData 점검 · 몬스터 셰이더 LOD 0 에 ShadowCaster 추가(복사본 대신) · 그림자 거리 · 타격감·데미지 숫자·보폭 맞춤을 ECS 시스템으로 이식 · 품질 등급별 on/off 5. 추가 개선 후보(WL 에 있고 아직 안 옮긴 것): 연속 처치 문구(DOUBLE/TRIPLE…)·보스 처치 킬캠 줌 · 데미지 숫자 병합(0.25 s 합산 ×N) · 몬스터 림 · ComfyUI 지면 텍스처 ## 7. 작업 방법 (실증 · 다음 세션용 조각 코드) - 스크래치패드 도우미는 세션마다 새로 만들어야 한다. 핵심 조각: - Play 대기: `editor_play` 후 3초 간격으로 `GameObject.Find("Ariel")` 존재·`Time.timeScale==1` 확인 - 플레이어 이동(eval): `PlayerTag`+`LocalTransform` 엔티티의 `Position` 을 (x, y, z) 로, `PhysicsVelocity` 0 - 컴파일 대기: `editor_status --json` 의 `compiling`·`domainReloadInProgress` 가 false, `status=="ready"` - 콘솔: `get_console_logs --json` · 비우기 = `UnityEditor.LogEntries.Clear()`(리플렉션) - 캡처: `capture_game_view --source screen --json` → base64 → 스크래치패드 PNG(Assets 저장 금지) - 규칙: 스크립트 수정 전 Play 중지 → `AssetDatabase.ImportAsset` → 컴파일 대기 · 씬 값은 편집 모드에서 `SerializedObject` + `EditorSceneManager.SaveScene` · `AssetDatabase.SaveAssets()` 금지(→ `SaveAssetIfDirty`) · 씬에 저장된 값이 스크립트 기본값을 덮으므로 기본값을 바꾸면 씬 값도 같이 바꿀 것 - 커밋 메시지는 Write 로 만든 파일을 `git commit -F` 로(PowerShell 5.1 `Set-Content -Encoding utf8` 은 BOM 을 붙임 — `f61aca517c` 제목 앞에 BOM 이 들어감, 기능 영향 없음) · 제목 다음 빈 줄 필수 - 명령 한 번이 5초 넘으면 CLI 응답만 끊기고 실행은 계속됨 → 무거운 작업은 나눠서