From 5f99650641859cfc61bfb6e9a127f83014441dbe Mon Sep 17 00:00:00 2001 From: swrring Date: Wed, 30 Sep 2026 11:59:05 +0900 Subject: [PATCH] =?UTF-8?q?docs(BT42~BT46):=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EB=A0=88=EB=B2=A8=EC=97=85=20=EB=81=84=EA=B8=B0=20=C2=B7=20?= =?UTF-8?q?=EC=9E=A5=EB=B9=84=20=EB=8F=99=EA=B8=B0=ED=99=94=20=C2=B7=20?= =?UTF-8?q?=EC=B5=9C=EC=86=8C=ED=99=94=20=EB=AA=A8=EB=93=9C=20=C2=B7=20?= =?UTF-8?q?=EB=A7=B5=20=EC=9D=B4=EB=8F=99=20=EB=A1=9C=EB=94=A9=20=C2=B7=20?= =?UTF-8?q?Claude=20=EC=95=B1=20=ED=8A=B8=EB=A0=88=EC=9D=B4=20=EA=B8=B0?= =?UTF-8?q?=EB=A1=9D=20=C2=B7=20=EC=84=B8=EC=85=98=20=EC=9D=B8=EC=88=98?= =?UTF-8?q?=EC=9D=B8=EA=B3=84=EC=84=9C=20=C2=B7=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=82=AC=EB=B3=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PD 지시 로그: BT42~BT46 결과(FR bt/work 4bca428e62 push) - 대화로그 FirstRising 2026-09-29(BT42) · 2026-09-30(BT43~45) · 조직운영 2026-09-30_Claude앱_트레이(BT46) - 세션 인수인계서 2026-09-30(첫 프롬프트 · 열 문서 · 현황 · 실측 사실 · 미검증) - unity_eval/bt_evals_0930: 이번 세션의 eval · 검증 스크립트 사본(원본 자리 %TEMP%\bt_evals 는 사라질 수 있음) Co-Authored-By: Claude Fable 5.1 --- 공유/PD_지시_트래킹/개발팀_PD_지시_로그.md | 5 + .../tools/unity_eval/bt_evals_0930/README.md | 10 ++ .../tools/unity_eval/bt_evals_0930/attach_bt44.cs | 37 ++++++ .../tools/unity_eval/bt_evals_0930/attach_equipsync.cs | 11 ++ .../tools/unity_eval/bt_evals_0930/attach_gate.cs | 11 ++ .../unity_eval/bt_evals_0930/attach_suppressor.cs | 11 ++ .../bt_evals_0930/boss_roundtrip.template.cs | 13 +++ .../tools/unity_eval/bt_evals_0930/check_state.cs | 9 ++ .../tools/unity_eval/bt_evals_0930/close_all.cs | 8 ++ .../tools/unity_eval/bt_evals_0930/close_info.cs | 3 + .../tools/unity_eval/bt_evals_0930/diag_blur1.cs | 24 ++++ .../tools/unity_eval/bt_evals_0930/diag_blur2.cs | 25 ++++ .../tools/unity_eval/bt_evals_0930/diag_blur3.cs | 25 ++++ .../tools/unity_eval/bt_evals_0930/diag_blur4.cs | 17 +++ .../tools/unity_eval/bt_evals_0930/ecs_weapon.cs | 11 ++ .../tools/unity_eval/bt_evals_0930/editor_ready.cs | 1 + .../tools/unity_eval/bt_evals_0930/equip_ui_state.cs | 20 ++++ .../tools/unity_eval/bt_evals_0930/exit_menu.cs | 8 ++ .../tools/unity_eval/bt_evals_0930/flag_on.cs | 1 + .../tools/unity_eval/bt_evals_0930/info_values.cs | 6 + .../tools/unity_eval/bt_evals_0930/install.cs | 3 + .../tools/unity_eval/bt_evals_0930/inv_equipment.cs | 4 + .../tools/unity_eval/bt_evals_0930/lv_addexp.cs | 1 + .../tools/unity_eval/bt_evals_0930/lv_addexp600.cs | 1 + .../tools/unity_eval/bt_evals_0930/lv_gate_off.cs | 1 + .../tools/unity_eval/bt_evals_0930/lv_gate_on.cs | 1 + .../tools/unity_eval/bt_evals_0930/lv_pick_card.cs | 4 + .../tools/unity_eval/bt_evals_0930/lv_state.cs | 10 ++ .../tools/unity_eval/bt_evals_0930/open_charinfo.cs | 1 + .../tools/unity_eval/bt_evals_0930/open_contents.cs | 1 + .../tools/unity_eval/bt_evals_0930/open_equipment.cs | 1 + .../tools/unity_eval/bt_evals_0930/open_growth.cs | 1 + .../tools/unity_eval/bt_evals_0930/open_info.cs | 3 + .../tools/unity_eval/bt_evals_0930/open_inventory.cs | 1 + .../tools/unity_eval/bt_evals_0930/open_trait.cs | 1 + .../tools/unity_eval/bt_evals_0930/play_age.cs | 1 + .../tools/unity_eval/bt_evals_0930/poll.cs | 8 ++ .../tools/unity_eval/bt_evals_0930/proxies.cs | 1 + .../tools/unity_eval/bt_evals_0930/refresh.cs | 2 + .../tools/unity_eval/bt_evals_0930/reset_tutorial.cs | 6 + .../unity_eval/bt_evals_0930/run_attack_verify.ps1 | 61 ++++++++++ .../tools/unity_eval/bt_evals_0930/run_bt44_probe.ps1 | 65 +++++++++++ .../tools/unity_eval/bt_evals_0930/run_bt44_verify.ps1 | 110 ++++++++++++++++++ .../tools/unity_eval/bt_evals_0930/run_crash_test.ps1 | 37 ++++++ .../tools/unity_eval/bt_evals_0930/run_daily_cycle.ps1 | 47 ++++++++ .../unity_eval/bt_evals_0930/run_daily_cycle2.ps1 | 25 ++++ .../unity_eval/bt_evals_0930/run_equip_verify.ps1 | 87 ++++++++++++++ .../unity_eval/bt_evals_0930/run_levelup_verify.ps1 | 61 ++++++++++ .../tools/unity_eval/bt_evals_0930/run_verify.ps1 | 45 +++++++ .../tools/unity_eval/bt_evals_0930/run_wsm_test.ps1 | 58 +++++++++ .../tools/unity_eval/bt_evals_0930/start_daily.cs | 4 + .../tools/unity_eval/bt_evals_0930/tp_to_monster.cs | 10 ++ .../tools/unity_eval/bt_evals_0930/travel_check.cs | 45 +++++++ .../tools/unity_eval/bt_evals_0930/travel_hook.cs | 71 +++++++++++ .../tools/unity_eval/bt_evals_0930/travel_state.cs | 8 ++ .../tools/unity_eval/bt_evals_0930/travel_unhook.cs | 18 +++ .../tools/unity_eval/bt_evals_0930/visible_probe.cs | 50 ++++++++ .../tools/unity_eval/bt_evals_0930/weapon_anim.cs | 6 + .../tools/unity_eval/bt_evals_0930/weapon_state.cs | 23 ++++ .../tools/unity_eval/bt_evals_0930/weapon_vis.cs | 8 ++ .../tools/unity_eval/bt_evals_0930/widget_probe.cs | 45 +++++++ .../tools/unity_eval/bt_evals_0930/wsm_diag.cs | 29 +++++ .../unity_eval/bt_evals_0930/wsm_pointer.template.cs | 48 ++++++++ .../tools/unity_eval/bt_evals_0930/wsm_state.cs | 37 ++++++ 공유/대화로그/FirstRising/2026-09-29.md | 34 ++++++ 공유/대화로그/FirstRising/2026-09-30.md | 110 ++++++++++++++++++ .../FirstRising/2026-09-30_세션인수인계.md | 86 ++++++++++++++ .../조직운영/2026-09-30_Claude앱_트레이.md | 45 +++++++ 68 files changed, 1581 insertions(+) create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/README.md create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_bt44.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_equipsync.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_gate.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_suppressor.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/boss_roundtrip.template.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/check_state.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_all.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_info.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur1.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur2.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur3.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur4.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/ecs_weapon.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/editor_ready.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/equip_ui_state.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/exit_menu.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/flag_on.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/info_values.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/install.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/inv_equipment.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp600.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_off.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_on.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_pick_card.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_state.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_charinfo.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_contents.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_equipment.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_growth.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_info.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_inventory.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_trait.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/play_age.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/poll.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/proxies.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/refresh.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/reset_tutorial.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_attack_verify.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_probe.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_verify.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_crash_test.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle2.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_equip_verify.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_levelup_verify.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_verify.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_wsm_test.ps1 create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/start_daily.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/tp_to_monster.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_check.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_hook.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_state.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_unhook.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/visible_probe.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_anim.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_state.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_vis.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/widget_probe.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_diag.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_pointer.template.cs create mode 100644 공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_state.cs create mode 100644 공유/대화로그/FirstRising/2026-09-29.md create mode 100644 공유/대화로그/FirstRising/2026-09-30.md create mode 100644 공유/대화로그/FirstRising/2026-09-30_세션인수인계.md create mode 100644 공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md diff --git a/공유/PD_지시_트래킹/개발팀_PD_지시_로그.md b/공유/PD_지시_트래킹/개발팀_PD_지시_로그.md index c0d9ed8..2a49727 100644 --- a/공유/PD_지시_트래킹/개발팀_PD_지시_로그.md +++ b/공유/PD_지시_트래킹/개발팀_PD_지시_로그.md @@ -33,6 +33,11 @@ C3·C13 위반에 해당. **즉시 자진 보고 후 소급 등록**. | # | 일시 | 지시 요지 | 처리 상태 | 산출물 경로 | 중단 사유 | 사후 조치 | |---|------|----------|----------|-----------|----------|----------| +| BT46-Org | 2026-09-30 09:40 | **Claude 윈도우 앱이 쓰지 않는 동안 트레이에서 사라짐 — 원인 확인 · 조치** (원문은 대화로그) | **원인 확정 · 조치 준비 완료(09-30 10:00) · PD 실행 대기** — 원인 [실측 · 앱 로그 + 윈도우 이벤트 로그]: 앱이 업데이트를 받아 둔 채 10분쯤 쉬면 창 · 트레이를 내리고 윈도우에 강제 종료 + 새 버전 등록을 요청하는데, 09-23 02:56 부터 앱이 자기 프로세스를 작업 묶음에 넣으면서(`[windows-app-job] joined`) 윈도우가 앱을 닫지 못한다(AppXDeploymentServer 419 · 404 — 0x800700E9 / 0x8007001F). 그 전 18회는 2~40초 만에 재시작, 그 뒤 6회는 전부 실패. 실패하면 앱은 창 · 트레이 없이 숨어서 남고 아이콘을 눌러도 뜨지 않는다. 앱 내부 결함이라 직접 고칠 수 없음 → 감시 스크립트 작성 · 시험 모드 4가지 상황 통과. 작업 스케줄러 등록은 시스템 설정이라 PD 가 명령 1개로 실행 | `%LOCALAPPDATA%/ClaudeTrayWatchdog/`(watchdog.ps1 · run-hidden.vbs) · `공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md` | — | 등록 뒤 첫 실제 발생 때 동작 확인 필요(실제 끄고 켜기는 시험하지 않음 — 이 세션이 그 앱 안에서 돈다) · 근본 수정은 Anthropic 앱 업데이트 | +| BT45-FirstRising | 2026-09-30 01:01 | **맵 이동 로딩 단축 + 로딩 진행도 표시** — 맵 이동 로딩이 긴 원인 · 줄일 방법, 로딩 중 "loading..." 글자와 진행도 막대 (원문은 대화로그) | **완료(09-30 02:10) · PD 확인 대기 · FR `bt/work` `4bca428e62` push** — 원인 [실측 5회]: 옮길 때마다 로비 씬을 통째로 다시 읽어 8.3~8.7초(엔티티 정리 1.9 · 씬 읽기 1.6 · 씬 켜짐 3.3 · 첫 프레임 1.0 · 가림 0.7), 처음 가는 맵은 에디터 전용 서브씬 굽기 4~6초 추가. 조치: 로비 씬을 다시 읽지 않고 맵만 바꿔 끼움(`TVFieldTravel.swapInPlace`) + 로딩 화면(`TVLoadingScreen` — Loading... · 진행 막대 · 퍼센트). 검증 [실측]: 1.4~2.0초(처음 가는 맵 5.6초) · 도착 즉시 몬스터 · 플레이어 자리/능력치/분위기 일치 · 보스전 출입 정상 · 예전 방식도 동작 · 에디터 로그 오류 0 | FR `Assets/BurningTimes/Visual/Scripts/TVFieldTravel.cs` · `TVLoadingScreen.cs` · `TVRuntimeFont.cs` · `공유/대화로그/FirstRising/2026-09-30.md` | — | PC 빌드 미검증(맵 서브씬이 빌드에 없음) · 처음 가는 맵의 굽기 시간은 에디터에서만 남음 | +| BT44-FirstRising | 2026-09-30 01:01 | **최소화(위젯) 모드 — 지형 숨김 + 창 크기 조절 메뉴** — 플레이어 · 몬스터 외 지형은 안 보이게, 하단 UI 위쪽에 마우스를 대면 창 크기 조절 슬라이드 메뉴 (원문은 대화로그) | **완료(09-30 02:10) · PD 확인 대기 · FR `bt/work` `4bca428e62` push** — 원인 [실측]: 게임은 최소화 모드에서 WidgetCulling 레이어만 숨기는데 숲 맵만 그 레이어이고 새 테마 맵은 기본 레이어(숲은 정상 · 묘지는 맵 엔티티 1,565 + 꾸러미 렌더러 427 이 보임). 조치: `TVWidgetScenery`(최소화 모드 동안 맵 엔티티 · 지형 · 꾸러미/전용 필드 그리기 끔 → 나올 때 복구) + `TVWidgetSizeMenu`(띠에 마우스 → 막대 → 놓으면 적용 · 저장) + 게임 코드 `DesktopWidgetManager` 2곳(조작 영역 등록 · 원형 리전 설정 따르기 — 날짜/이유 주석). 검증 [실측]: 묘지 최소화 모드에서 캐릭터 · 몬스터 · HUD 만 보임 → 복구 / 150% 적용 시 위젯 440→660 · 게임 화면 471→706 px · 에디터 로그 오류 0 | FR `TVWidgetScenery.cs` · `TVWidgetSizeMenu.cs` · `Assets/Scripts/Manager/DesktopWidgetManager.cs` · `TVBossField.cs` · `LobbyPlayScene.prefab` | — | PC 빌드 미검증(창 크기 변경 · 투명 창 · 커서 읽기는 빌드 전용 경로) · 에디터에서는 게임 화면 영역을 키우고 줄여 미리 보기 | +| BT43-FirstRising | 2026-09-30 00:34 | **장착 무기에 따른 공격 모션 · 패턴 + 장착 장비의 플레이어 정보 화면 반영** (원문은 대화로그) | **완료(09-30 00:59) · PD 확인 대기 · FR `bt/work` `4bca428e62` push** — 원인 [실측]: 싸우는 캐릭터는 Play 시작 때 한 번만 무기를 들고 게임은 예전 전시용 캐릭터만 갱신 · 활 장식(TVArcherGear)이 무기 종류와 무관하게 게임 무기를 숨김 · 정보 화면은 열 때만 값을 씀. 조치: BT_Visual `TVEquipmentSync` 신설(장비 변경 → 무기 · 능력치 갱신 → 신호) + `TVArcherGear` 는 활일 때만 + `TVCharacterInfoPanel` 은 신호를 받아 다시 씀. 검증 [실측 · Play 2회]: 검 → 공격 상태 Sword0~2 · 기본 피해 34 / 활 → Bow0~3 · 27 / 정보 화면 공격력 37 ↔ 27 즉시 반영 / 에디터 로그 오류 0 · 저장 파일 원복 | FR `Assets/BurningTimes/Visual/Scripts/TVEquipmentSync.cs` · `TVArcherGear.cs` · `TVCharacterInfoPanel.cs` · `Assets/GameAssets/LobbyPlayScene.prefab` · `공유/대화로그/FirstRising/2026-09-30.md` | — | 가방에 검 · 활뿐이라 다른 무기 종류(둔기 · 단검 · 지팡이 · 마법 단검) · 방어구 경로는 미실측 | +| BT42-FirstRising | 2026-09-28 23:27 | **필드에서 레벨업(스킬 카드 3장 선택) 비활성화** — PD ① "지금 내 에디터에서 갑자기 게임이 멈추고, 화면이 블러처리 된 것 같은데 왜 그런지 체크해봐." ② "기존 레벨업 시스템(레벨업 시 스킬 카드 3가지 중 선택)은 특정 컨텐츠에서만 사용 될 예정이야. 따라서, 필드 형태의 맵에서는 해당 레벨업 기능을 비활성화 시켜줘." | **완료(09-29 00:12) · PD 확인 대기 · FR `bt/work` `4bca428e62` push(09-30)** — 원인 [실측]: 레벨업으로 게임의 스킬 선택 창(CommonSkillSelectUI)이 열려 게임 시간 0 + 피사계 심도 흐림이 켜졌는데, 창이 있는 GameCanvas(그리는 순서 0)가 게임 화면 그림 GamePlayCanvas(순서 200) 아래라 보이지도 눌리지도 않았다. 에디터 로그 오류 0 · 에디터 일시정지 아님. 조치: 게임 스위치 `PlayerManager.gemLevelUp`(기본 켜짐) 신설 + `Character.AddGemScore` 가 꺼져 있으면 경험치를 쌓지 않음(게임 코드 2곳 · 날짜/이유 주석) + BT_Visual `TVFieldLevelUpGate`(로비 씬이 켜져 있는 동안 끔 · 내려가면 되돌림). 검증 [실측 · Play 1회]: 필드 경험치 600 투입 → 0 유지 · 창 없음 · 시간 1 / 스위치를 되돌리면 원래대로 창 · 시간 0 · 흐림 / 일일 도전(스테이지)에서는 레벨업 정상 / 로비 복귀 뒤 다시 꺼짐 / 에디터 로그 오류 0 · 저장 파일 원복 | FR 작업 트리(미커밋): `Assets/BurningTimes/Visual/Scripts/TVFieldLevelUpGate.cs` · `Assets/Scripts/Manager/PlayerManager.cs` · `Assets/Scripts/Fx/Character/Character.cs` · `Assets/GameAssets/LobbyPlayScene.prefab` · `공유/대화로그/FirstRising/2026-09-29.md` | — | 보스전 전용 필드도 로비 씬이라 함께 꺼짐(PD 확인 필요) · 같은 GameCanvas 의 다른 창(사망 결과 · 스킬북 · 게임 메뉴)은 여전히 게임 화면 아래에 깔림(미수정) · 커밋은 PD 지시 시 | | BT41-FirstRising | 2026-09-28 19:0x | **옆 창 독립 — 좌 · 우 함께 보이기** — PD "각각의 UI는 개별적으로 열 수 있어야하고, 개별적으로 닫을 수도 있는 독립적인 구조여야 해 좌, 우측 영역의 UI가 함께 보여져야하는데 지금은 한쪽이 켜지면 한쪽이 닫히는 현상이 있는거 같아. 체크해보고 수정해줘." | **완료(09-28) · PD 확인("의도한 것이 맞아") · FR `bt/work` `2d510d24c4` push** — 원인 [실측 + 코드]: ① 컨텐츠 ↔ 성장 · 가방을 서로 닫는 코드 ② 장비 · 컨텐츠 · 성장(강화)이 게임의 로비 페이지 전환(한 번에 한 페이지)을 빌려 씀 ③ 성장(강화)과 컨텐츠가 같은 페이지를 통째로 옮겨 씀. 조치: 자리(왼쪽 · 오른쪽)별 관리(`TVSideSlots`) · 창마다 자기 페이지만 켜고 끔 · 성장(강화)은 진화 창만 빌려 옴. 좌 · 우 조합 17 단계 통과 · 에디터 로그 오류 0 [실측] | FR `Assets/BurningTimes/Visual/Scripts/TVSideSlots.cs` · 옆 창 5종 · `공유/기획/FirstRising/UI/2026-09-28_FR_화면구성_세영역_필드이동.md` §4 | — | 같은 자리의 창 둘은 함께 열리지 않는다(겹침) — PD 확인 | | BT40-FirstRising | 2026-09-28 17:20 | **게임 화면 기본 크기 · 세 영역** — PD "매번 에디터 화면이 너무 작아서 불편해. → 따라서 에디터 기준 화면 크기를 2배로 확대한 상태가 기본 크기면 좋겠어. (※추후 옵션에서 기본 화면 크기를 조절할 수 있도록 할 예정이야.)" · (보완) "옆에 발생되는 창도 게임 화면과 동일한 크기로 … 왼쪽 중앙 오른쪽 3 영역의 좌/우길이의 총합이 모니터 화면이 되도록" · (보완) "게임 뷰의 크기를 조절할 경우 잘리는 화면이 존재해. scale 1.0 기준으로 화면을 조절하면 맞춰서 유동적으로 그려지게 해줘" | **완료(09-28) · PD 확인("의도한 것이 맞아") · PC 빌드 확인은 PD 보류 · FR `bt/work` `2d510d24c4` push** — 1차(고정 크기 2배)는 보완 지시로 폐기. 최종: 세 영역의 배치(2200×1280)가 화면에 꽉 차게 배율을 잡음(`TVScreenScale`) · 옆 창 틈/여백 10 · 10 으로 세 영역 같은 크기 · 게임 화면 텍스처를 그려지는 픽셀 수로 · PC 창은 큰 모니터에서 키움(게임 원본 2곳 · 날짜/이유 주석). 게임 뷰 크기 8종에서 세 영역 모두 화면 안 [실측]. PD 에디터 게임 뷰를 자유 비율 · 제 해상도로 맞춤 | FR `TVScreenScale.cs` · `ViewportLayoutController.cs` · `DesktopWidgetManager.cs` · `Editor/TVLobbyScreenSetup.cs` · `공유/기획/FirstRising/UI/2026-09-28_FR_화면구성_세영역_필드이동.md` §3 | — | PC 빌드로 창 크기 확인(PD 보류 09-28) · Entry/로딩/스테이지 화면 배율은 게임 원래대로 | | BT39-FirstRising | 2026-09-28 17:00 | **PC 화면 영역 질문 · 테스트 맵/보스전 화면 구성 통일** — PD "1) 에디터에서 Entry 씬 실행 시 보여지는 화면이 실제 PC에서 게임 실행 시 보여지는 영역이 되는거야? (현재 내 에디터 화면의 검은 부분이 PC 화면이고, 게임이 보여지는 영역은 게임 화면이 떠있는 영역인가에 대한 질문이야.) 2) 테스트 맵(숲맵을 제외한 네가 새로 만든 맵)에 진입 시 캐릭터 상단에 떠있는 창은 제외해줘. (최초 Entry 씬 진입 시 보여지는 화면이 맵을 바꾸거나 보스 전에서도 동일하게 보여져야 해. 따라서 하단의 HP 정보가 보이는 메뉴 등은 맵이 바뀌어도 보여져야 한다는 의미야. 3) 단, 게임 화면 최하단의 "성장, 장비, 특성, 컨텐츠, 길드, 포탈" 메뉴는 보스전에서는 보여지지 않아야 해." | **완료(09-28) · PD 확인("의도한 것이 맞아") · FR `bt/work` `2d510d24c4` push** — ① 답: 에디터의 검은 부분은 PC 화면이 아니라 게임 창의 빈 자리(빌드에서 투명) [코드 + 실측] ② 필드 이동을 "로비 씬은 그대로 · 맵만 바꿔 끼우기"로 바꿈(`TVFieldTravel` · 필드 꾸러미 6종) → 로비 + 맵 6종 같은 화면 구성 · 스테이지용 화면과 머리 위 HP 막대 가림 ③ 보스전 중 하단 메뉴 숨김(7곳). 전체 순회 4회 에디터 로그 오류 0 [실측] | FR `TVFieldTravel.cs` · `TVBottomMenu.cs` · `TVBossArenaUI.cs` · `Editor/Maps/MapFieldKitBuilder.cs` · `Maps/Resources/FieldKits/` · `공유/기획/FirstRising/UI/2026-09-28_FR_화면구성_세영역_필드이동.md` §1~2 | — | 빌드에서의 필드 이동은 맵 서브씬을 빌드에 넣어야 함(미반영) | diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/README.md b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/README.md new file mode 100644 index 0000000..b144f44 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/README.md @@ -0,0 +1,10 @@ +# bt_evals_0930 — 2026-09-24 ~ 09-30 세션이 쓴 Unity eval · 검증 스크립트 사본 + +원래 자리는 `%TEMP%\bt_evals\`(스크립트가 그 경로를 기준으로 돈다). 다시 쓰려면 이 폴더를 `%TEMP%\bt_evals\` 로 복사한다. +- `run_*.ps1` — 검증 Play 한 벌(UTF-8 BOM 저장 · `powershell -NoProfile -ExecutionPolicy Bypass -File`). 결과는 같은 폴더의 `run_*.log` + - run_equip_verify · run_attack_verify(BT43 장비 · 공격 모션) · run_levelup_verify(BT42) · run_bt44_probe(실측) · run_bt44_verify(BT44 · BT45 검증) · run_wsm_test(창 크기 메뉴) +- `travel_hook.cs` / `travel_unhook.cs` — 필드 이동 구간별 시간을 `%TEMP%\bt_evals\travel_timeline.log` 에 적는 훅(Play 마다 설치 · 끝나면 반드시 unhook) +- `visible_probe.cs` · `widget_probe.cs` · `wsm_state.cs` · `wsm_diag.cs` — 최소화 모드에서 보이는 것 · 창 크기 메뉴 상태 +- `wsm_pointer.template.cs` — 가짜 마우스 입력(입력 갱신 직후 훅 · `off` 로 해제) · `boss_roundtrip.template.cs` — 보스전 입장/상태/퇴장. `__TARGET__` 같은 자리는 run 스크립트가 바꿔 넣는다 +- `attach_*.cs` — 프리팹(LobbyPlayScene)의 BT_Visual 에 구성 요소 부착 +- `editor_ready.cs` — 재컴파일 · Play 전 상태 확인(단독 명령으로) · `poll.cs` — 로비 준비 확인 diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_bt44.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_bt44.cs new file mode 100644 index 0000000..0d22922 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_bt44.cs @@ -0,0 +1,37 @@ +// BT44 · BT45 — BT_Visual 에 새 구성 요소 둘을 붙이고, 글자체 참조를 하단 메뉴(TVBottomMenu)의 것으로 채운다 +var path = "Assets/GameAssets/LobbyPlayScene.prefab"; +var root = UnityEditor.PrefabUtility.LoadPrefabContents(path); +var sb = new System.Text.StringBuilder(); +try { + UnityEngine.Transform bt = null; + foreach (var t in root.GetComponentsInChildren(true)) if (t.name == "BT_Visual") { bt = t; break; } + if (bt == null) return "BT_Visual not found"; + + UnityEngine.Object font = null; + var menu = bt.GetComponent(); + if (menu != null) font = new UnityEditor.SerializedObject(menu).FindProperty("font").objectReferenceValue; + sb.Append("font=").Append(font != null ? font.name : "null").Append(" | "); + + var scenery = bt.GetComponent(); + if (scenery == null) { bt.gameObject.AddComponent(); sb.Append("added TVWidgetScenery | "); } else sb.Append("TVWidgetScenery exists | "); + + var size = bt.GetComponent(); + if (size == null) { size = bt.gameObject.AddComponent(); sb.Append("added TVWidgetSizeMenu | "); } else sb.Append("TVWidgetSizeMenu exists | "); + var sizeSo = new UnityEditor.SerializedObject(size); + sizeSo.FindProperty("font").objectReferenceValue = font; + sizeSo.ApplyModifiedPropertiesWithoutUndo(); + + var travel = bt.GetComponent(); + if (travel != null) + { + var so = new UnityEditor.SerializedObject(travel); + so.FindProperty("loadingFont").objectReferenceValue = font; + so.ApplyModifiedPropertiesWithoutUndo(); + sb.Append("TVFieldTravel.loadingFont set swapInPlace=").Append(so.FindProperty("swapInPlace").boolValue).Append(" | "); + } + else sb.Append("TVFieldTravel missing | "); + + UnityEditor.PrefabUtility.SaveAsPrefabAsset(root, path); + sb.Append("saved"); + return sb.ToString(); +} finally { UnityEditor.PrefabUtility.UnloadPrefabContents(root); } diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_equipsync.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_equipsync.cs new file mode 100644 index 0000000..b32fdac --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_equipsync.cs @@ -0,0 +1,11 @@ +var path = "Assets/GameAssets/LobbyPlayScene.prefab"; +var root = UnityEditor.PrefabUtility.LoadPrefabContents(path); +try { + UnityEngine.Transform bt = null; + foreach (var t in root.GetComponentsInChildren(true)) if (t.name == "BT_Visual") { bt = t; break; } + if (bt == null) return "BT_Visual not found"; + var existing = bt.GetComponent(); + if (existing == null) bt.gameObject.AddComponent(); + UnityEditor.PrefabUtility.SaveAsPrefabAsset(root, path); + return existing == null ? "added TVEquipmentSync to BT_Visual" : "already attached"; +} finally { UnityEditor.PrefabUtility.UnloadPrefabContents(root); } \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_gate.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_gate.cs new file mode 100644 index 0000000..061c81b --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_gate.cs @@ -0,0 +1,11 @@ +var path = "Assets/GameAssets/LobbyPlayScene.prefab"; +var root = UnityEditor.PrefabUtility.LoadPrefabContents(path); +try { + UnityEngine.Transform bt = null; + foreach (var t in root.GetComponentsInChildren(true)) if (t.name == "BT_Visual") { bt = t; break; } + if (bt == null) return "BT_Visual not found"; + var existing = bt.GetComponent(); + if (existing == null) bt.gameObject.AddComponent(); + UnityEditor.PrefabUtility.SaveAsPrefabAsset(root, path); + return existing == null ? "added TVFieldLevelUpGate to BT_Visual" : "already attached"; +} finally { UnityEditor.PrefabUtility.UnloadPrefabContents(root); } \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_suppressor.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_suppressor.cs new file mode 100644 index 0000000..0a69cc5 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/attach_suppressor.cs @@ -0,0 +1,11 @@ +var path = "Assets/GameAssets/LobbyPlayScene.prefab"; +var root = UnityEditor.PrefabUtility.LoadPrefabContents(path); +try { + UnityEngine.Transform bt = null; + foreach (var t in root.GetComponentsInChildren(true)) if (t.name == "BT_Visual") { bt = t; break; } + if (bt == null) return "BT_Visual not found"; + var existing = bt.GetComponent(); + if (existing == null) bt.gameObject.AddComponent(); + UnityEditor.PrefabUtility.SaveAsPrefabAsset(root, path); + return existing == null ? "added TVTutorialSuppressor to BT_Visual" : "already attached"; +} finally { UnityEditor.PrefabUtility.UnloadPrefabContents(root); } \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/boss_roundtrip.template.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/boss_roundtrip.template.cs new file mode 100644 index 0000000..3abf7da --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/boss_roundtrip.template.cs @@ -0,0 +1,13 @@ +// 시험용 — 보스전 모드: STEP enter | state | exit +var a = BurningTimes.Boss.TVBossArena.Instance; +if (a == null) return "no TVBossArena"; +string step = "__STEP__"; +if (step == "enter") return "enter=" + a.Enter(0) + " state=" + a.state + " inField=" + a.inField; +if (step == "exit") { a.Exit(); return "exit state=" + a.state + " hidden=" + a.hiddenCount; } +var sb = new System.Text.StringBuilder(); +sb.Append("state=").Append(a.state).Append(" inField=").Append(a.inField).Append(" hidden=").Append(a.hiddenCount); +var d = BurningTimes.Boss.TVBossDirector.Instance; +if (d != null && d.TryGetHud(out var hud)) sb.Append(" bossAlive=").Append(hud.aliveCount).Append(" phase=").Append(hud.phase); +var f = BurningTimes.Boss.TVBossField.Instance; +sb.Append(" field=").Append(f != null && f.isOpen ? f.currentKey : "closed"); +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/check_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/check_state.cs new file mode 100644 index 0000000..7e80f5c --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/check_state.cs @@ -0,0 +1,9 @@ +var sb = new System.Text.StringBuilder(); +sb.Append("ts=").Append(UnityEngine.Time.timeScale).Append(" audioPause=").Append(UnityEngine.AudioListener.pause); +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +if (Fx.LobbyScene.HasInstance()) { var login = typeof(Fx.LobbyScene).GetField("m_Login", nf).GetValue(Fx.LobbyScene.Instance) as UnityEngine.GameObject; sb.Append(" login=").Append(login != null && login.activeSelf); } +foreach (var tp in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) sb.Append(" tut=").Append(tp.transform.parent != null && tp.transform.parent.parent != null ? tp.transform.parent.parent.name : "?").Append(tp.gameObject.activeSelf ? "(on)" : "(off)"); +var sup = UnityEngine.Object.FindFirstObjectByType(); sb.Append(" suppressor=").Append(sup != null ? ("on suppressed=" + sup.SuppressedCount) : "none"); +sb.Append(" user.gameTutorialChapter=").Append(Fx.PlayerManager.HasInstance() && Fx.PlayerManager.Instance.player != null ? Fx.PlayerManager.Instance.player.user.gameTutorialChapter.ToString() : "?"); +sb.Append(" StartGameType=").Append(Fx.LoadingScene.StartGameType); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_all.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_all.cs new file mode 100644 index 0000000..614c911 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_all.cs @@ -0,0 +1,8 @@ +var sb = new System.Text.StringBuilder(); +var inv = UnityEngine.Object.FindFirstObjectByType(); if (inv != null && inv.isOpen) { inv.Close(); sb.Append("inv "); } +var eq = UnityEngine.Object.FindFirstObjectByType(); if (eq != null && eq.isOpen) { eq.Close(); sb.Append("eq "); } +var gr = UnityEngine.Object.FindFirstObjectByType(); if (gr != null && gr.isOpen) { gr.Close(); sb.Append("growth "); } +var tr = UnityEngine.Object.FindFirstObjectByType(); if (tr != null && tr.isOpen) { tr.Close(); sb.Append("trait "); } +var co = UnityEngine.Object.FindFirstObjectByType(); if (co != null && co.isOpen) { co.Close(); sb.Append("contents "); } +var ci = UnityEngine.Object.FindFirstObjectByType(); if (ci != null) { var prop = ci.GetType().GetProperty("isOpen"); var m = ci.GetType().GetMethod("Close"); if (prop != null && m != null && (bool)prop.GetValue(ci)) { m.Invoke(ci, null); sb.Append("charinfo "); } } +return "closed: " + sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_info.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_info.cs new file mode 100644 index 0000000..bc0106b --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/close_info.cs @@ -0,0 +1,3 @@ +var ui = UnityEngine.Object.FindFirstObjectByType(); if (ui == null) return "no GamePlayUI"; +var page = typeof(Fx.GamePlayUI).GetField("m_CharacterPageUI", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(ui) as Fx.PageUI; +if (page == null) return "no page"; if (page.gameObject.activeInHierarchy) page.Show(false); return "player info closed active=" + page.gameObject.activeInHierarchy; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur1.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur1.cs new file mode 100644 index 0000000..e69e6c2 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur1.cs @@ -0,0 +1,24 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +sb.Append("isPlaying=").Append(UnityEditor.EditorApplication.isPlaying).Append(" isPaused=").Append(UnityEditor.EditorApplication.isPaused); +sb.Append(" timeScale=").Append(UnityEngine.Time.timeScale).Append(" time=").Append(UnityEngine.Time.time.ToString("F1")).Append(" unscaled=").Append(UnityEngine.Time.unscaledTime.ToString("F1")).Append(" frame=").Append(UnityEngine.Time.frameCount).Append(" fixedDt=").Append(UnityEngine.Time.fixedDeltaTime); +sb.Append(" scene=").Append(UnityEngine.SceneManagement.SceneManager.GetActiveScene().name).Append(" targetFps=").Append(UnityEngine.Application.targetFrameRate); +sb.Append(" quality=").Append(UnityEngine.QualitySettings.GetQualityLevel()).Append(":").Append(UnityEngine.QualitySettings.names[UnityEngine.QualitySettings.GetQualityLevel()]); +var urp = UnityEngine.Rendering.Universal.UniversalRenderPipeline.asset; sb.Append(" urp=").Append(urp != null ? urp.name + " renderScale=" + urp.renderScale + " upscale=" + urp.upscalingFilter : "null"); +sb.Append(" shaderLOD=").Append(UnityEngine.Shader.globalMaximumLOD).Append(" audioPause=").Append(UnityEngine.AudioListener.pause); +foreach (var p in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) sb.Append("\n POPUP ").Append(p.GetType().Name).Append(" pause=").Append(p.pauseGame).Append(" path=").Append(path(p.transform)); +foreach (var t in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) sb.Append("\n TUTORIAL ").Append(path(t.transform)); +foreach (var v in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) { + sb.Append("\n VOLUME ").Append(path(v.transform)).Append(" enabled=").Append(v.isActiveAndEnabled).Append(" w=").Append(v.weight).Append(" global=").Append(v.isGlobal).Append(" prio=").Append(v.priority); + var prof = v.sharedProfile; if (prof == null) continue; sb.Append(" profile=").Append(prof.name); + foreach (var c in prof.components) { if (c == null || !c.active) continue; sb.Append(" [").Append(c.GetType().Name); + var dof = c as UnityEngine.Rendering.Universal.DepthOfField; if (dof != null) sb.Append(" mode=").Append(dof.mode.value).Append(" gStart=").Append(dof.gaussianStart.value).Append(" gEnd=").Append(dof.gaussianEnd.value).Append(" gMax=").Append(dof.gaussianMaxRadius.value).Append(" focus=").Append(dof.focusDistance.value).Append(" apert=").Append(dof.aperture.value).Append(" focal=").Append(dof.focalLength.value); + sb.Append("]"); } +} +foreach (var cam in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) { var d = cam.GetComponent(); sb.Append("\n CAMERA ").Append(path(cam.transform)).Append(" enabled=").Append(cam.isActiveAndEnabled).Append(" depth=").Append(cam.depth).Append(" rect=").Append(cam.rect).Append(" target=").Append(cam.targetTexture != null ? cam.targetTexture.name + " " + cam.targetTexture.width + "x" + cam.targetTexture.height : "screen").Append(" pp=").Append(d != null ? d.renderPostProcessing.ToString() : "?").Append(" type=").Append(d != null ? d.renderType.ToString() : "?").Append(" aa=").Append(d != null ? d.antialiasing.ToString() : "?"); } +sb.Append("\n screen=").Append(UnityEngine.Screen.width).Append("x").Append(UnityEngine.Screen.height); +if (Fx.DesktopWidgetManager.HasInstance()) sb.Append(" widget=").Append(Fx.DesktopWidgetManager.Instance.state); +if (Fx.WorldManager.HasInstance()) sb.Append(" wmFinish=").Append(Fx.WorldManager.Instance.finishGameProgress); +sb.Append(" player=").Append(Fx.Player.Instance != null); +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; sb.Append(" world=").Append(w != null && w.IsCreated ? "ok entities=" + w.EntityManager.UniversalQuery.CalculateEntityCount() : "none"); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur2.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur2.cs new file mode 100644 index 0000000..b674697 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur2.cs @@ -0,0 +1,25 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +var pop = UnityEngine.Object.FindFirstObjectByType(); +if (pop == null) return "no active CommonSkillSelectUI"; +var rt = (UnityEngine.RectTransform)pop.transform; +sb.Append("popup activeInH=").Append(pop.gameObject.activeInHierarchy).Append(" rect=").Append(rt.rect).Append(" lossyScale=").Append(rt.lossyScale).Append(" pos=").Append(rt.position); +var canvas = pop.GetComponentInParent(); +var root = canvas != null ? canvas.rootCanvas : null; +if (root != null) { sb.Append("\n rootCanvas=").Append(path(root.transform)).Append(" enabled=").Append(root.enabled).Append(" mode=").Append(root.renderMode).Append(" cam=").Append(root.worldCamera != null ? root.worldCamera.name : "null").Append(" order=").Append(root.sortingOrder).Append(" display=").Append(root.targetDisplay).Append(" scaleFactor=").Append(root.scaleFactor).Append(" planeDist=").Append(root.planeDistance); + var rrt = (UnityEngine.RectTransform)root.transform; sb.Append(" rootRect=").Append(rrt.rect).Append(" rootScale=").Append(rrt.lossyScale).Append(" rootPos=").Append(rrt.position); } +if (canvas != null && canvas != root) sb.Append("\n nearestCanvas=").Append(path(canvas.transform)).Append(" enabled=").Append(canvas.enabled).Append(" overrideSorting=").Append(canvas.overrideSorting).Append(" order=").Append(canvas.sortingOrder); +var corners = new UnityEngine.Vector3[4]; rt.GetWorldCorners(corners); +var cam = root != null && root.renderMode != UnityEngine.RenderMode.ScreenSpaceOverlay ? root.worldCamera : null; +sb.Append("\n screenCorners="); +for (int i = 0; i < 4; i++) { var sp = UnityEngine.RectTransformUtility.WorldToScreenPoint(cam, corners[i]); sb.Append("(").Append(sp.x.ToString("F0")).Append(",").Append(sp.y.ToString("F0")).Append(") "); } +sb.Append(" screen=").Append(UnityEngine.Screen.width).Append("x").Append(UnityEngine.Screen.height); +for (var t = pop.transform; t != null; t = t.parent) { var cg = t.GetComponent(); if (cg != null) sb.Append("\n CanvasGroup@").Append(path(t)).Append(" alpha=").Append(cg.alpha).Append(" interactable=").Append(cg.interactable).Append(" blocks=").Append(cg.blocksRaycasts).Append(" ignoreParent=").Append(cg.ignoreParentGroups); } +sb.Append("\n children:"); +foreach (UnityEngine.Transform c in pop.transform) sb.Append(" ").Append(c.name).Append(c.gameObject.activeSelf ? "(on)" : "(off)"); +int graphics = 0, visibleGraphics = 0; foreach (var g in pop.GetComponentsInChildren(false)) { graphics++; if (g.enabled && g.canvasRenderer.GetAlpha() > 0.01f && g.color.a > 0.01f && !g.canvasRenderer.cull) visibleGraphics++; } +sb.Append("\n graphics=").Append(graphics).Append(" visible(not culled)=").Append(visibleGraphics); +int buttons = 0; foreach (var b in pop.GetComponentsInChildren(false)) { buttons++; if (buttons <= 6) sb.Append("\n button ").Append(path(b.transform).Replace(path(pop.transform), "~")).Append(" interactable=").Append(b.IsInteractable()); } +foreach (var gr in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) if (root != null && gr.transform == root.transform) sb.Append("\n raycaster on root enabled=").Append(gr.enabled); +var es = UnityEngine.EventSystems.EventSystem.current; sb.Append("\n eventSystem.current=").Append(es != null ? path(es.transform) : "null"); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur3.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur3.cs new file mode 100644 index 0000000..0356be4 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur3.cs @@ -0,0 +1,25 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +float sw = UnityEngine.Screen.width, sh = UnityEngine.Screen.height; +var canvases = new System.Collections.Generic.List(); +foreach (var c in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) if (c.isRootCanvas) canvases.Add(c); +canvases.Sort((a, b) => a.sortingOrder.CompareTo(b.sortingOrder)); +foreach (var c in canvases) { + sb.Append("\nCANVAS order=").Append(c.sortingOrder).Append(" mode=").Append(c.renderMode).Append(" enabled=").Append(c.enabled).Append(" sf=").Append(c.scaleFactor.ToString("F2")).Append(" px=").Append(c.pixelRect.width.ToString("F0")).Append("x").Append(c.pixelRect.height.ToString("F0")).Append(" ").Append(path(c.transform)); + int big = 0; + foreach (var g in c.GetComponentsInChildren(false)) { + if (!g.enabled || g.canvasRenderer.cull || g.color.a < 0.05f || g.canvasRenderer.GetInheritedAlpha() < 0.05f) continue; + var corners = new UnityEngine.Vector3[4]; g.rectTransform.GetWorldCorners(corners); + float w = UnityEngine.Mathf.Abs(corners[2].x - corners[0].x), h = UnityEngine.Mathf.Abs(corners[2].y - corners[0].y); + if (w >= 400f && h >= 700f && big < 6) { big++; var raw = g as UnityEngine.UI.RawImage; sb.Append("\n BIG ").Append(g.GetType().Name).Append(" ").Append(w.ToString("F0")).Append("x").Append(h.ToString("F0")).Append(" at(").Append(corners[0].x.ToString("F0")).Append(",").Append(corners[0].y.ToString("F0")).Append(") a=").Append(g.color.a.ToString("F2")).Append(" inhA=").Append(g.canvasRenderer.GetInheritedAlpha().ToString("F2")).Append(raw != null && raw.texture != null ? " tex=" + raw.texture.name : "").Append(" ").Append(path(g.transform).Replace(path(c.transform), "~")); } + } +} +var pop = UnityEngine.Object.FindFirstObjectByType(); +if (pop != null) { + sb.Append("\nPOPUP children:"); + foreach (UnityEngine.Transform ch in pop.transform) { var cg = ch.GetComponent(); var an = ch.GetComponent(); var rt = (UnityEngine.RectTransform)ch; var corners = new UnityEngine.Vector3[4]; rt.GetWorldCorners(corners); + sb.Append("\n ").Append(ch.name).Append(" scale=").Append(ch.localScale.x.ToString("F2")).Append(" box=(").Append(corners[0].x.ToString("F0")).Append(",").Append(corners[0].y.ToString("F0")).Append(")-(").Append(corners[2].x.ToString("F0")).Append(",").Append(corners[2].y.ToString("F0")).Append(")").Append(cg != null ? " cgAlpha=" + cg.alpha.ToString("F2") : "").Append(an != null ? " animator=" + an.enabled + "/" + an.updateMode : ""); + var g0 = ch.GetComponentInChildren(false); if (g0 != null) sb.Append(" firstGraphic=").Append(g0.GetType().Name).Append(" a=").Append(g0.color.a.ToString("F2")).Append(" inhA=").Append(g0.canvasRenderer.GetInheritedAlpha().ToString("F2")).Append(" cull=").Append(g0.canvasRenderer.cull); } + var pan = pop.GetComponent(); sb.Append("\n popup animator=").Append(pan != null ? pan.enabled + "/" + pan.updateMode : "none"); +} +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur4.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur4.cs new file mode 100644 index 0000000..025ddb8 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/diag_blur4.cs @@ -0,0 +1,17 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +var pop = UnityEngine.Object.FindFirstObjectByType(); +sb.Append("popupActive=").Append(pop != null).Append(" timeScale=").Append(UnityEngine.Time.timeScale).Append(" pausedFor=").Append((UnityEngine.Time.unscaledTime - UnityEngine.Time.time).ToString("F0")).Append("s(누적)"); +var es = UnityEngine.EventSystems.EventSystem.current; +if (es != null && pop != null) { + var card = pop.transform.Find("CommonSkillSelectPopup1"); var rt = (UnityEngine.RectTransform)card; var c = new UnityEngine.Vector3[4]; rt.GetWorldCorners(c); + var center = new UnityEngine.Vector2((c[0].x + c[2].x) * 0.5f, (c[0].y + c[2].y) * 0.5f); + var ped = new UnityEngine.EventSystems.PointerEventData(es) { position = center }; + var hits = new System.Collections.Generic.List(); es.RaycastAll(ped, hits); + sb.Append("\n raycast at card center ").Append(center).Append(" hits=").Append(hits.Count); + for (int i = 0; i < hits.Count && i < 5; i++) sb.Append("\n #").Append(i).Append(" ").Append(path(hits[i].gameObject.transform)).Append(" order=").Append(hits[i].sortingOrder).Append(" depth=").Append(hits[i].depth); +} +if (Fx.Player.Instance != null) { var p = Fx.Player.Instance; var t = p.GetType(); var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.FlattenHierarchy; + foreach (var n in new[]{"m_GemLevel","m_CurrentExp","m_MaxExp","m_VisibleExp"}) { System.Reflection.FieldInfo f = null; for (var tt = t; tt != null && f == null; tt = tt.BaseType) f = tt.GetField(n, nf); sb.Append(" ").Append(n).Append("=").Append(f != null ? f.GetValue(p) : "?"); } } +var ft = UnityEngine.Object.FindFirstObjectByType(); sb.Append("\n fieldTravel=").Append(ft != null); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/ecs_weapon.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/ecs_weapon.cs new file mode 100644 index 0000000..ba73cf1 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/ecs_weapon.cs @@ -0,0 +1,11 @@ +var sb = new System.Text.StringBuilder(); +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; if (w == null || !w.IsCreated) return "no world"; +var em = w.EntityManager; +var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly()); +using (var ents = q.ToEntityArray(Unity.Collections.Allocator.Temp)) { foreach (var e in ents) { var a = em.GetComponentData(e); sb.Append("ECS weaponId=").Append(a.weaponId).Append(" level=").Append(a.weaponLevel).Append(" needUpdate=").Append(a.needUpdate); if (em.Exists(a.skillBufferEntity) && em.HasBuffer(a.skillBufferEntity)) { var buf = em.GetBuffer(a.skillBufferEntity, true); sb.Append(" skillBuf=").Append(buf.Length); for (int i = 0; i < buf.Length && i < 3; i++) sb.Append(" [").Append(i).Append(" skillId=").Append(buf[i].skillId).Append(" index=").Append(buf[i].index).Append(" baseDamage=").Append(buf[i].baseDamage).Append(" abilityRate=").Append(buf[i].abilityDamageRate.ToString("F2")).Append(" range=").Append(buf[i].targetingRange.ToString("F1")).Append("]"); } } } +q.Dispose(); +var p = Fx.Player.Instance; +if (p != null && p.characterEntity != Unity.Entities.Entity.Null && em.Exists(p.characterEntity)) { if (em.HasComponent(p.characterEntity)) { var h = em.GetComponentData(p.characterEntity); sb.Append(" | entityHealth=").Append(h.health).Append("/").Append(h.healthMax); } if (em.HasComponent(p.characterEntity)) sb.Append(" finalAttack=").Append(em.GetComponentData(p.characterEntity).finalAttack); } +if (p != null) sb.Append(" | managed atk=").Append(p.CalcFinalAttack()).Append(" hpMax=").Append(p.CalcFinalHealthMax()).Append(" weaponId=").Append(p.attachedWeaponId).Append(" skillElems=").Append(p.weaponSkillElements.Count); +var sync = UnityEngine.Object.FindFirstObjectByType(); sb.Append(" | sync=").Append(sync != null ? ("count=" + sync.syncCount + " lastWeaponChanged=" + sync.lastWeaponChanged) : "none"); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/editor_ready.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/editor_ready.cs new file mode 100644 index 0000000..4499423 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/editor_ready.cs @@ -0,0 +1 @@ +return "compiling=" + UnityEditor.EditorApplication.isCompiling + " updating=" + UnityEditor.EditorApplication.isUpdating + " playing=" + UnityEditor.EditorApplication.isPlaying; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/equip_ui_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/equip_ui_state.cs new file mode 100644 index 0000000..ca7d123 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/equip_ui_state.cs @@ -0,0 +1,20 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance; +if (!UnityEditor.EditorApplication.isPlaying) return "not playing"; +System.Action dump = (page) => { + sb.Append("\n").Append(page.GetType().Name).Append(" @").Append(path(page.transform)).Append(" activeInH=").Append(page.gameObject.activeInHierarchy); + var f = page.GetType().GetField("m_MountItemIconDatas", nf); var list = f != null ? f.GetValue(page) as System.Collections.IList : null; + if (list != null) for (int i = 0; i < list.Count; i++) { var d = list[i] as Fx.ItemIconData; if (d == null) { sb.Append(" [").Append(i).Append(" null]"); continue; } sb.Append(" [").Append(i).Append(" ").Append(d.name).Append(d.gameObject.activeSelf ? " on" : " off").Append(" item=").Append(d.itemJson != null ? d.itemJson.Index.ToString() : "-").Append("]"); } + var at = page.GetType().GetField("m_AttackText", nf)?.GetValue(page) as TMPro.TMP_Text; var ht = page.GetType().GetField("m_HealthText", nf)?.GetValue(page) as TMPro.TMP_Text; + sb.Append(" gameAtkText=").Append(at != null ? at.text : "?").Append(" gameHpText=").Append(ht != null ? ht.text : "?"); +}; +foreach (var p in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) dump(p); +foreach (var p in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) dump(p); +foreach (var p in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) dump(p); +var pl = Fx.Player.Instance; if (pl != null) sb.Append("\n Player.Instance atk=").Append(pl.CalcFinalAttack()).Append(" hp=").Append(pl.CalcFinalHealthMax()); +var lp = Fx.LobbyManager.HasInstance() ? Fx.LobbyManager.Instance.lobbyPlayer : null; if (lp != null) sb.Append(" | lobbyPlayer atk=").Append(lp.CalcFinalAttack()).Append(" hp=").Append(lp.CalcFinalHealthMax()); +foreach (var t in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) { if (t.name == "Value" && t.transform.parent != null && t.transform.parent.parent != null && t.transform.parent.parent.name == "TVInfoPower") sb.Append("\n BT info ").Append(t.transform.parent.name).Append(" = ").Append(t.text).Append(" activeInH=").Append(t.gameObject.activeInHierarchy); } +var mp = Fx.MainPageUI.HasInstance() ? Fx.MainPageUI.Instance : null; sb.Append("\n MainPageUI=").Append(mp != null); +foreach (var c in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) { var n = c.GetType().Name; if (n == "TVEquipmentPanel" || n == "TVCharacterInfoPanel") { var pr = c.GetType().GetProperty("isOpen"); sb.Append("\n ").Append(n).Append(" enabled=").Append(c.isActiveAndEnabled).Append(pr != null ? " isOpen=" + pr.GetValue(c) : ""); } } +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/exit_menu.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/exit_menu.cs new file mode 100644 index 0000000..2b3048c --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/exit_menu.cs @@ -0,0 +1,8 @@ +var ui = Fx.UIManager.Instance; if (ui == null) return "no UIManager"; +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +var menu = typeof(Fx.UIManager).GetField("m_GameMenuPopup", nf).GetValue(ui) as Fx.GameMenuPopup; if (menu == null) return "no menu"; +menu.Show(true); +var homeBtn = typeof(Fx.GameMenuPopup).GetField("m_HomeButton", nf).GetValue(menu) as UnityEngine.UI.Button; homeBtn.onClick.Invoke(); +var home = typeof(Fx.GameMenuPopup).GetField("m_GotoHomePopup", nf).GetValue(menu) as Fx.GotoHomePopup; +var goBtn = typeof(Fx.GotoHomePopup).GetField("m_GotoHomeButton", nf).GetValue(home) as UnityEngine.UI.Button; goBtn.onClick.Invoke(); +return "menu→home clicked frame=" + UnityEngine.Time.frameCount + " ts=" + UnityEngine.Time.timeScale; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/flag_on.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/flag_on.cs new file mode 100644 index 0000000..75529f3 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/flag_on.cs @@ -0,0 +1 @@ +BurningTimes.Visual.TVMonsterProxies.experimentSkipUnregister = true; return "skipUnregister=" + BurningTimes.Visual.TVMonsterProxies.experimentSkipUnregister; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/info_values.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/info_values.cs new file mode 100644 index 0000000..e4e0482 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/info_values.cs @@ -0,0 +1,6 @@ +var panel = UnityEngine.Object.FindFirstObjectByType(); if (panel == null) return "no panel"; +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +var layout = typeof(BurningTimes.Visual.TVCharacterInfoPanel).GetField("layout", nf).GetValue(panel); +var lt = layout.GetType(); var atk = lt.GetField("m_Attack", nf).GetValue(layout) as TMPro.TMP_Text; var hp = lt.GetField("m_Health", nf).GetValue(layout) as TMPro.TMP_Text; var lv = lt.GetField("m_Level", nf).GetValue(layout) as TMPro.TMP_Text; +var p = Fx.Player.Instance; +return "BT 정보 화면 공격력=" + (atk != null ? atk.text : "?") + " 생명력=" + (hp != null ? hp.text : "?") + " 레벨=" + (lv != null ? lv.text : "?") + " | 캐릭터 계산값 atk=" + (p != null ? p.CalcFinalAttack().ToString() : "?") + " hp=" + (p != null ? p.CalcFinalHealthMax().ToString() : "?"); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/install.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/install.cs new file mode 100644 index 0000000..7f11708 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/install.cs @@ -0,0 +1,3 @@ +BurningTimes.Visual.TVLoadTimeline.Install(); +BurningTimes.Visual.TVMaterialProbe.Install(); +return "installed frame=" + UnityEngine.Time.frameCount; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/inv_equipment.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/inv_equipment.cs new file mode 100644 index 0000000..31a3cb4 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/inv_equipment.cs @@ -0,0 +1,4 @@ +var sb = new System.Text.StringBuilder(); +var pm = Fx.PlayerManager.Instance; +foreach (var kv in pm.player.itemsDic) { var it = kv.Value; if (it == null || it.itemJson == null || it.rawItem == null) continue; if (it.itemJson.IsEquip != 1) continue; sb.Append("ITEM id=").Append(it.rawItem._id).Append(" idx=").Append(it.itemJson.Index).Append(" equipType=").Append(it.itemJson.EquipmentType).Append(" weaponType=").Append(it.itemJson.WeaponType).Append(" grade=").Append(it.itemJson.ItemGrade).Append(" level=").Append(it.rawItem.level).Append(" mounted=").Append(pm.IsMount(it.rawItem._id)).Append("\n"); } +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp.cs new file mode 100644 index 0000000..4624197 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp.cs @@ -0,0 +1 @@ +var p = Fx.Player.Instance; if (p == null) return "player=null"; p.AddGemScore(100000); return "AddGemScore(100000) called frame=" + UnityEngine.Time.frameCount; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp600.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp600.cs new file mode 100644 index 0000000..38d8976 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_addexp600.cs @@ -0,0 +1 @@ +var p = Fx.Player.Instance; if (p == null) return "player=null"; p.AddGemScore(600); return "AddGemScore(600) called frame=" + UnityEngine.Time.frameCount; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_off.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_off.cs new file mode 100644 index 0000000..aeb47fa --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_off.cs @@ -0,0 +1 @@ +var g = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); if (g == null) return "no gate"; g.enabled = false; return "gate disabled gemLevelUp=" + Fx.PlayerManager.Instance.gemLevelUp; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_on.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_on.cs new file mode 100644 index 0000000..bc4f8fc --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_gate_on.cs @@ -0,0 +1 @@ +var g = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); if (g == null) return "no gate"; g.enabled = true; return "gate enabled gemLevelUp=" + Fx.PlayerManager.Instance.gemLevelUp; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_pick_card.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_pick_card.cs new file mode 100644 index 0000000..cd48e9d --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_pick_card.cs @@ -0,0 +1,4 @@ +var pop = UnityEngine.Object.FindFirstObjectByType(); if (pop == null) return "no popup"; +var card = pop.transform.Find("CommonSkillSelectPopup0"); if (card == null) return "no card0"; +var btn = card.GetComponentInChildren(); if (btn == null) return "no button"; +btn.onClick.Invoke(); return "picked card0 ts=" + UnityEngine.Time.timeScale; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_state.cs new file mode 100644 index 0000000..5fe097a --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/lv_state.cs @@ -0,0 +1,10 @@ +var sb = new System.Text.StringBuilder(); +sb.Append("play=").Append(UnityEditor.EditorApplication.isPlaying).Append(" scene=").Append(UnityEngine.SceneManagement.SceneManager.GetActiveScene().name).Append(" ts=").Append(UnityEngine.Time.timeScale); +sb.Append(" gemLevelUp=").Append(Fx.PlayerManager.HasInstance() ? Fx.PlayerManager.Instance.gemLevelUp.ToString() : "noPM"); +var gate = UnityEngine.Object.FindFirstObjectByType(); sb.Append(" gate=").Append(gate != null ? ("on applied=" + gate.applied) : "none"); +var pop = UnityEngine.Object.FindFirstObjectByType(); sb.Append(" skillPopup=").Append(pop != null); +var p = Fx.Player.Instance; +if (p != null) { var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; foreach (var n in new[]{"m_GemLevel","m_CurrentExp","m_VisibleExp","m_MaxExp","m_ExpUpdating"}) { System.Reflection.FieldInfo f = null; for (var tt = p.GetType(); tt != null && f == null; tt = tt.BaseType) f = tt.GetField(n, nf); sb.Append(" ").Append(n).Append("=").Append(f != null ? f.GetValue(p) : "?"); } } else sb.Append(" player=null"); +var urpDof = false; foreach (var v in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) { var prof = v.sharedProfile; if (prof == null) continue; UnityEngine.Rendering.Universal.DepthOfField d; if (prof.TryGet(out d) && d.active) urpDof = true; } +sb.Append(" dofActive=").Append(urpDof); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_charinfo.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_charinfo.cs new file mode 100644 index 0000000..d48d98d --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_charinfo.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no charinfo panel"; var m = p.GetType().GetMethod("Open") ?? p.GetType().GetMethod("Toggle"); if (m == null) return "no Open/Toggle"; m.Invoke(p, null); var prop = p.GetType().GetProperty("isOpen"); return "charinfo " + m.Name + " isOpen=" + (prop != null ? prop.GetValue(p) : "?"); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_contents.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_contents.cs new file mode 100644 index 0000000..cc413bb --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_contents.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no contents panel"; p.Open(); var hit = UnityEngine.GameObject.Find("TV_DailyChallengeHit"); return "contents open=" + p.isOpen + " dailyHit=" + (hit != null); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_equipment.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_equipment.cs new file mode 100644 index 0000000..10a5141 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_equipment.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no equipment panel"; p.Open(); return "equipment open=" + p.isOpen; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_growth.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_growth.cs new file mode 100644 index 0000000..22fbb55 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_growth.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no growth panel"; p.Open(); return "growth open=" + p.isOpen; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_info.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_info.cs new file mode 100644 index 0000000..1bddb67 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_info.cs @@ -0,0 +1,3 @@ +var ui = UnityEngine.Object.FindFirstObjectByType(); if (ui == null) return "no GamePlayUI"; +var btn = typeof(Fx.GamePlayUI).GetField("m_PlayerInfoButton", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(ui) as UnityEngine.UI.Button; +if (btn == null) return "no button"; btn.onClick.Invoke(); return "player info opened"; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_inventory.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_inventory.cs new file mode 100644 index 0000000..5dde619 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_inventory.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no inventory panel"; if (!p.isOpen) p.Toggle(); return "inventory open=" + p.isOpen; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_trait.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_trait.cs new file mode 100644 index 0000000..1e34f7f --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/open_trait.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); if (p == null) return "no trait panel"; p.Open(); return "trait open=" + p.isOpen; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/play_age.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/play_age.cs new file mode 100644 index 0000000..fb69783 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/play_age.cs @@ -0,0 +1 @@ +return "time=" + UnityEngine.Time.time.ToString("F0") + " unscaled=" + UnityEngine.Time.unscaledTime.ToString("F0") + " realtime=" + UnityEngine.Time.realtimeSinceStartup.ToString("F0") + " frame=" + UnityEngine.Time.frameCount; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/poll.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/poll.cs new file mode 100644 index 0000000..0708d7c --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/poll.cs @@ -0,0 +1,8 @@ +var sb = new System.Text.StringBuilder(); +sb.Append("play=").Append(UnityEditor.EditorApplication.isPlaying).Append(" scene=").Append(UnityEngine.SceneManagement.SceneManager.GetActiveScene().name).Append(" f=").Append(UnityEngine.Time.frameCount).Append(" rt=").Append(UnityEngine.Time.realtimeSinceStartup.ToString("F1")).Append(" ts=").Append(UnityEngine.Time.timeScale); +sb.Append(" req=").Append(Fx.LoadingScene.RequestGameSceneStatus).Append(" wm=").Append(Fx.WorldManager.HasInstance() ? Fx.WorldManager.Instance.finishGameProgress.ToString() : "-"); +sb.Append(" ui=").Append(Fx.UIManager.HasInstance()).Append(" player=").Append(Fx.Player.Instance != null).Append(" lobby=").Append(Fx.LobbyScene.HasInstance()).Append(" loading=").Append(Fx.LoadingScene.HasInstance()); +if (Fx.LobbyScene.HasInstance()) { var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; var login = typeof(Fx.LobbyScene).GetField("m_Login", nf).GetValue(Fx.LobbyScene.Instance) as UnityEngine.GameObject; sb.Append(" login=").Append(login != null && login.activeSelf); } +sb.Append(" mainPage=").Append(Fx.MainPageUI.HasInstance()); +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; sb.Append(" entities=").Append(w != null && w.IsCreated ? w.EntityManager.UniversalQuery.CalculateEntityCount() : -1); +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/proxies.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/proxies.cs new file mode 100644 index 0000000..cef6b14 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/proxies.cs @@ -0,0 +1 @@ +var p = UnityEngine.Object.FindFirstObjectByType(); return p == null ? "no component" : ("enabled=" + p.isActiveAndEnabled + " proxies=" + p.ProxyCount + " skipUnregister=" + BurningTimes.Visual.TVMonsterProxies.experimentSkipUnregister); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/refresh.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/refresh.cs new file mode 100644 index 0000000..7a9626d --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/refresh.cs @@ -0,0 +1,2 @@ +UnityEditor.AssetDatabase.Refresh(); +return "refreshed isCompiling=" + UnityEditor.EditorApplication.isCompiling + " isPlaying=" + UnityEditor.EditorApplication.isPlaying; diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/reset_tutorial.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/reset_tutorial.cs new file mode 100644 index 0000000..1fdc059 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/reset_tutorial.cs @@ -0,0 +1,6 @@ +var user = Fx.PlayerManager.Instance.player.user; +int before = user.gameTutorialChapter; +int after = before & ~(1 << 5); +Fx.GameHub.Instance.TutorialChapter(after, false, (ack) => { UnityEngine.Debug.Log("[TV] tutorial flag reset ack " + ack.result); }); +user.gameTutorialChapter = after; +return "gameTutorialChapter " + before + " -> " + after; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_attack_verify.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_attack_verify.ps1 new file mode 100644 index 0000000..b992074 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_attack_verify.ps1 @@ -0,0 +1,61 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$shots = 'C:\Users\sw\AppData\Local\Temp\claude\E--BurningTimes\8069d3be-a326-4f70-be4f-eeefd684887a\scratchpad\equip_shots' +$logFile = Join-Path $dir 'run_attack_verify.log' +$enc = New-Object System.Text.UTF8Encoding $false +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { $t = ($r -replace '\s+', ' '); return "RAW " + $t.Substring(0, [Math]::Min(300, $t.Length)) } +} +function EvCode($name, $code) { [IO.File]::WriteAllText((Join-Path $dir $name), $code, $enc); return (Ev $name) } +function Equip($id) { return EvCode 'tmp_equip.cs' ('var pm = Fx.PlayerManager.Instance; Protocol.Item item; if (!pm.player.itemsDic.TryGetValue(' + $id + ', out item)) return "no item"; Fx.GameHub.Instance.EquipItem(item, (ack) => { if (Fx.MainPageUI.HasInstance()) { var pg = Fx.MainPageUI.Instance.GetPage(); if (pg != null) pg.UpdatePage(); } }); return "equip requested id=' + $id + '";') } +function Shot($name) { + $r = unity command capture_game_view --project-path $proj --source screen --width 1080 --height 1920 --json 2>&1 | Out-String + if ($r -match '"([A-Za-z0-9+/=]{2000,})"') { [IO.File]::WriteAllBytes((Join-Path $shots $name), [Convert]::FromBase64String($matches[1])); return "shot $name saved" } else { return "shot failed" } +} +function AttackSamples($tag) { $seen = @{}; for ($i = 0; $i -lt 14; $i++) { $v = Ev 'weapon_vis.cs'; foreach ($l in ($v -split "`n")) { if ($l -match '^ANIM') { foreach ($m in [regex]::Matches($l, 'L\d=(\w+)')) { $n = $m.Groups[1].Value; if ($seen.ContainsKey($n)) { $seen[$n]++ } else { $seen[$n] = 1 } } } }; Start-Sleep -Milliseconds 350 }; Log ("[$tag] 애니메이션 상태(14회 표본 · 층 3개): " + (($seen.Keys | Sort-Object | ForEach-Object { "$_ x$($seen[$_])" }) -join ', ')) } +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log ("lobby ready: " + $s) +Start-Sleep 10 +Log ("[시작] ecs: " + (Ev 'ecs_weapon.cs')) +foreach ($l in ((Ev 'weapon_vis.cs') -split "`n")) { if ($l -match '^MESH') { Log ("[시작] vis: " + $l.Trim()) } } + +# 검 +Log ("[검] " + (Ev 'weapon_anim.cs')) +Log ("[검] " + (Ev 'tp_to_monster.cs')) +Start-Sleep 2 +AttackSamples '검' +Log (Shot 'attack_sword.png') +Log ("[검] ecs: " + (Ev 'ecs_weapon.cs')) + +# 활 +Log ("[활] " + (Equip 1)); Start-Sleep 4 +Log ("[활] " + (Ev 'weapon_anim.cs')) +Log ("[활] " + (Ev 'tp_to_monster.cs')) +Start-Sleep 2 +AttackSamples '활' +Log (Shot 'attack_bow.png') +Log ("[활] ecs: " + (Ev 'ecs_weapon.cs')) + +# 정보 화면 값 +Log ("[정보 화면] " + (Ev 'open_info.cs')); Start-Sleep 3 +Log ("[정보 화면 · 활] " + (Ev 'info_values.cs')) +Log ("[정보 화면] " + (Equip 8)); Start-Sleep 3 +Log ("[정보 화면 · 검] " + (Ev 'info_values.cs')) +Log (Shot 'info_sword.png') +Log ("[닫기] " + (Ev 'close_info.cs')) +Start-Sleep 2 +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan attack | ForEach-Object { Log $_ } +Log 'DONE' \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_probe.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_probe.ps1 new file mode 100644 index 0000000..eea0253 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_probe.ps1 @@ -0,0 +1,65 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$shots = 'C:\Users\sw\AppData\Local\Temp\claude\E--BurningTimes\8069d3be-a326-4f70-be4f-eeefd684887a\scratchpad\bt44_shots' +New-Item -ItemType Directory -Force $shots | Out-Null +$logFile = Join-Path $dir 'run_bt44_probe.log' +$enc = New-Object System.Text.UTF8Encoding $false +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { $t = ($r -replace '\s+', ' '); return "RAW " + $t.Substring(0, [Math]::Min(300, $t.Length)) } +} +function EvCode($name, $code) { [IO.File]::WriteAllText((Join-Path $dir $name), $code, $enc); return (Ev $name) } +function Shot($name) { + $r = unity command capture_game_view --project-path $proj --source screen --width 1080 --height 1920 --json 2>&1 | Out-String + if ($r -match '"([A-Za-z0-9+/=]{2000,})"') { [IO.File]::WriteAllBytes((Join-Path $shots $name), [Convert]::FromBase64String($matches[1])); return "shot $name saved" } else { return "shot failed" } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +function Block($tag, $text) { foreach ($l in ($text -split "`n")) { if ($l.Trim().Length -gt 0) { Log ("[$tag] " + $l.Trim()) } } } +function Widget($on) { if ($on) { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.EnterWidget(); return "widget=" + w.isWidget;' } else { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.ExitWidget(); return "widget=" + w.isWidget;' } } +function Travel($key) { + $start = Get-Date + Log ("[travel $key] " + (EvCode 'tmp_travel.cs' ('var t = BurningTimes.Visual.TVFieldTravel.Instance; if (t == null) return "no instance"; return "started=" + t.Travel("' + $key + '") + " from=" + t.currentKey;'))) + Start-Sleep 2 + $deadline = (Get-Date).AddSeconds(120) + do { Start-Sleep 2; $s = Ev 'travel_state.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } + while (-not ($s -match 'traveling=False' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) + Log ("[travel $key] done after {0:F1}s (바깥 시계 · 폴링 2초 간격): {1}" -f ((Get-Date) - $start).TotalSeconds, $s) + Start-Sleep 4 +} + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Set-Content -Path (Join-Path $dir 'travel_timeline.log') -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log ("lobby ready: " + $s) +Start-Sleep 8 + +Block '평소 · 숲' (Ev 'widget_probe.cs') +Block '평소 · 숲 · 보이는 것' (Ev 'visible_probe.cs') +Log ("[최소화 진입] " + (Widget $true)); Start-Sleep 3 +Block '최소화 · 숲' (Ev 'widget_probe.cs') +Block '최소화 · 숲 · 보이는 것' (Ev 'visible_probe.cs') +Log (Shot 'widget_forest.png') +Log ("[최소화 해제] " + (Widget $false)); Start-Sleep 2 + +Log ("[hook] " + (Ev 'travel_hook.cs')) +Travel 'Graveyard' +Log ("[최소화 진입] " + (Widget $true)); Start-Sleep 3 +Block '최소화 · 묘지 · 보이는 것' (Ev 'visible_probe.cs') +Log (Shot 'widget_graveyard.png') +Log ("[최소화 해제] " + (Widget $false)); Start-Sleep 2 +Travel 'Lobby' +Travel 'Graveyard' +Travel 'ScorchedDesert' +Travel 'Lobby' +Log ("[unhook] " + (Ev 'travel_unhook.cs')) +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan bt44probe | ForEach-Object { Log $_ } +Log 'DONE' diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_verify.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_verify.ps1 new file mode 100644 index 0000000..9e49a29 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_bt44_verify.ps1 @@ -0,0 +1,110 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$shots = 'C:\Users\sw\AppData\Local\Temp\claude\E--BurningTimes\8069d3be-a326-4f70-be4f-eeefd684887a\scratchpad\bt44_shots' +New-Item -ItemType Directory -Force $shots | Out-Null +$logFile = Join-Path $dir 'run_bt44_verify.log' +$enc = New-Object System.Text.UTF8Encoding $false +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { $t = ($r -replace '\s+', ' '); return "RAW " + $t.Substring(0, [Math]::Min(300, $t.Length)) } +} +function EvCode($name, $code) { [IO.File]::WriteAllText((Join-Path $dir $name), $code, $enc); return (Ev $name) } +function EvTemplate($template, $map) { $code = [IO.File]::ReadAllText((Join-Path $dir $template), [Text.Encoding]::UTF8); foreach ($k in $map.Keys) { $code = $code.Replace($k, [string]$map[$k]) }; return (EvCode 'tmp_template.cs' $code) } +function Shot($name) { + $r = unity command capture_game_view --project-path $proj --source screen --width 1080 --height 1920 --json 2>&1 | Out-String + if ($r -match '"([A-Za-z0-9+/=]{2000,})"') { [IO.File]::WriteAllBytes((Join-Path $shots $name), [Convert]::FromBase64String($matches[1])); return "shot $name saved" } else { return "shot failed" } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +function Block($tag, $text) { foreach ($l in ($text -split "`n")) { $s = $l.Trim(); if ($s.Length -gt 0 -and $s -notmatch 'Impact_\d+') { if ($s.Length -gt 300) { $s = $s.Substring(0, 300) }; Log ("[$tag] " + $s) } } } +function Widget($on) { if ($on) { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.EnterWidget(); return "widget=" + w.isWidget;' } else { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.ExitWidget(); return "widget=" + w.isWidget;' } } +function Pointer($target, $button) { return EvTemplate 'wsm_pointer.template.cs' @{ '__TARGET__' = $target; '__BUTTON__' = $button } } +function Boss($step) { return EvTemplate 'boss_roundtrip.template.cs' @{ '__STEP__' = $step } } +function Travel($key, $shot = $null) { + $start = Get-Date + Log ("[travel $key] " + (EvCode 'tmp_travel.cs' ('var t = BurningTimes.Visual.TVFieldTravel.Instance; if (t == null) return "no instance"; return "started=" + t.Travel("' + $key + '") + " from=" + t.currentKey;'))) + if ($shot) { Start-Sleep -Milliseconds 700; Log (Shot $shot) } + $deadline = (Get-Date).AddSeconds(120) + do { Start-Sleep -Milliseconds 800; $s = Ev 'travel_state.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } + while (-not ($s -match 'traveling=False' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) + Log ("[travel $key] done after {0:F1}s (바깥 시계): {1}" -f ((Get-Date) - $start).TotalSeconds, (Ev 'travel_check.cs')) +} + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Set-Content -Path (Join-Path $dir 'travel_timeline.log') -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log ("lobby ready: " + $s) +Start-Sleep 10 +$prefBefore = EvCode 'tmp_pref.cs' 'return UnityEngine.PlayerPrefs.HasKey("BT_WidgetSize") ? UnityEngine.PlayerPrefs.GetFloat("BT_WidgetSize").ToString(System.Globalization.CultureInfo.InvariantCulture) : "none";' +Log ("[준비] 창 크기 저장 값(시험 전)=" + $prefBefore) +Log ("[숲 · 처음] " + (Ev 'travel_check.cs')) +Log ("[hook] " + (Ev 'travel_hook.cs')) + +# 1) 맵만 바꿔 끼우기 — 처음 가는 맵(굽기 포함) + 로딩 화면 캡처 +Travel 'Graveyard' 'loading_first.png' +Log ("[묘지 · 도착 직후 능력치] " + (Ev 'ecs_weapon.cs')) +Start-Sleep 8 +Log ("[묘지 · 8초 뒤] " + (Ev 'travel_check.cs')) + +# 2) 최소화 모드 — 배경 숨김 +Log ("[최소화 진입] " + (Widget $true)); Start-Sleep 2 +Log ("[최소화 · 묘지] " + (Ev 'wsm_state.cs')) +Block '최소화 · 묘지 · 보이는 것' (Ev 'visible_probe.cs') +Log (Shot 'widget_graveyard_after.png') + +# 3) 창 크기 메뉴 — 띠에 마우스 → 메뉴 → 막대 끌기 → 놓기 → 벗어남 +Log ("[메뉴] " + (Pointer 'zone' 0)); Start-Sleep -Milliseconds 600 +Log ("[메뉴 · 띠에 댐] " + (Ev 'wsm_state.cs')) +Log ("[메뉴] " + (Pointer 'track:0.75' 1)); Start-Sleep -Milliseconds 500 +Log ("[메뉴 · 막대 누름] " + (Ev 'wsm_state.cs')) +Log ("[메뉴] " + (Pointer 'track:0.25' 1)); Start-Sleep -Milliseconds 500 +Log ("[메뉴 · 끄는 중] " + (Ev 'wsm_state.cs')) +Log ("[메뉴] " + (Pointer 'track:0.25' 0)); Start-Sleep -Milliseconds 600 +Log ("[메뉴 · 놓음] " + (Ev 'wsm_state.cs')) +Log (Shot 'widget_menu.png') +Log ("[메뉴] " + (Pointer 'away' 0)); Start-Sleep -Milliseconds 1800 +Log ("[메뉴 · 벗어남] " + (Ev 'wsm_state.cs')) +# 시험으로 바꾼 창 크기 값을 되돌린다 +if ($prefBefore -eq 'none') { Log ("[원복] " + (EvCode 'tmp_pref.cs' 'BurningTimes.Visual.TVWidgetSizeMenu.Instance.size = 1f; UnityEngine.PlayerPrefs.DeleteKey("BT_WidgetSize"); return "size=1 · 저장 값 지움";')) } +else { Log ("[원복] " + (EvCode 'tmp_pref.cs' ('BurningTimes.Visual.TVWidgetSizeMenu.Instance.size = ' + $prefBefore + 'f; return "size=" + BurningTimes.Visual.TVWidgetSizeMenu.Instance.size;'))) } +Log ("[최소화 해제] " + (Widget $false)); Start-Sleep 2 +Log ("[평소 · 묘지] " + (Ev 'wsm_state.cs')) +Block '평소 · 묘지 · 보이는 것' (Ev 'visible_probe.cs') + +# 4) 보스전 — 맵을 바꿔 끼운 뒤에도 들어갔다 나올 수 있는가 +Log ("[보스전] " + (Boss 'enter')); Start-Sleep 5 +Log ("[보스전 · 5초 뒤] " + (Boss 'state')) +Log ("[보스전 중 이동 시도] " + (EvCode 'tmp_travel.cs' 'var t = BurningTimes.Visual.TVFieldTravel.Instance; return "started=" + t.Travel("Lobby");')) +Log ("[보스전] " + (Boss 'exit')); Start-Sleep 3 +Log ("[보스전 나온 뒤] " + (Boss 'state') + " | " + (Ev 'travel_check.cs')) + +# 5) 숲으로 · 다시 묘지 · 다른 맵 +Travel 'Lobby' +Start-Sleep 8 +Log ("[숲 · 8초 뒤] " + (Ev 'travel_check.cs')) +Travel 'Graveyard' +Start-Sleep 4 +Travel 'ScorchedDesert' +Start-Sleep 6 +Log ("[사막 · 6초 뒤] " + (Ev 'travel_check.cs')) + +# 6) 예전 방식(로비 씬 다시 읽기)도 그대로 되는가 +Log ("[예전 방식 켜기] " + (EvCode 'tmp_swap.cs' 'var t = BurningTimes.Visual.TVFieldTravel.Instance; typeof(BurningTimes.Visual.TVFieldTravel).GetField("swapInPlace", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(t, false); return "swapInPlace=false";')) +Travel 'Graveyard' +Start-Sleep 8 +Log ("[묘지 · 예전 방식 · 8초 뒤] " + (Ev 'travel_check.cs')) +Travel 'Lobby' +Start-Sleep 6 +Log ("[숲 · 끝] " + (Ev 'travel_check.cs')) + +Log ("[unhook] " + (Ev 'travel_unhook.cs')) +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan bt44verify | ForEach-Object { Log $_ } +Log 'DONE' diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_crash_test.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_crash_test.ps1 new file mode 100644 index 0000000..47e5f4a --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_crash_test.ps1 @@ -0,0 +1,37 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$logFile = Join-Path $dir 'run_crash_test.log' +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + $r.Substring(0, [Math]::Min(300, $r.Length)) } +} +function EvCode($code, $timeout = 30) { + $r = unity command eval --project-path $proj --code $code --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + $r.Substring(0, [Math]::Min(300, $r.Length)) } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 6 +$r = '' +for ($i = 0; $i -lt 30; $i++) { $r = Ev 'install.cs'; if ($r -like 'installed*') { break }; Start-Sleep 3 } +Log "install: $r" +Log ("experiment flag: " + (Ev 'flag_on.cs')) +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True') -and (Get-Date) -lt $deadline) +Log 'lobby ready — 프록시 생성 대기 15초' +Start-Sleep 20 +Log ("proxies: " + (Ev 'proxies.cs')) +Log ("start: " + (Ev 'start_daily.cs')) +$deadline = (Get-Date).AddSeconds(150) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash reproduced)'; exit 3 } } +while (-not ($s -match 'scene=(?!Lobby)' -and $s -match 'req=None' -and $s -match 'ui=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log 'SURVIVED — game ready without crash' +Start-Sleep 5 +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Log 'DONE' diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle.ps1 new file mode 100644 index 0000000..ee6edeb --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle.ps1 @@ -0,0 +1,47 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$logFile = Join-Path $dir 'run_daily_cycle.log' +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + $r.Substring(0, [Math]::Min(300, $r.Length)) } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 120)) +Start-Sleep 6 + +# 1. 훅 설치(도메인 리로드 끝날 때까지 재시도) +$r = '' +for ($i = 0; $i -lt 30; $i++) { $r = Ev 'install.cs'; if ($r -like 'installed*') { break }; Start-Sleep 3 } +Log "install: $r" + +# 2. 로비 준비 대기 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True') -and (Get-Date) -lt $deadline) +Log 'lobby ready' +Start-Sleep 6 + +# 3. 일일 도전 시작 +Log ("start: " + (Ev 'start_daily.cs')) + +# 4. 전투 준비 대기(씬 Loading · 요청 None · UIManager · Player) +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 3 } } +while (-not ($s -match 'scene=(?!Lobby)' -and $s -match 'req=None' -and $s -match 'ui=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log 'game ready' +Start-Sleep 12 + +# 5. 메뉴 → 홈으로 나가기 +Log ("exit: " + (Ev 'exit_menu.cs')) + +# 6. 로비 복귀 대기 후 20초 관찰 +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 4 } } +while (-not ($s -match 'scene=Lobby' -and $s -match 'lobby=True') -and (Get-Date) -lt $deadline) +Log 'back in lobby scene' +for ($i = 0; $i -lt 6; $i++) { Start-Sleep 4; Log (Ev 'poll.cs') } +Log 'DONE' diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle2.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle2.ps1 new file mode 100644 index 0000000..6be570d --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_daily_cycle2.ps1 @@ -0,0 +1,25 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$logFile = Join-Path $dir 'run_daily_cycle2.log' +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + $r.Substring(0, [Math]::Min(300, $r.Length)) } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Log ("install: " + (Ev 'install.cs')) +Log ("start: " + (Ev 'start_daily.cs')) +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 3 } } +while (-not ($s -match 'scene=(?!Lobby)' -and $s -match 'req=None' -and $s -match 'ui=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log 'game ready' +Start-Sleep 12 +Log ("exit: " + (Ev 'exit_menu.cs')) +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 4 } } +while (-not ($s -match 'scene=Lobby' -and $s -match 'lobby=True') -and (Get-Date) -lt $deadline) +Log 'back in lobby scene' +for ($i = 0; $i -lt 6; $i++) { Start-Sleep 4; Log (Ev 'poll.cs') } +Log 'DONE' \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_equip_verify.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_equip_verify.ps1 new file mode 100644 index 0000000..8f541c9 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_equip_verify.ps1 @@ -0,0 +1,87 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$shots = 'C:\Users\sw\AppData\Local\Temp\claude\E--BurningTimes\8069d3be-a326-4f70-be4f-eeefd684887a\scratchpad\equip_shots' +New-Item -ItemType Directory -Force $shots | Out-Null +$logFile = Join-Path $dir 'run_equip_verify.log' +$enc = New-Object System.Text.UTF8Encoding $false +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { $t = ($r -replace '\s+', ' '); return "RAW " + $t.Substring(0, [Math]::Min(300, $t.Length)) } +} +function EvCode($name, $code) { [IO.File]::WriteAllText((Join-Path $dir $name), $code, $enc); return (Ev $name) } +function Equip($id) { return EvCode 'tmp_equip.cs' ('var pm = Fx.PlayerManager.Instance; Protocol.Item item; if (!pm.player.itemsDic.TryGetValue(' + $id + ', out item)) return "no item"; Fx.GameHub.Instance.EquipItem(item, (ack) => { UnityEngine.Debug.Log("[TV] equip ack " + ack.result); if (Fx.MainPageUI.HasInstance()) { var pg = Fx.MainPageUI.Instance.GetPage(); if (pg != null) pg.UpdatePage(); } }); return "equip requested id=' + $id + '";') } +function Unequip($id) { return EvCode 'tmp_unequip.cs' ('var pm = Fx.PlayerManager.Instance; Protocol.Item item; if (!pm.player.itemsDic.TryGetValue(' + $id + ', out item)) return "no item"; Fx.GameHub.Instance.UnequipItem(item, (ack) => { UnityEngine.Debug.Log("[TV] unequip ack " + ack.result); if (Fx.MainPageUI.HasInstance()) { var pg = Fx.MainPageUI.Instance.GetPage(); if (pg != null) pg.UpdatePage(); } }); return "unequip requested id=' + $id + '";') } +function Shot($name) { + $r = unity command capture_game_view --project-path $proj --source screen --width 1080 --height 1920 --json 2>&1 | Out-String + if ($r -match '"([A-Za-z0-9+/=]{2000,})"') { [IO.File]::WriteAllBytes((Join-Path $shots $name), [Convert]::FromBase64String($matches[1])); return "shot $name saved" } else { $t = ($r -replace '\s+', ' '); return "shot failed: " + $t.Substring(0, [Math]::Min(200, $t.Length)) } +} +function Dump($tag) { Log ("[$tag] ecs: " + (Ev 'ecs_weapon.cs')); foreach ($l in ((Ev 'weapon_vis.cs') -split "`n")) { if ($l.Trim()) { Log ("[$tag] vis: " + $l.Trim()) } } } +function AnimSamples($tag) { $seen = @{}; for ($i = 0; $i -lt 8; $i++) { $v = Ev 'weapon_vis.cs'; foreach ($l in ($v -split "`n")) { if ($l -match '^ANIM') { foreach ($m in [regex]::Matches($l, 'L\d=(\w+)')) { $seen[$m.Groups[1].Value] = 1 } } }; Start-Sleep -Milliseconds 400 }; Log ("[$tag] 공격 애니메이션 상태(8회 표본): " + (($seen.Keys | Sort-Object) -join ', ')) } +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log ("lobby ready: " + $s) +Start-Sleep 10 + +# A. 시작 상태 +foreach ($l in ((Ev 'weapon_state.cs') -split "`n")) { if ($l -match 'EQUIP|PLAYER|lobbyPlayer|TVArcherGear|TVCharacterSwap|playing=') { Log ("[A 시작] " + $l.Trim().Substring(0, [Math]::Min(330, $l.Trim().Length))) } } +Dump 'A 시작' +AnimSamples 'A 시작' +Log (Shot 'A_start.png') + +# B. 가방 +$inv = Ev 'inv_equipment.cs' +$items = @() +foreach ($l in ($inv -split "`n")) { if ($l -match 'ITEM id=(\d+) idx=(\d+) equipType=(\w+) weaponType=(\w+) grade=(\w+) level=(\d+) mounted=(\w+)') { $items += [pscustomobject]@{ id = [int]$matches[1]; idx = [int]$matches[2]; equipType = $matches[3]; weaponType = $matches[4]; mounted = $matches[7] } } } +Log ("[B 가방] 장비 " + $items.Count + "개: " + (($items | ForEach-Object { "$($_.id):$($_.idx):$($_.equipType)/$($_.weaponType)" + $(if ($_.mounted -eq 'True') { '*' } else { '' }) }) -join ' ')) +$current = $items | Where-Object { $_.equipType -eq 'Weapon' -and $_.mounted -eq 'True' } | Select-Object -First 1 +$bow = $items | Where-Object { $_.equipType -eq 'Weapon' -and $_.weaponType -eq 'Bow' -and $_.mounted -ne 'True' } | Select-Object -First 1 +$other = $items | Where-Object { $_.equipType -eq 'Weapon' -and $_.weaponType -ne 'Bow' -and $_.mounted -ne 'True' -and ($current -eq $null -or $_.weaponType -ne $current.weaponType) } | Select-Object -First 1 +$armor = $items | Where-Object { $_.equipType -ne 'Weapon' -and $_.mounted -ne 'True' } | Select-Object -First 1 +Log ("[B 가방] 지금 무기=" + $(if ($current) { "$($current.id)/$($current.weaponType)" } else { '없음' }) + " 활=" + $(if ($bow) { $bow.id } else { '없음' }) + " 다른 무기=" + $(if ($other) { "$($other.id)/$($other.weaponType)" } else { '없음' }) + " 방어구=" + $(if ($armor) { "$($armor.id)/$($armor.equipType)" } else { '없음' })) + +# C. 활 장착 +if ($bow) { Log ("[C 활] " + (Equip $bow.id)); Start-Sleep 4; Dump 'C 활'; AnimSamples 'C 활'; Log (Shot 'C_bow.png') } +# D. 다른 무기 장착 +if ($other) { Log ("[D 다른 무기] " + (Equip $other.id)); Start-Sleep 4; Dump 'D 다른 무기'; AnimSamples 'D 다른 무기'; Log (Shot 'D_other.png') } +# E. 처음 무기로 +if ($current) { Log ("[E 처음 무기] " + (Equip $current.id)); Start-Sleep 4; Dump 'E 처음 무기'; AnimSamples 'E 처음 무기'; Log (Shot 'E_first.png') } + +# F. 플레이어 정보 화면 +Log ("[F 정보 화면] " + (Ev 'open_info.cs')); Start-Sleep 3 +foreach ($l in ((Ev 'equip_ui_state.cs') -split "`n")) { if ($l.Trim()) { Log ("[F 열림] " + $l.Trim().Substring(0, [Math]::Min(420, $l.Trim().Length))) } } +Log (Shot 'F_info_open.png') +if ($armor) { + Log ("[F 방어구 장착] " + (Equip $armor.id)); Start-Sleep 3 + foreach ($l in ((Ev 'equip_ui_state.cs') -split "`n")) { if ($l -match 'CharacterPageUI|Player.Instance|BT info') { Log ("[F 방어구 장착 뒤] " + $l.Trim().Substring(0, [Math]::Min(420, $l.Trim().Length))) } } + Log ("[F 방어구 장착 뒤] ecs: " + (Ev 'ecs_weapon.cs')) + Log (Shot 'F_info_armor.png') + Log ("[F 방어구 해제] " + (Unequip $armor.id)); Start-Sleep 3 + foreach ($l in ((Ev 'equip_ui_state.cs') -split "`n")) { if ($l -match 'CharacterPageUI|Player.Instance|BT info') { Log ("[F 방어구 해제 뒤] " + $l.Trim().Substring(0, [Math]::Min(420, $l.Trim().Length))) } } + Log ("[F 방어구 해제 뒤] ecs: " + (Ev 'ecs_weapon.cs')) +} +if ($bow) { + Log ("[F 정보 화면 열린 채 활 장착] " + (Equip $bow.id)); Start-Sleep 3 + foreach ($l in ((Ev 'equip_ui_state.cs') -split "`n")) { if ($l -match 'CharacterPageUI|Player.Instance|BT info') { Log ("[F 활 장착 뒤] " + $l.Trim().Substring(0, [Math]::Min(420, $l.Trim().Length))) } } + Log (Shot 'F_info_bow.png') +} + +# G. 무기 해제 → 기본 무기 +$mountedNow = if ($bow) { $bow } else { $current } +if ($mountedNow) { Log ("[G 무기 해제] " + (Unequip $mountedNow.id)); Start-Sleep 4; Dump 'G 무기 해제'; foreach ($l in ((Ev 'equip_ui_state.cs') -split "`n")) { if ($l -match 'CharacterPageUI|BT info') { Log ("[G 무기 해제 뒤] " + $l.Trim().Substring(0, [Math]::Min(420, $l.Trim().Length))) } } } +# 원래 장착으로 +if ($current) { Log ("[복구] " + (Equip $current.id)); Start-Sleep 3; Dump '복구' } +Log ("[닫기] " + (Ev 'close_info.cs')) +Start-Sleep 2 +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan equip | ForEach-Object { Log $_ } +Log 'DONE' \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_levelup_verify.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_levelup_verify.ps1 new file mode 100644 index 0000000..6e7f293 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_levelup_verify.ps1 @@ -0,0 +1,61 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$logFile = Join-Path $dir 'run_levelup_verify.log' +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + ($r -replace '\s+', ' ').Substring(0, [Math]::Min(300, ($r -replace '\s+', ' ').Length)) } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +Set-Content -Path $logFile -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log 'lobby ready' +Start-Sleep 6 +Log ("[1 field] state: " + (Ev 'lv_state.cs')) +Log ("[1 field] " + (Ev 'lv_addexp600.cs')) +Start-Sleep 3 +Log ("[1 field] after exp: " + (Ev 'lv_state.cs')) +Log ("[2 switch on] " + (Ev 'lv_gate_off.cs')) +Log ("[2 switch on] " + (Ev 'lv_addexp600.cs')) +Start-Sleep 3 +Log ("[2 switch on] after exp: " + (Ev 'lv_state.cs')) +for ($i = 0; $i -lt 5; $i++) { $st = Ev 'lv_state.cs'; if ($st -notmatch 'skillPopup=True') { break }; Log ("[2 switch on] " + (Ev 'lv_pick_card.cs')); Start-Sleep 2 } +Log ("[2 switch on] after pick: " + (Ev 'lv_state.cs')) +Log ("[3 switch off again] " + (Ev 'lv_gate_on.cs')) +Log ("[3 switch off again] " + (Ev 'lv_addexp600.cs')) +Start-Sleep 3 +Log ("[3 switch off again] after exp: " + (Ev 'lv_state.cs')) +Log ("[4 content] start: " + (Ev 'start_daily.cs')) +$entered = $false; $deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if ($s -match 'scene=(?!Lobby)' -and $s -match 'req=None' -and $s -match 'ui=True' -and $s -match 'player=True') { $entered = $true }; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 3 } } +while (-not $entered -and (Get-Date) -lt $deadline) +Log ("[4 content] entered=$entered " + $s) +if ($entered) { + Start-Sleep 5 + Log ("[4 content] state: " + (Ev 'lv_state.cs')) + Log ("[4 content] " + (Ev 'lv_addexp600.cs')) + Start-Sleep 3 + Log ("[4 content] after exp: " + (Ev 'lv_state.cs')) + for ($i = 0; $i -lt 5; $i++) { $st = Ev 'lv_state.cs'; if ($st -notmatch 'skillPopup=True') { break }; Log ("[4 content] " + (Ev 'lv_pick_card.cs')); Start-Sleep 2 } + Log ("[4 content] after pick: " + (Ev 'lv_state.cs')) + Log ("[4 content] exit: " + (Ev 'exit_menu.cs')) + $deadline = (Get-Date).AddSeconds(240) + do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 4 } } + while (-not ($s -match 'scene=Lobby' -and $s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) + Log ("[5 back in field] " + $s) + Start-Sleep 5 + Log ("[5 back in field] state: " + (Ev 'lv_state.cs')) + Log ("[5 back in field] " + (Ev 'lv_addexp600.cs')) + Start-Sleep 3 + Log ("[5 back in field] after exp: " + (Ev 'lv_state.cs')) +} +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan verify | ForEach-Object { Log $_ } +Log 'DONE' \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_verify.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_verify.ps1 new file mode 100644 index 0000000..fcbf3ad --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_verify.ps1 @@ -0,0 +1,45 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$logFile = Join-Path $dir 'run_verify.log' +function Log($m) { $line = "{0:HH:mm:ss} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { return "RAW " + $r.Substring(0, [Math]::Min(300, $r.Length)) } +} +function Errors() { $e = unity command get_console_logs --project-path $proj --severity error --limit 20 --json 2>&1 | Out-String; try { $je = $e | ConvertFrom-Json; $msgs = @($je.data.result.logs | ForEach-Object { $_.message.Substring(0, [Math]::Min(160, $_.message.Length)) }); return "errors total=$($je.data.result.total) " + ($msgs -join ' || ') } catch { return "errors ?" } } +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +Set-Content -Path $logFile -Value '' -Encoding UTF8 +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 6 +$r = '' +for ($i = 0; $i -lt 30; $i++) { $r = Ev 'install.cs'; if ($r -like 'installed*') { break }; Start-Sleep 3 } +Log "install: $r" +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True') -and (Get-Date) -lt $deadline) +Log 'lobby ready' +Start-Sleep 5 +Log ("state: " + (Ev 'check_state.cs')) +Log ("before panels: " + (Errors)) +foreach ($f in @('open_inventory.cs','open_equipment.cs','open_growth.cs','open_trait.cs','open_contents.cs','open_charinfo.cs')) { Log ("$f -> " + (Ev $f)); Start-Sleep 2 } +Log ("close: " + (Ev 'close_all.cs')) +Start-Sleep 2 +Log ("after panels: " + (Errors)) +Log ("start: " + (Ev 'start_daily.cs')) +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 3 } } +while (-not ($s -match 'scene=(?!Lobby)' -and $s -match 'req=None' -and $s -match 'ui=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log 'game ready' +Start-Sleep 6 +Log ("reset tutorial: " + (Ev 'reset_tutorial.cs')) +Start-Sleep 3 +Log ("exit: " + (Ev 'exit_menu.cs')) +$deadline = (Get-Date).AddSeconds(300) +do { Start-Sleep 3; $s = Ev 'poll.cs'; Log $s; if (-not (Alive)) { Log 'EDITOR GONE (crash?)'; exit 4 } } +while (-not ($s -match 'scene=Lobby' -and $s -match 'lobby=True') -and (Get-Date) -lt $deadline) +Log 'back in lobby scene' +for ($i = 0; $i -lt 6; $i++) { Start-Sleep 3; Log ((Ev 'poll.cs') + ' | ' + (Ev 'check_state.cs')) } +Log ("final: " + (Errors)) +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Log 'DONE' \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_wsm_test.ps1 b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_wsm_test.ps1 new file mode 100644 index 0000000..5536cb4 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/run_wsm_test.ps1 @@ -0,0 +1,58 @@ +$proj = 'C:\BurningTimes\FirstRising' +$dir = Join-Path $env:TEMP 'bt_evals' +$shots = 'C:\Users\sw\AppData\Local\Temp\claude\E--BurningTimes\8069d3be-a326-4f70-be4f-eeefd684887a\scratchpad\bt44_shots' +$logFile = Join-Path $dir 'run_wsm_test.log' +$enc = New-Object System.Text.UTF8Encoding $false +function Log($m) { $line = "{0:HH:mm:ss.fff} {1}" -f (Get-Date), $m; $line; Add-Content -Path $logFile -Value $line -Encoding UTF8 } +function Ev($file, $timeout = 30) { + $r = unity command eval_file --project-path $proj --file (Join-Path $dir $file) --timeout $timeout --json 2>&1 | Out-String + try { $j = $r | ConvertFrom-Json; if ($j.data) { return [string]$j.data.result.result } else { return "ERR " + (($j.errors | ForEach-Object { $_.message }) -join '; ') } } + catch { $t = ($r -replace '\s+', ' '); return "RAW " + $t.Substring(0, [Math]::Min(300, $t.Length)) } +} +function EvCode($name, $code) { [IO.File]::WriteAllText((Join-Path $dir $name), $code, $enc); return (Ev $name) } +function EvTemplate($template, $map) { $code = [IO.File]::ReadAllText((Join-Path $dir $template), [Text.Encoding]::UTF8); foreach ($k in $map.Keys) { $code = $code.Replace($k, [string]$map[$k]) }; return (EvCode 'tmp_template.cs' $code) } +function Shot($name) { + $r = unity command capture_game_view --project-path $proj --source screen --width 1080 --height 1920 --json 2>&1 | Out-String + if ($r -match '"([A-Za-z0-9+/=]{2000,})"') { [IO.File]::WriteAllBytes((Join-Path $shots $name), [Convert]::FromBase64String($matches[1])); return "shot $name saved" } else { return "shot failed" } +} +function Alive() { return ((Get-Process Unity -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle -like 'FirstRising*' }) -ne $null) } +function Widget($on) { if ($on) { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.EnterWidget(); return "widget=" + w.isWidget;' } else { return EvCode 'tmp_widget.cs' 'var w = Fx.DesktopWidgetManager.Instance; w.ExitWidget(); return "widget=" + w.isWidget;' } } +function Pointer($target, $button) { return EvTemplate 'wsm_pointer.template.cs' @{ '__TARGET__' = $target; '__BUTTON__' = $button } } + +Set-Content -Path $logFile -Value '' -Encoding UTF8 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' mark | ForEach-Object { Log $_ } +Log ("play: " + ((unity command editor_play --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 100)) +Start-Sleep 8 +$deadline = (Get-Date).AddSeconds(240) +do { Start-Sleep 3; $s = Ev 'poll.cs'; if (-not (Alive)) { Log 'EDITOR GONE'; exit 2 } } +while (-not ($s -match 'lobby=True' -and $s -match 'login=False' -and $s -match 'mainPage=True' -and $s -match 'player=True') -and (Get-Date) -lt $deadline) +Log ("lobby ready") +Start-Sleep 6 +$prefBefore = EvCode 'tmp_pref.cs' 'return UnityEngine.PlayerPrefs.HasKey("BT_WidgetSize") ? UnityEngine.PlayerPrefs.GetFloat("BT_WidgetSize").ToString(System.Globalization.CultureInfo.InvariantCulture) : "none";' +Log ("[준비] 창 크기 저장 값(시험 전)=" + $prefBefore) +Log ("[최소화 진입] " + (Widget $true)); Start-Sleep 2 +Log ("[진단 · 처음] " + (Ev 'wsm_diag.cs')) +Log ("[메뉴] " + (Pointer 'zone' 0)) +Log ("[진단 · 띠에 댄 직후] " + (Ev 'wsm_diag.cs')) +Start-Sleep -Milliseconds 500 +Log ("[진단 · 0.5초 뒤] " + (Ev 'wsm_diag.cs')) +Log ("[상태] " + (Ev 'wsm_state.cs')) +Log ("[메뉴] " + (Pointer 'track:0.75' 1)); Start-Sleep -Milliseconds 400 +Log ("[상태 · 누름] " + (Ev 'wsm_state.cs')) +Log ("[메뉴] " + (Pointer 'track:0.5' 1)); Start-Sleep -Milliseconds 400 +Log ("[상태 · 끄는 중] " + (Ev 'wsm_state.cs')) +Log (Shot 'widget_menu.png') +Log ("[메뉴] " + (Pointer 'track:0.5' 0)); Start-Sleep -Milliseconds 600 +Log ("[상태 · 놓음] " + (Ev 'wsm_state.cs')) +Log (Shot 'widget_menu_applied.png') +Log ("[메뉴] " + (Pointer 'away' 0)); Start-Sleep -Milliseconds 1800 +Log ("[상태 · 벗어남] " + (Ev 'wsm_state.cs')) +if ($prefBefore -eq 'none') { Log ("[원복] " + (EvCode 'tmp_pref.cs' 'BurningTimes.Visual.TVWidgetSizeMenu.Instance.size = 1f; UnityEngine.PlayerPrefs.DeleteKey("BT_WidgetSize"); return "size=1 · 저장 값 지움";')) } +else { Log ("[원복] " + (EvCode 'tmp_pref.cs' ('BurningTimes.Visual.TVWidgetSizeMenu.Instance.size = ' + $prefBefore + 'f; return "size=" + BurningTimes.Visual.TVWidgetSizeMenu.Instance.size;'))) } +Log ("[훅 제거] " + (Pointer 'off' 0)) +Log ("[최소화 해제] " + (Widget $false)); Start-Sleep 1 +Log ("[상태 · 평소] " + (Ev 'wsm_state.cs')) +Log ("stop: " + ((unity command editor_stop --project-path $proj --json 2>&1 | Out-String) -replace '\s+', ' ').Substring(0, 80)) +Start-Sleep 5 +python 'E:\BurningTimes\공유\기획\FirstRising\몬스터보스\tools\unity_eval\logscan.py' scan wsmtest | ForEach-Object { Log $_ } +Log 'DONE' diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/start_daily.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/start_daily.cs new file mode 100644 index 0000000..32551a5 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/start_daily.cs @@ -0,0 +1,4 @@ +var mapJson = Fx.TableManager.Instance.GetTable().GetJsons().Find(j => j.GameType == Fx.eGameType.DailyChallenge); +if (mapJson == null) return "no daily map"; +Fx.GameHub.Instance.StartGame(mapJson.Index, false, (ack) => { UnityEngine.Debug.Log("[TV] StartGame ack " + ack.result); if (ack.result == Protocol.eErrorType.SUCCESS) Fx.LobbyScene.Instance.StartGame(mapJson); }); +return "requested index=" + mapJson.Index + " prefab=" + mapJson.Prefab; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/tp_to_monster.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/tp_to_monster.cs new file mode 100644 index 0000000..ba94ff2 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/tp_to_monster.cs @@ -0,0 +1,10 @@ +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; if (w == null || !w.IsCreated) return "no world"; +var em = w.EntityManager; var p = Fx.Player.Instance; if (p == null || p.characterEntity == Unity.Entities.Entity.Null) return "no player entity"; +var q = em.CreateEntityQuery(new Unity.Entities.EntityQueryDesc { All = new[] { Unity.Entities.ComponentType.ReadOnly(), Unity.Entities.ComponentType.ReadOnly(), Unity.Entities.ComponentType.ReadOnly() }, None = new[] { Unity.Entities.ComponentType.ReadOnly() } }); +var me = em.GetComponentData(p.characterEntity); +float best = float.MaxValue; Unity.Mathematics.float3 pos = default; int count = 0; +using (var ents = q.ToEntityArray(Unity.Collections.Allocator.Temp)) { foreach (var e in ents) { var h = em.GetComponentData(e); if (h.health <= 0) continue; count++; var lt = em.GetComponentData(e); float d = Unity.Mathematics.math.distance(lt.Position, me.Position); if (d < best) { best = d; pos = lt.Position; } } } +q.Dispose(); +if (count == 0) return "no living monsters"; +me.Position = pos + new Unity.Mathematics.float3(1.6f, 0f, 0f); em.SetComponentData(p.characterEntity, me); +return "teleported near monster (was " + best.ToString("F1") + " m away) living=" + count; \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_check.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_check.cs new file mode 100644 index 0000000..d9ffb02 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_check.cs @@ -0,0 +1,45 @@ +// 필드 이동 뒤 상태 — 있는 필드 · 맵만 바꿨는지 · 플레이어 자리 · 몬스터 수 · 읽힌 씬 수 · 분위기 +var sb = new System.Text.StringBuilder(); +var t = BurningTimes.Visual.TVFieldTravel.Instance; +sb.Append("key=").Append(t != null ? t.currentKey : "-").Append(" traveling=").Append(BurningTimes.Visual.TVFieldTravel.traveling).Append(" inPlace=").Append(BurningTimes.Visual.TVFieldTravel.lastSwappedInPlace); +sb.Append(" kit=").Append(t != null && t.kit != null ? t.kit.name : "none"); +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; +if (w == null || !w.IsCreated) return sb.Append(" no world").ToString(); +var em = w.EntityManager; +var p = Fx.Player.Instance; +if (p != null && em.Exists(p.characterEntity)) +{ + var lt = em.GetComponentData(p.characterEntity); + sb.Append(" playerEntity=(").Append(lt.Position.x.ToString("F1")).Append(",").Append(lt.Position.y.ToString("F1")).Append(",").Append(lt.Position.z.ToString("F1")).Append(")"); + var gp = p.GetTransform().position; + sb.Append(" playerObject=(").Append(gp.x.ToString("F1")).Append(",").Append(gp.z.ToString("F1")).Append(")"); + var h = em.GetComponentData(p.characterEntity); + sb.Append(" hp=").Append(h.health).Append("/").Append(h.healthMax); +} +else sb.Append(" player=none"); +using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) +using (var arr = q.ToComponentDataArray(Unity.Collections.Allocator.Temp)) +{ + sb.Append(" starts=").Append(arr.Length); + for (int i = 0; i < arr.Length; i++) sb.Append("(").Append(arr[i].position.x.ToString("F1")).Append(",").Append(arr[i].position.z.ToString("F1")).Append(")"); +} +int alive = 0, total = 0; +using (var q = em.CreateEntityQuery(new Unity.Entities.EntityQueryDesc { All = new[] { Unity.Entities.ComponentType.ReadOnly(), Unity.Entities.ComponentType.ReadOnly() }, None = new[] { Unity.Entities.ComponentType.ReadOnly() } })) +using (var hs = q.ToComponentDataArray(Unity.Collections.Allocator.Temp)) +{ + total = hs.Length; + for (int i = 0; i < hs.Length; i++) if (hs[i].health > 0) alive++; +} +sb.Append(" monsters=").Append(alive).Append("/").Append(total); +using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) sb.Append(" sceneRefs=").Append(q.CalculateEntityCount()); +using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) sb.Append(" gems=").Append(q.CalculateEntityCount()); +using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly(), Unity.Entities.ComponentType.ReadOnly())) sb.Append(" renderEntities=").Append(q.CalculateEntityCount()); +sb.Append(" streaming=").Append(Fx.SceneManagementSystem.IsSceneStreaming(w)); +var fc = UnityEngine.RenderSettings.fogColor; +sb.Append(" fog=(").Append(fc.r.ToString("F2")).Append(",").Append(fc.g.ToString("F2")).Append(",").Append(fc.b.ToString("F2")).Append(") ").Append(UnityEngine.RenderSettings.fogStartDistance.ToString("F0")).Append("~").Append(UnityEngine.RenderSettings.fogEndDistance.ToString("F0")); +var amb = UnityEngine.RenderSettings.ambientSkyColor; +sb.Append(" ambSky=(").Append(amb.r.ToString("F2")).Append(",").Append(amb.g.ToString("F2")).Append(",").Append(amb.b.ToString("F2")).Append(")"); +int zonesOn = 0; +foreach (var z in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsSortMode.None)) if (z.isActiveAndEnabled) zonesOn++; +sb.Append(" zonesOn=").Append(zonesOn).Append(" terrains=").Append(UnityEngine.Terrain.activeTerrains.Length).Append(" ts=").Append(UnityEngine.Time.timeScale); +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_hook.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_hook.cs new file mode 100644 index 0000000..7c71fc4 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_hook.cs @@ -0,0 +1,71 @@ +// 필드 이동 구간별 시간 실측용 — 상태가 바뀌는 프레임과 긴 프레임을 %TEMP%\bt_evals\travel_timeline.log 에 적는다(진단 전용 · 게임에 남기지 않는다) +var domain = System.AppDomain.CurrentDomain; +var oldHook = domain.GetData("bt.travel.hook") as UnityEngine.Events.UnityAction; +if (oldHook != null) UnityEngine.Application.onBeforeRender -= oldHook; +var oldLoaded = domain.GetData("bt.travel.loaded") as UnityEngine.Events.UnityAction; +if (oldLoaded != null) UnityEngine.SceneManagement.SceneManager.sceneLoaded -= oldLoaded; +var oldUnloaded = domain.GetData("bt.travel.unloaded") as UnityEngine.Events.UnityAction; +if (oldUnloaded != null) UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= oldUnloaded; +var oldLog = domain.GetData("bt.travel.log") as UnityEngine.Application.LogCallback; +if (oldLog != null) UnityEngine.Application.logMessageReceived -= oldLog; +var oldWriter = domain.GetData("bt.travel.writer") as System.IO.StreamWriter; +if (oldWriter != null) { try { oldWriter.Dispose(); } catch (System.Exception) { } } + +var file = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "bt_evals", "travel_timeline.log"); +var writer = new System.IO.StreamWriter(file, true, new System.Text.UTF8Encoding(false)) { AutoFlush = true }; +System.Action write = (m) => writer.WriteLine(string.Format("{0,9:F3} f{1,-6} {2}", UnityEngine.Time.realtimeSinceStartup, UnityEngine.Time.frameCount, m)); +write("=== hook installed " + System.DateTime.Now.ToString("HH:mm:ss") + " ==="); + +string lastState = ""; +float lastTime = -1f; +UnityEngine.CanvasGroup fade = null; +UnityEngine.Events.UnityAction hook = () => +{ + float now = UnityEngine.Time.realtimeSinceStartup; + if (lastTime >= 0f && now - lastTime > 0.15f) + write("LONGFRAME " + (now - lastTime).ToString("F2") + "s"); + lastTime = now; + bool traveling = BurningTimes.Visual.TVFieldTravel.traveling; + var world = Unity.Entities.World.DefaultGameObjectInjectionWorld; + int players = -1, characters = -1, sceneRefs = -1; bool streaming = false; + if (world != null && world.IsCreated) + { + var em = world.EntityManager; + using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) players = q.CalculateEntityCount(); + using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) characters = q.CalculateEntityCount(); + using (var q = em.CreateEntityQuery(Unity.Entities.ComponentType.ReadOnly())) sceneRefs = q.CalculateEntityCount(); + streaming = Fx.SceneManagementSystem.IsSceneStreaming(world); + } + if (traveling && fade == null) + { + var go = UnityEngine.GameObject.Find("FieldTravelFade"); + if (go != null) fade = go.GetComponent(); + } + string alpha = (fade != null) ? fade.alpha.ToString("F1") : "-"; + string state = "trav=" + traveling + " scene=" + UnityEngine.SceneManagement.SceneManager.GetActiveScene().name + + " player=" + (Fx.Player.Instance != null) + " ptag=" + players + " chars>0=" + (characters > 0) + " sceneRefs=" + sceneRefs + " streaming=" + streaming + " fade=" + alpha + + " lobby=" + Fx.LobbyScene.HasInstance() + " ts=" + UnityEngine.Time.timeScale; + if (state != lastState) + { + lastState = state; + write(state + " chars=" + characters); + } +}; +UnityEngine.Events.UnityAction loaded = (s, m) => write("SCENE LOADED " + s.name + " (" + m + ")"); +UnityEngine.Events.UnityAction unloaded = (s) => write("SCENE UNLOADED " + s.name); +UnityEngine.Application.LogCallback log = (c, st, t) => +{ + if (c.StartsWith("There are ")) return; + var m = c.Length > 160 ? c.Substring(0, 160) : c; + write("LOG[" + t + "] " + m.Replace('\n', ' ')); +}; +UnityEngine.Application.onBeforeRender += hook; +UnityEngine.SceneManagement.SceneManager.sceneLoaded += loaded; +UnityEngine.SceneManagement.SceneManager.sceneUnloaded += unloaded; +UnityEngine.Application.logMessageReceived += log; +domain.SetData("bt.travel.hook", hook); +domain.SetData("bt.travel.loaded", loaded); +domain.SetData("bt.travel.unloaded", unloaded); +domain.SetData("bt.travel.log", log); +domain.SetData("bt.travel.writer", writer); +return "hook installed -> " + file; diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_state.cs new file mode 100644 index 0000000..cc414c7 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_state.cs @@ -0,0 +1,8 @@ +// 필드 이동 상태 — 목록 · 지금 있는 필드 · 옮기는 중인지 +var t = BurningTimes.Visual.TVFieldTravel.Instance; +if (t == null) return "no TVFieldTravel traveling=" + BurningTimes.Visual.TVFieldTravel.traveling; +var sb = new System.Text.StringBuilder(); +sb.Append("traveling=").Append(BurningTimes.Visual.TVFieldTravel.traveling).Append(" current=").Append(t.currentKey).Append(" fields="); +for (int i = 0; i < t.fieldCount; i++) sb.Append(t.GetField(i).key).Append(i < t.fieldCount - 1 ? "," : ""); +sb.Append(" player=").Append(Fx.Player.Instance != null).Append(" ts=").Append(UnityEngine.Time.timeScale); +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_unhook.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_unhook.cs new file mode 100644 index 0000000..84d7ca9 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/travel_unhook.cs @@ -0,0 +1,18 @@ +// 필드 이동 실측용 훅을 떼고 기록 파일을 닫는다 +var domain = System.AppDomain.CurrentDomain; +var oldHook = domain.GetData("bt.travel.hook") as UnityEngine.Events.UnityAction; +if (oldHook != null) UnityEngine.Application.onBeforeRender -= oldHook; +var oldLoaded = domain.GetData("bt.travel.loaded") as UnityEngine.Events.UnityAction; +if (oldLoaded != null) UnityEngine.SceneManagement.SceneManager.sceneLoaded -= oldLoaded; +var oldUnloaded = domain.GetData("bt.travel.unloaded") as UnityEngine.Events.UnityAction; +if (oldUnloaded != null) UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= oldUnloaded; +var oldLog = domain.GetData("bt.travel.log") as UnityEngine.Application.LogCallback; +if (oldLog != null) UnityEngine.Application.logMessageReceived -= oldLog; +var oldWriter = domain.GetData("bt.travel.writer") as System.IO.StreamWriter; +if (oldWriter != null) { try { oldWriter.Dispose(); } catch (System.Exception) { } } +domain.SetData("bt.travel.hook", null); +domain.SetData("bt.travel.loaded", null); +domain.SetData("bt.travel.unloaded", null); +domain.SetData("bt.travel.log", null); +domain.SetData("bt.travel.writer", null); +return "hook removed (had hook=" + (oldHook != null) + ")"; diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/visible_probe.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/visible_probe.cs new file mode 100644 index 0000000..071de65 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/visible_probe.cs @@ -0,0 +1,50 @@ +// 메인 카메라(게임 화면을 그리는 카메라)가 지금 그리는 것 — 게임 오브젝트 렌더러와 엔티티 렌더러를 레이어 · 묶음별로 센다 +var sb = new System.Text.StringBuilder(); +UnityEngine.Camera cam = (Fx.WorldManager.HasInstance() && Fx.WorldManager.Instance.mainCamera != null) ? Fx.WorldManager.Instance.mainCamera : UnityEngine.Camera.main; +if (cam == null) return "no main camera"; +int mask = cam.cullingMask; +sb.Append("CAM ").Append(cam.name).Append(" widgetCullingVisible=").Append((mask & (1 << 29)) != 0).Append("\n"); +System.Func ln = (i) => { var n = UnityEngine.LayerMask.LayerToName(i); return string.IsNullOrEmpty(n) ? i.ToString() : n; }; +System.Func prefix = (t, depth) => { var st = new System.Collections.Generic.List(); while (t != null) { st.Add(t.name); t = t.parent; } st.Reverse(); return string.Join("/", st.GetRange(0, System.Math.Min(depth, st.Count))); }; +var groups = new System.Collections.Generic.SortedDictionary(); +foreach (var r in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None)) +{ + if (r.enabled == false || r.forceRenderingOff) continue; + string vis = ((mask & (1 << r.gameObject.layer)) != 0) ? "SHOWN " : "hidden"; + string key = vis + " GO " + ln(r.gameObject.layer) + " | " + r.GetType().Name + " | " + prefix(r.transform, 4); + int n; groups.TryGetValue(key, out n); groups[key] = n + 1; +} +foreach (var kv in groups) sb.Append(kv.Key).Append(" x").Append(kv.Value).Append("\n"); +// 지형(Terrain)은 렌더러가 아니다 +foreach (var t in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None)) + sb.Append(((mask & (1 << t.gameObject.layer)) != 0) ? "SHOWN " : "hidden").Append(" TERRAIN ").Append(ln(t.gameObject.layer)).Append(" | ").Append(prefix(t.transform, 5)).Append(" draw=").Append(t.drawHeightmap).Append("\n"); +var world = Unity.Entities.World.DefaultGameObjectInjectionWorld; +bool worldOk = world != null && world.IsCreated; if (worldOk == false) sb.Append("no world"); if (worldOk) { +var em = world.EntityManager; +var q = em.CreateEntityQuery(new Unity.Entities.EntityQueryDesc { All = new[] { Unity.Entities.ComponentType.ReadOnly(), Unity.Entities.ComponentType.ReadOnly() }, None = new[] { Unity.Entities.ComponentType.ReadOnly() } }); +var fh = em.GetSharedComponentTypeHandle(); +var sh = em.GetSharedComponentTypeHandle(); +var eh = em.GetEntityTypeHandle(); +var eg = new System.Collections.Generic.SortedDictionary(); +var samples = new System.Collections.Generic.Dictionary(); +using (var chunks = q.ToArchetypeChunkArray(Unity.Collections.Allocator.Temp)) +{ + for (int i = 0; i < chunks.Length; i++) + { + var ch = chunks[i]; + var f = ch.GetSharedComponent(fh); + string sec = ch.Has(sh) ? ch.GetSharedComponent(sh).SceneGUID.ToString().Substring(0, 8) : "runtime"; + string vis = ((mask & (1 << f.Layer)) != 0) ? "SHOWN " : "hidden"; + string key = vis + " ENT " + ln(f.Layer) + " | scene=" + sec; + int n; eg.TryGetValue(key, out n); eg[key] = n + ch.Count; + if (samples.ContainsKey(key) == false) + { + var ents = ch.GetNativeArray(eh); var names = new System.Collections.Generic.List(); + for (int k = 0; k < ents.Length && k < 4; k++) names.Add(em.GetName(ents[k])); + samples[key] = string.Join(", ", names); + } + } +} +foreach (var kv in eg) sb.Append(kv.Key).Append(" x").Append(kv.Value).Append(" e.g. ").Append(samples[kv.Key]).Append("\n"); +q.Dispose(); } +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_anim.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_anim.cs new file mode 100644 index 0000000..b95833f --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_anim.cs @@ -0,0 +1,6 @@ +var sb = new System.Text.StringBuilder(); +var p = Fx.Player.Instance; if (p == null) return "no player"; +var names = new System.Collections.Generic.List { "Idle", "EmptyState" }; foreach (var pre in new[]{"Bow","Sword","Blunt","Dagger","MagicStick","MagicDagger"}) for (int i = 0; i < 6; i++) names.Add(pre + i); +sb.Append("weaponId=").Append(p.attachedWeaponId).Append(" skillId=").Append(p.attachedWeaponSkillId).Append(" elements=").Append(p.weaponSkillElements.Count); +foreach (var e in p.weaponSkillElements) { string nm = "hash" + e.AnimStateHash; foreach (var n in names) if (UnityEngine.Animator.StringToHash(n) == e.AnimStateHash) nm = n; sb.Append(" [skill ").Append(e.skillBufferElement.skillId).Append(" anim=").Append(nm).Append(" range=").Append(e.skillBufferElement.targetingRange.ToString("F1")).Append("]"); } +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_state.cs new file mode 100644 index 0000000..1702ef6 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_state.cs @@ -0,0 +1,23 @@ +var sb = new System.Text.StringBuilder(); +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance; +System.Func field = (o, n) => { for (var t = o.GetType(); t != null; t = t.BaseType) { var f = t.GetField(n, nf | System.Reflection.BindingFlags.DeclaredOnly); if (f != null) return f.GetValue(o); var p = t.GetProperty(n, nf | System.Reflection.BindingFlags.DeclaredOnly); if (p != null) return p.GetValue(o); } return "?"; }; +sb.Append("playing=").Append(UnityEditor.EditorApplication.isPlaying).Append(" scene=").Append(UnityEngine.SceneManagement.SceneManager.GetActiveScene().name).Append(" ts=").Append(UnityEngine.Time.timeScale); +if (!UnityEditor.EditorApplication.isPlaying) return sb.ToString(); +var pm = Fx.PlayerManager.Instance; +sb.Append(" equipWeaponIndex=").Append(pm.GetEquipWeaponIndex()); +foreach (var id in pm.player.user.equipItems) { if (!pm.player.itemsDic.ContainsKey(id)) { sb.Append("\n EQUIP id=").Append(id).Append(" (not in itemsDic)"); continue; } var it = pm.player.itemsDic[id]; var j = it.itemJson; sb.Append("\n EQUIP id=").Append(id).Append(" idx=").Append(j.Index).Append(" equipType=").Append(j.EquipmentType).Append(" weaponType=").Append(j.WeaponType).Append(" skill=").Append(j.SkillIndex).Append(" grade=").Append(j.ItemGrade).Append(" prefab=").Append(field(j, "Prefab")); } +var lm = Fx.LobbyManager.HasInstance() ? Fx.LobbyManager.Instance : null; +sb.Append("\n LobbyManager=").Append(lm != null).Append(" lobbyPlayer=").Append(lm != null && lm.lobbyPlayer != null ? path(lm.lobbyPlayer.transform) + " weaponId=" + lm.lobbyPlayer.attachedWeaponId : "null"); +foreach (var pl in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) { + sb.Append("\n PLAYER ").Append(path(pl.transform)).Append(" active=").Append(pl.gameObject.activeInHierarchy).Append(" isInstance=").Append(object.ReferenceEquals(Fx.Player.Instance, pl)).Append(" weaponId=").Append(pl.attachedWeaponId).Append(" weaponType=").Append(field(pl, "m_AttachedWeaponType")).Append(" weaponSkillId=").Append(field(pl, "m_AttachedWeaponSkillId")).Append(" level=").Append(field(pl, "m_AttachedWeaponLevel")).Append(" attachedModels=").Append((field(pl, "m_AttachedWeapons") as System.Collections.ICollection)?.Count); + var an = pl.GetAnimator(); if (an != null) { sb.Append(" controller=").Append(an.runtimeAnimatorController != null ? an.runtimeAnimatorController.name : "null"); + var names = new System.Collections.Generic.List { "Idle", "EmptyState", "Roll", "Run", "Move", "Locomotion" }; foreach (var pre in new[]{"Bow","Sword","Blunt","Dagger","MagicStick","MagicDagger"}) for (int i = 0; i < 6; i++) names.Add(pre + i); + for (int l = 0; l < an.layerCount; l++) { var st = an.GetCurrentAnimatorStateInfo(l); string nm = "hash" + st.shortNameHash; foreach (var n in names) if (UnityEngine.Animator.StringToHash(n) == st.shortNameHash) nm = n; sb.Append(" L").Append(l).Append("(").Append(an.GetLayerName(l)).Append(" w=").Append(an.GetLayerWeight(l).ToString("F1")).Append(")=").Append(nm); } + foreach (var prm in an.parameters) if (prm.type == UnityEngine.AnimatorControllerParameterType.Float || prm.type == UnityEngine.AnimatorControllerParameterType.Int) sb.Append(" ").Append(prm.name).Append("=").Append(prm.type == UnityEngine.AnimatorControllerParameterType.Float ? an.GetFloat(prm.name).ToString("F2") : an.GetInteger(prm.name).ToString()); } +} +var gear = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); sb.Append("\n TVArcherGear=").Append(gear != null ? ("enabled=" + gear.isActiveAndEnabled + " hideGameBow=" + field(gear, "hideGameBow") + " bow=" + (field(gear, "bowPrefab") as UnityEngine.Object)?.name) : "none"); +var swap = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); sb.Append("\n TVCharacterSwap=").Append(swap != null ? ("enabled=" + swap.isActiveAndEnabled + " prefab=" + (field(swap, "characterPrefab") as UnityEngine.Object)?.name) : "none"); +var ov = UnityEngine.Object.FindFirstObjectByType(UnityEngine.FindObjectsInactive.Include); +if (ov != null) { sb.Append("\n TVAnimationOverride enabled=").Append(ov.isActiveAndEnabled).Append(" applied=").Append(ov.AppliedCount); var list = field(ov, "overrides") as System.Collections.IList; if (list != null) foreach (var o in list) sb.Append("\n OVERRIDE ").Append(field(o, "originalClip")).Append(" -> ").Append((field(o, "clip") as UnityEngine.Object)?.name); } +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_vis.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_vis.cs new file mode 100644 index 0000000..c478019 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/weapon_vis.cs @@ -0,0 +1,8 @@ +var sb = new System.Text.StringBuilder(); +var p = Fx.Player.Instance; if (p == null) return "no player"; +System.Func path = null; path = (t) => t.parent == null ? t.name : path(t.parent) + "/" + t.name; +string root = path(p.transform); +foreach (var mr in p.GetComponentsInChildren(true)) sb.Append("MESH ").Append(path(mr.transform).Replace(root, "~")).Append(" active=").Append(mr.gameObject.activeInHierarchy).Append(" hidden=").Append(mr.forceRenderingOff).Append("\n"); +var an = p.GetAnimator(); if (an != null) { var names = new System.Collections.Generic.List { "Idle", "EmptyState" }; foreach (var pre in new[]{"Bow","Sword","Blunt","Dagger","MagicStick","MagicDagger"}) for (int i = 0; i < 6; i++) names.Add(pre + i); + for (int l = 0; l < an.layerCount; l++) { var st = an.GetCurrentAnimatorStateInfo(l); string nm = "hash" + st.shortNameHash; foreach (var n in names) if (UnityEngine.Animator.StringToHash(n) == st.shortNameHash) nm = n; sb.Append("ANIM L").Append(l).Append("=").Append(nm).Append(" t=").Append(st.normalizedTime.ToString("F2")).Append(" "); } } +return sb.ToString(); \ No newline at end of file diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/widget_probe.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/widget_probe.cs new file mode 100644 index 0000000..0fcb14f --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/widget_probe.cs @@ -0,0 +1,45 @@ +var sb = new System.Text.StringBuilder(); +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +if (Fx.DesktopWidgetManager.HasInstance() == false) return "no DesktopWidgetManager"; +var wm = Fx.DesktopWidgetManager.Instance; +var root = typeof(Fx.DesktopWidgetManager).GetField("m_WidgetRoot", nf).GetValue(wm) as UnityEngine.GameObject; +sb.Append("WIDGET state=").Append(wm.state).Append(" diameter=").Append(wm.widgetDiameter).Append(" simulate=").Append(wm.simulateInEditor) + .Append(" canEnter=").Append(wm.CanEnterWidget()).Append(" root=").Append(root != null ? root.name + "(active=" + root.activeSelf + ")" : "null") + .Append(" fps=").Append(UnityEngine.Application.targetFrameRate).Append(" quality=").Append(UnityEngine.QualitySettings.GetQualityLevel()).Append("\n"); +System.Func path = (t) => { var s = t.name; int d = 0; while (t.parent != null && d < 6) { t = t.parent; s = t.name + "/" + s; d++; } return s; }; +System.Func maskNames = (m) => { var l = new System.Collections.Generic.List(); for (int i = 0; i < 32; i++) if ((m & (1 << i)) != 0) { var n = UnityEngine.LayerMask.LayerToName(i); l.Add(string.IsNullOrEmpty(n) ? i.ToString() : n); } return string.Join(",", l); }; +foreach (var c in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) +{ + sb.Append("CAM ").Append(path(c.transform)).Append(" on=").Append(c.isActiveAndEnabled).Append(" depth=").Append(c.depth).Append(" clear=").Append(c.clearFlags) + .Append(" bg=").Append(c.backgroundColor).Append(" rt=").Append(c.targetTexture != null ? c.targetTexture.name + " " + c.targetTexture.width + "x" + c.targetTexture.height + " " + c.targetTexture.format : "screen") + .Append(" hidden=["); + int all = ~0; var hidden = new System.Collections.Generic.List(); + for (int i = 0; i < 32; i++) { var n = UnityEngine.LayerMask.LayerToName(i); if (string.IsNullOrEmpty(n) == false && (c.cullingMask & (1 << i)) == 0) hidden.Add(n); } + sb.Append(string.Join(",", hidden)).Append("]\n"); +} +foreach (var h in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) +{ + var targets = typeof(Fx.HideOnWidgetMode).GetField("m_Targets", nf).GetValue(h) as UnityEngine.GameObject[]; + var shows = typeof(Fx.HideOnWidgetMode).GetField("m_ShowTargets", nf).GetValue(h) as UnityEngine.GameObject[]; + sb.Append("HIDE ").Append(path(h.transform)).Append(" active=").Append(h.gameObject.activeInHierarchy).Append(" targets=["); + if (targets != null) foreach (var t in targets) if (t != null) sb.Append(t.name).Append(t.activeSelf ? "+" : "-").Append(' '); + sb.Append("] show=["); + if (shows != null) foreach (var t in shows) if (t != null) sb.Append(t.name).Append(t.activeSelf ? "+" : "-").Append(' '); + sb.Append("]\n"); +} +foreach (var cv in UnityEngine.Object.FindObjectsByType(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None)) +{ + if (cv.isRootCanvas == false) continue; + var r = cv.GetComponent(); + sb.Append("CANVAS ").Append(path(cv.transform)).Append(" on=").Append(cv.isActiveAndEnabled).Append(" mode=").Append(cv.renderMode).Append(" order=").Append(cv.sortingOrder) + .Append(" scale=").Append(cv.scaleFactor.ToString("F3")).Append(" size=").Append(r.rect.width.ToString("F0")).Append("x").Append(r.rect.height.ToString("F0")).Append(" px=").Append(cv.pixelRect.width.ToString("F0")).Append("x").Append(cv.pixelRect.height.ToString("F0")).Append("\n"); +} +var vlc = UnityEngine.Object.FindFirstObjectByType(); +if (vlc != null) +{ + var hp = vlc.holderParent; var corners = new UnityEngine.Vector3[4]; hp.GetWorldCorners(corners); + sb.Append("VIEWPORT holder=").Append(path(hp)).Append(" rect=").Append(hp.rect.width.ToString("F0")).Append("x").Append(hp.rect.height.ToString("F0")) + .Append(" screen=(").Append(corners[0].x.ToString("F0")).Append(",").Append(corners[0].y.ToString("F0")).Append(")~(").Append(corners[2].x.ToString("F0")).Append(",").Append(corners[2].y.ToString("F0")).Append(")\n"); +} +sb.Append("SCREEN ").Append(UnityEngine.Screen.width).Append("x").Append(UnityEngine.Screen.height); +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_diag.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_diag.cs new file mode 100644 index 0000000..f7be38e --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_diag.cs @@ -0,0 +1,29 @@ +// 창 크기 메뉴 진단 — 코루틴 · 화면 묶음 · 사각형과 커서의 관계 +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +var sm = BurningTimes.Visual.TVWidgetSizeMenu.Instance; +if (sm == null) return "no menu instance"; +var ty = typeof(BurningTimes.Visual.TVWidgetSizeMenu); +var sb = new System.Text.StringBuilder(); +var run = ty.GetField("m_Run", nf).GetValue(sm); +var host = ty.GetField("m_HostGO", nf).GetValue(sm) as UnityEngine.GameObject; +var panel = ty.GetField("m_Panel", nf).GetValue(sm) as UnityEngine.GameObject; +var zone = ty.GetField("m_Zone", nf).GetValue(sm) as UnityEngine.RectTransform; +var track = ty.GetField("m_Track", nf).GetValue(sm) as UnityEngine.RectTransform; +sb.Append("enabled=").Append(sm.isActiveAndEnabled).Append(" run=").Append(run != null).Append(" host=").Append(host != null ? (host.activeSelf + "/" + host.activeInHierarchy) : "null") + .Append(" panel=").Append(panel != null ? panel.activeSelf.ToString() : "null"); +var mouse = UnityEngine.InputSystem.Mouse.current; +var p = mouse != null ? mouse.position.ReadValue() : default(UnityEngine.Vector2); +sb.Append(" mouse=(").Append(p.x.ToString("F0")).Append(",").Append(p.y.ToString("F0")).Append(")"); +System.Action dump = (n, r) => +{ + if (r == null) { sb.Append(" ").Append(n).Append("=null"); return; } + var c = new UnityEngine.Vector3[4]; r.GetWorldCorners(c); + sb.Append(" ").Append(n).Append("=[").Append(c[0].x.ToString("F0")).Append(",").Append(c[0].y.ToString("F0")).Append(" ~ ").Append(c[2].x.ToString("F0")).Append(",").Append(c[2].y.ToString("F0")).Append("] z=").Append(c[0].z.ToString("F1")) + .Append(" contains=").Append(UnityEngine.RectTransformUtility.RectangleContainsScreenPoint(r, p, null)); + var cv = r.GetComponentInParent(true); + sb.Append(" canvas=").Append(cv != null ? (cv.rootCanvas.name + ":" + cv.rootCanvas.renderMode) : "none"); +}; +dump("zone", zone); +dump("track", track); +sb.Append(" widget=").Append(Fx.DesktopWidgetManager.Instance.isWidget).Append(" frame=").Append(UnityEngine.Time.frameCount); +return sb.ToString(); diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_pointer.template.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_pointer.template.cs new file mode 100644 index 0000000..d39a647 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_pointer.template.cs @@ -0,0 +1,48 @@ +// 시험용 — 마우스 위치와 버튼 상태를 넣는다. TARGET: zone | panel | away | track:<0~1> | off · BUTTON: 0 | 1 +// 입력 시스템은 에디터 갱신과 게임 갱신의 상태를 따로 들고 있다 — eval(에디터 갱신)에서 바로 바꾸면 게임 쪽에는 보이지 않는다. +// 실제 마우스가 조금이라도 움직이면 입력 갱신 때 값이 덮인다 — 그래서 게임의 입력 갱신 직후마다 상태를 넣는 훅을 걸어 둔다. TARGET 이 off 면 훅을 뗀다 +var domain = System.AppDomain.CurrentDomain; +var old = domain.GetData("bt.wsm.hook") as System.Action; +string target = "__TARGET__"; +int button = __BUTTON__; +if (target == "off") +{ + if (old != null) UnityEngine.InputSystem.InputSystem.onAfterUpdate -= old; + domain.SetData("bt.wsm.hook", null); + return "pointer hook removed (had=" + (old != null) + ")"; +} +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +var sm = BurningTimes.Visual.TVWidgetSizeMenu.Instance; +if (sm == null) return "no menu"; +System.Func rect = (n) => typeof(BurningTimes.Visual.TVWidgetSizeMenu).GetField(n, nf).GetValue(sm) as UnityEngine.RectTransform; +UnityEngine.Vector2 p; +if (target == "away") p = new UnityEngine.Vector2(5f, 5f); +else +{ + var r = rect(target.StartsWith("track") ? "m_Track" : (target == "panel" ? "m_PanelRect" : "m_Zone")); + if (r == null) return "no rect (menu not built yet)"; + var c = new UnityEngine.Vector3[4]; r.GetWorldCorners(c); + float tx = 0.5f; + if (target.StartsWith("track:")) tx = float.Parse(target.Substring(6), System.Globalization.CultureInfo.InvariantCulture); + p = new UnityEngine.Vector2(UnityEngine.Mathf.Lerp(c[0].x, c[2].x, tx), (c[0].y + c[2].y) * 0.5f); + if (target == "panel") p.x = UnityEngine.Mathf.Lerp(c[0].x, c[2].x, 0.06f); +} +domain.SetData("bt.wsm.x", p.x); +domain.SetData("bt.wsm.y", p.y); +domain.SetData("bt.wsm.button", button); +if (old == null) +{ + System.Action hook = () => + { + var mouse = UnityEngine.InputSystem.Mouse.current; + if (mouse == null) return; + if (UnityEngine.InputSystem.LowLevel.InputState.currentUpdateType != UnityEngine.InputSystem.LowLevel.InputUpdateType.Dynamic) return; + var d = System.AppDomain.CurrentDomain; + var state = new UnityEngine.InputSystem.LowLevel.MouseState { position = new UnityEngine.Vector2((float)d.GetData("bt.wsm.x"), (float)d.GetData("bt.wsm.y")) }; + if ((int)d.GetData("bt.wsm.button") == 1) state = state.WithButton(UnityEngine.InputSystem.LowLevel.MouseButton.Left, true); + UnityEngine.InputSystem.LowLevel.InputState.Change(mouse, state); + }; + UnityEngine.InputSystem.InputSystem.onAfterUpdate += hook; + domain.SetData("bt.wsm.hook", hook); +} +return "pointer " + target + " -> (" + p.x.ToString("F0") + "," + p.y.ToString("F0") + ") button=" + button + " (게임 루프 훅)"; diff --git a/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_state.cs b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_state.cs new file mode 100644 index 0000000..79368a4 --- /dev/null +++ b/공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/wsm_state.cs @@ -0,0 +1,37 @@ +// 최소화 모드 — 배경 숨김과 창 크기 메뉴의 상태 +var sb = new System.Text.StringBuilder(); +var nf = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance; +var wm = Fx.DesktopWidgetManager.HasInstance() ? Fx.DesktopWidgetManager.Instance : null; +sb.Append("widget=").Append(wm != null && wm.isWidget).Append(" diameter=").Append(wm != null ? wm.widgetDiameter : -1); +var sc = BurningTimes.Visual.TVWidgetScenery.Instance; +sb.Append(" | scenery=").Append(sc != null ? ("hidden=" + sc.hidden + " entities=" + sc.hiddenEntityCount) : "none"); +if (sc != null) +{ + var rs = typeof(BurningTimes.Visual.TVWidgetScenery).GetField("m_Renderers", nf).GetValue(sc) as System.Collections.Generic.List; + var ts = typeof(BurningTimes.Visual.TVWidgetScenery).GetField("m_Terrains", nf).GetValue(sc) as System.Collections.ICollection; + sb.Append(" renderers=").Append(rs != null ? rs.Count : -1).Append(" terrains=").Append(ts != null ? ts.Count : -1); +} +var w = Unity.Entities.World.DefaultGameObjectInjectionWorld; +if (w != null && w.IsCreated) +{ + using (var q = w.EntityManager.CreateEntityQuery(new Unity.Entities.EntityQueryDesc { All = new[] { Unity.Entities.ComponentType.ReadOnly() }, Options = Unity.Entities.EntityQueryOptions.IncludeDisabledEntities })) + sb.Append(" tagged=").Append(q.CalculateEntityCount()); +} +int drawn = 0; +foreach (var t in UnityEngine.Terrain.activeTerrains) if (t.drawHeightmap) drawn++; +sb.Append(" terrainsDrawn=").Append(drawn).Append("/").Append(UnityEngine.Terrain.activeTerrains.Length); +var sm = BurningTimes.Visual.TVWidgetSizeMenu.Instance; +if (sm == null) return sb.Append(" | menu=none").ToString(); +sb.Append(" | menu size=").Append(sm.size.ToString("F2")).Append(" visible=").Append(sm.menuVisible); +var area = typeof(BurningTimes.Visual.TVWidgetSizeMenu).GetField("m_Area", nf).GetValue(sm) as UnityEngine.RectTransform; +if (area != null) +{ + var c = new UnityEngine.Vector3[4]; area.GetWorldCorners(c); + sb.Append(" areaScale=").Append(area.localScale.x.ToString("F2")).Append(" areaPx=").Append((c[2].x - c[0].x).ToString("F0")).Append("x").Append((c[2].y - c[0].y).ToString("F0")); +} +var dragging = (bool)typeof(BurningTimes.Visual.TVWidgetSizeMenu).GetField("m_Dragging", nf).GetValue(sm); +sb.Append(" dragging=").Append(dragging); +var mouse = UnityEngine.InputSystem.Mouse.current; +if (mouse != null) { var p = mouse.position.ReadValue(); sb.Append(" mouse=(").Append(p.x.ToString("F0")).Append(",").Append(p.y.ToString("F0")).Append(") down=").Append(mouse.leftButton.isPressed); } +sb.Append(" pref=").Append(UnityEngine.PlayerPrefs.GetFloat("BT_WidgetSize", -1f).ToString("F2")); +return sb.ToString(); diff --git a/공유/대화로그/FirstRising/2026-09-29.md b/공유/대화로그/FirstRising/2026-09-29.md new file mode 100644 index 0000000..27adb67 --- /dev/null +++ b/공유/대화로그/FirstRising/2026-09-29.md @@ -0,0 +1,34 @@ +# FirstRising 대화로그 — 2026-09-29 (개발팀 세션 · BT42) + +## BT42-FirstRising · 필드에서 레벨업(스킬 카드 3장 선택) 비활성화 + +### PD 지시(원문) +- (09-28 23:27) "지금 내 에디터에서 갑자기 게임이 멈추고, 화면이 블러처리 된 것 같은데 왜 그런지 체크해봐." +- (09-28 23:5x) "기존 레벨업 시스템(레벨업 시 스킬 카드 3가지 중 선택)은 특정 컨텐츠에서만 사용 될 예정이야. 따라서, 필드 형태의 맵에서는 해당 레벨업 기능을 비활성화 시켜줘." +- Play 종료 질문 2회: ① "지금 종료하고 진행" ② (PD 가 다시 Play 를 켠 뒤) "제가 Play 를 끄고 바로 마무리" + +### 원인(실측 · PD Play 를 읽기만 함) +- `Time.timeScale = 0` · 에디터 일시정지 아님 · 에디터 로그 최근 60 MB 에 오류 · 예외 0(리스너/이벤트 시스템 중복 경고만). +- 활성 창: `Lobby/LobbyPlayScene/LevelEssential/GameCanvas/CenterHolder/CommonSkillSelectUI`(pauseGame) — 젬 레벨 1→2(경험치 550/500) 레벨업으로 열림. +- 흐림: 같은 창이 `RuntimePostProcessVolumeController.ActiveDOF(true)` 를 불러 피사계 심도(Bokeh · 초점 0.1)를 켬. +- 안 보인 이유: GameCanvas 는 그리는 순서 0, 게임 화면 그림(`Lobby/GamePlayCanvas/CenterHolder/ViewportLayoutController/RawImage` · ViewportRT)은 순서 200 → 창이 게임 화면 아래에 깔림. 카드 자리를 누르면 RawImage 가 먼저 맞는다(EventSystem.RaycastAll). +- 순서 200 은 09-22 bluecell2 "위젯 작업"에서 정해진 게임 원본 설정. 전투가 로비 씬 안에서 이루어지면서(필드 이동 BT39) 드러남. + +### 결정(PM) +- 끄는 자리: 경험치가 쌓이는 곳(`Character.AddGemScore`). 레벨업 판정(`UpdateExp`)에서 막으면 경험치가 몰래 쌓였다가 스위치를 켜는 순간 레벨업이 몰려 터진다. +- 스위치: `PlayerManager.gemLevelUp`(기본 켜짐) — 기존 `startFromLobby` 와 같은 형태. 정적 값이 아니라 Play 마다 새로 만들어져 초기화 걱정이 없다. +- 끄는 주체: BT_Visual 의 `TVFieldLevelUpGate` — 켜질 때 끄고 꺼질 때 되돌린다(매 프레임 하는 일 없음). BT_Visual 은 로비 씬에만 있으므로 스테이지 등 다른 컨텐츠는 그대로다. +- 범위 판단: 로비 씬에서 진행되는 전투 전체(기본 필드 · 필드 이동 맵 · 보스전 전용 필드). 보스전도 같은 화면 구조라 창이 가려지므로 함께 껐다 — PD 확인 필요. + +### 결과 +- 게임 코드 2곳(날짜 · 이유 한글 주석): `Assets/Scripts/Manager/PlayerManager.cs`(속성 추가) · `Assets/Scripts/Fx/Character/Character.cs`(AddGemScore 앞머리). +- 새 파일 `Assets/BurningTimes/Visual/Scripts/TVFieldLevelUpGate.cs` + `LobbyPlayScene.prefab` 의 BT_Visual 에 부착(프리팹 변경은 구성 요소 추가 14줄뿐). +- 검증(Play 1회 · 00:10~00:12): ① 필드에서 경험치 600 투입 → 경험치 0 유지 · 창 없음 · 시간 1 · 흐림 없음 ② 스위치를 되돌리면 원래대로(창 · 시간 0 · 흐림 · 젬 레벨 2) → 카드 선택으로 재개 ③ 다시 끄면 경험치 변화 없음 ④ 일일 도전(스테이지): 스위치 켜짐 · 레벨업 · 카드 선택 정상 ⑤ 로비 복귀 뒤 다시 꺼짐. 에디터 로그 오류 0(logscan · 6.8 MB). 저장 파일 3개 시험 전 백업 → 원복. +- 한글 주석 점검(comment_scan): 새 파일 선언 5개 중 누락 0. +- 커밋하지 않음(PD 지시 시). + +### 남은 이슈 +- 보스전 전용 필드에서도 레벨업이 꺼짐 — PD 확인 필요. +- 같은 GameCanvas 의 다른 창(사망 시 결과/부활 창 · 스킬북 결과 · 게임 메뉴 · 보스 등장 알림)은 로비 씬에서 여전히 게임 화면 아래에 깔린다(미수정). 필드에서 플레이어가 쓰러지면 같은 '멈춤'이 날 수 있다(미실측). +- 필드에서도 경험치 젬은 떨어지고 주워진다(효과만 없음). +- 에디터의 `L` 키(UIManager 에디터 전용)는 스킬 선택 창을 억지로 연다 — 필드에서 누르면 같은 멈춤이 난다. diff --git a/공유/대화로그/FirstRising/2026-09-30.md b/공유/대화로그/FirstRising/2026-09-30.md new file mode 100644 index 0000000..d562148 --- /dev/null +++ b/공유/대화로그/FirstRising/2026-09-30.md @@ -0,0 +1,110 @@ +# FirstRising 대화로그 — 2026-09-30 (개발팀 세션 · BT43~) + +## BT43-FirstRising · 장착 무기에 따른 공격 모션 · 패턴 + 플레이어 정보 화면 반영 + +### PD 지시(원문 · 00:34) +"현재 에디터에서 플레이해보니 플레이어가 장착한 장비에 따른 모션 및 공격 패턴 변화가 적용되지 않는 것 같아. +* 원래 FR 프로젝트를 살펴보고 장착한 무기에 따른 공격 모션 및 패턴 변화를 구현 또는 복원해줘. +* "장비" 메뉴에서 장착한 아이템이 로비(숲)의 기본 플레이어 정보(스텟이 보여지는 화면)에 반영되지 않고 있어. 장비 메뉴에서 장착하면 플레이어 스텟이 보이는 화면에도 반영될 수 있도록 수정해줘." + +### 원인(실측 + 코드) +- 로비 씬에는 캐릭터가 둘이다 — 싸우는 캐릭터(`LevelEssential/Ariel` · `Player.Instance`)와 예전 전시용 캐릭터(`LobbyManager.lobbyPlayer` · 꺼져 있음). +- 게임 원본은 장비를 바꾸면 전시용 캐릭터만 갱신한다(`ItemPageUI` · `CharacterPageUI.ShowCharacterItems`). 싸우는 캐릭터는 `TestMonstersController` 가 Play 시작 때 한 번만 무기를 들린다(장착이 없으면 1029 활). +- BT 활 장식(`TVArcherGear`)이 무기 종류와 무관하게 게임 무기 모델을 숨기고 활 · 화살통을 붙였다 → 검을 들어도 활만 보였다. +- 플레이어 정보 화면은 열 때만 슬롯 · 값을 썼다. 장착 뒤 게임이 다시 그리는 것은 장비 화면뿐이다. +- 무기별 공격 모션 · 패턴 자체는 게임에 살아 있다(무기 → 스킬 → `SkillElement.AnimStateHash` → 애니메이터 상태 `Sword0`… `Bow0`…). 끊긴 것은 '장비 변경 → 싸우는 캐릭터' 연결이다. + +### 결정(PM) +- 게임 코드는 고치지 않고 BT_Visual 에 연결 구성 요소를 둔다(`TVEquipmentSync`). 게임의 장비 변경 이벤트(`PlayerManager.OnUserStatusChanged`)를 듣고, 장착 상태의 지문이 달라졌을 때만 일한다(매 프레임 하는 일 없음). +- 무기 교체는 게임 함수 `Character.EquipWeapon` 을 그대로 쓴다(에디터 무기 목록 창의 선례). 무기는 그대로고 능력치만 바뀐 경우는 `ApplyAbility` + 능력치 갱신 요청 + 무기 스킬 기본 피해 다시 쓰기. +- 활 전용 대기 · 달리기 교체(`TVAnimationOverride`)는 프리팹에서 꺼진 상태 그대로 둔다(켜면 다른 무기에서도 활 자세가 나온다). + +### 결과 +- 새 파일 `Assets/BurningTimes/Visual/Scripts/TVEquipmentSync.cs` + `LobbyPlayScene.prefab` 의 BT_Visual 에 부착. 수정 `TVArcherGear.cs`(활일 때만) · `TVCharacterInfoPanel.cs`(장비 변경 신호에 다시 씀). +- 검증 1차(00:4x): 시작 시 검 1436 → 엔티티 무기 1436 · 검 모델 표시 / 활 1029 장착 → 엔티티 1029 · 기본 피해 27 · 활 · 화살통 부착 / 검 재장착 → 기본 피해 34 · 최종 공격력 37 / 정보 화면을 연 채 활 장착 → 무기 슬롯 1436→1029 · 공격력 37→27 / 무기 해제 → 기본 활 1029 · 기본 피해 20. +- 검증 2차(00:57~00:59 · 몬스터 곁에서 공격): 검 → 애니메이터 상태 `Sword0 · Sword1 · Sword2`(스킬 100061~63 · 사거리 4.9 · 4.9 · 6.3) / 활 → `Bow0~Bow3`(스킬 100021~24 · 사거리 6.3) / 정보 화면 공격력 · 생명력 = 캐릭터 계산값(활 27 · 510, 검 37 · 510). 에디터 로그 오류 0(logscan · 6.5 MB). +- 1차에서 찾은 결함 수정: 시작 때 내 동기화와 게임의 첫 장착이 겹쳐 검 모델이 둘 생김 → 게임이 먼저 들 때까지 최대 3초 기다림. +- 한글 주석 점검(comment_scan): 선언 35개 중 누락 0. 저장 파일 3개 시험 전 백업 → 원복(해시 일치). +- 필드를 바꿔 끼운 직후(BT45)에도 새 플레이어 엔티티에 능력치 · 무기 스킬을 다시 넣게 했다(도착 직후 기본 피해 34 · 최종 공격력 37 확인). +- FR `bt/work` `4bca428e62` push(PD 지시 02:0x "충분히 반영되었다고 판단될 경우 … 푸시"). + +### 남은 이슈 +- 가방에 장비가 검 · 활 둘뿐이라 다른 무기 종류(둔기 · 단검 · 지팡이 · 마법 단검)와 방어구 · 룬 경로는 실측하지 못했다(같은 경로라 동작할 것으로 보이나 추정). +- 활이 아닌 무기에서 대기 · 달리기 자세는 무기와 무관하게 같다(게임 원본도 같다). +- 게임 코드 `Character.ApplyAbility` 의 능력치 갱신 요청은 '값'을 종류로 바꿔 넣는 것으로 보인다(손대지 않음 · 정섭 님 확인 필요). +- Editor.log 가 매 프레임 '오디오 리스너 2개 · 이벤트 시스템 2개' 경고로 커진다(게임 측 중복 · 미수정). + +## BT44-FirstRising · 최소화(위젯) 모드 — 지형 숨김 + 창 크기 조절 메뉴 + +### PD 지시(원문 · 01:01) +"* 최소화 모드에서는 플레이어와 몬스터 외에 지형은 보이지 않도록 해줘. (현재 맵 오브젝트가 보이고 있음) +* 최소화 모드에서는 하단 UI 상단 부분에 마우스를 가져다 대면, "창 크기를 조절할 수 있는 슬라이드 메뉴"를 제공해줘. 이 메뉴를 통해 게임 화면의 창의 크기를 조절하고 싶어." + +### 원인(실측) +- 최소화 모드 = 게임의 위젯 기능(`DesktopWidgetManager`). 들어가면 메인 카메라에서 `WidgetCulling` 레이어(29)만 뺀다. +- 그 레이어를 쓰는 것은 로비 프리팹(92개)과 숲 맵 서브씬뿐 → 숲에서는 캐릭터 · HUD 만 보인다(정상). 새 테마 맵(필드 이동 6종 · 보스전 전용 필드)은 기본 레이어라 그대로 보였다 — 묘지에서 맵 엔티티 1,565 · 필드 꾸러미 렌더러 427(메시 315 · 입자 102 · VFX 10) · 지형. +- 에디터에서 최소화 모드로 들어가는 길은 게임 화면의 `WidgetModeButton`(에디터는 창을 다루지 않고 상태만 흉내 낸다 — `m_SimulateInEditor`). + +### 결정(PM) +- 맵 에셋 · 빌더는 고치지 않는다(레이어를 바꿔 다시 지으면 맵 6종 · 꾸러미 · 전용 필드를 모두 다시 만들어야 한다). 최소화 모드에 들어갈 때 숨기고 나올 때 되돌린다. +- 창 크기 값은 '게임에 설정된 기본 크기'의 배수(0.5~2.5 · 5% 단위)로 둔다. 빌드는 위젯 창의 한 변(`widgetDiameter` 440 × 배수), 에디터는 게임 화면 영역을 같은 배수로 키우고 줄여 미리 보여 준다. +- 막대를 끄는 동안 바로 적용하지 않고 놓는 순간 적용한다 — 창(화면)이 커지면 막대가 마우스 밑에서 움직여 값이 흔들린다. +- 입력은 UGUI 이벤트가 아니라 커서 좌표를 직접 읽는다 — 위젯 창은 포커스가 없을 수 있고, 게임의 위젯 입력도 같은 방식이다. + +### 결과 +- 새 파일 `TVWidgetScenery.cs`(맵 엔티티에 DisableRendering + 표식 · 지형 그리기 끔 · 등록된 배경 묶음의 렌더러 끔) · `TVWidgetSizeMenu.cs` · `TVRuntimeFont.cs`(실행 중 화면용 글자체). `TVFieldTravel` · `TVBossField` 가 배경 묶음을 등록한다. +- 게임 코드 `DesktopWidgetManager.cs` 2곳(날짜 · 이유 한글 주석): ① 조작 UI 영역 등록(`RegisterWidgetControlArea`) — 그 안에서 시작한 누름은 창 끌기 · 복귀로 보지 않는다 ② 위젯 모드 중 `widgetDiameter` 를 바꿀 때 원형 리전 설정(`m_UseCircularRegion`)을 따르게 함(꺼져 있는데도 원형으로 잘랐다). +- 검증: 묘지 최소화 모드에서 캐릭터 · 몬스터 · HUD 만 보임(엔티티 1,565 · 렌더러 427 · 지형 1 숨김) → 나오면 모두 복구 / 띠에 마우스 → 메뉴 나옴 → 막대 누름(끄는 중 · 위젯 유지) → 50% 자리에서 놓음 → 크기 1.50 · 위젯 440→660 · 게임 화면 471×837→706×1255 px · 저장 / 벗어나면 0.8초 뒤 들어감 / 시험 뒤 크기 · 저장 값 원복. 에디터 로그 오류 0. + +### 남은 이슈 +- PC 빌드 미검증 — 창 크기 변경(Win32) · 투명 창에서의 모습 · 바탕화면 커서 읽기는 빌드에서만 도는 경로다. +- 크기를 바꾸면 메뉴가 마우스 밑에서 벗어나 잠시 뒤 들어간다(다시 조절하려면 띠에 다시 댄다). +- 최소화 모드에서 스킬 칸 · 가방 버튼을 누르면 게임의 위젯 입력이 '복귀'로 처리한다(기존 동작 · 이번에 바꾸지 않음) — 필요하면 같은 조작 영역 등록으로 풀 수 있다. + +## BT45-FirstRising · 맵 이동 로딩 단축 + 로딩 진행도 표시 + +### PD 지시(원문 · 01:01) +"맵 이동 시 로딩이 너무 긴데 왜 이렇게 오래 걸리는거지? 좀 더 로딩 시간을 줄일 방법이 없을까? +그리고 로딩 중에는 loading... 글자와 함께 로딩 진행도를 프로그레스 바 형태로 제공해줘." + +### 원인(실측 · 맵 이동 5회 · 게임 안 시계) +| 구간 | 시간 | +|---|---| +| 화면 가림 | 0.2초 | +| 엔티티 전체 정리(엔티티 씬 16개 내림 · 재질 등록 해제 · 삭제) | 1.85초(한 프레임) | +| 로비 씬 읽기(뒤에서) | 1.6초 | +| 로비 씬이 켜지는 프레임(화면 · 구성 요소 전부 다시 만듦) | 3.25초(한 프레임) | +| 그 뒤 첫 프레임(맵 서브씬 읽기 포함) | 1.0~1.2초 | +| 기다림 + 화면 걷음 | 0.7초 | +| **합계** | **8.3~8.7초** · 몬스터는 도착 6~7초 뒤에 나옴 | +- 그 맵에 처음 갈 때(스크립트를 고친 뒤 첫 방문)는 서브씬 굽기 4~5.5초가 더 붙어 12~15초 — 에디터 전용(빌드는 미리 구워 둔다). +- 즉 '맵만 바꾸면 되는데 로비 씬 전체를 다시 읽는 것'이 대부분이었다(BT39 에서 화면 구성을 유지하려고 택한 방식). + +### 결정(PM) +- 로비 씬을 다시 읽지 않고 맵만 바꿔 끼운다. 예전 방식은 남겨 두고, 바꿀 수 없거나 실패하면 그쪽으로 넘어간다. +- 플레이어 엔티티는 맵 서브씬에 구워져 있다(1차 검증에서 발견 — 맵을 내리자 게임 시스템 5곳이 사라진 엔티티를 건드려 예외 15,781회). → 맵을 내리기 전에 관리 쪽 캐릭터의 엔티티 연결을 비우고(`initPlayer` 끔 · `characterEntity` 비움), 새 맵이 읽히면 게임의 플레이어 초기화가 다시 돌게 한다(`WorldManager.initPlayer` 끔 → `PlayerEntitySyncSystem` 이 새 엔티티에 연결 · 시작 자리 · 카메라 · 체력). 게임 코드는 고치지 않았다. +- 몬스터 · 소환수 · 젬 · 드롭은 지우지 않고 풀로 돌려보낸다(게임의 회수 절차와 같은 값). 상자(깨지는 상자)는 건드리지 않는다. +- 맵 읽기는 기다리지 않고 맡긴다(화면이 멈추지 않아 진행 막대가 움직인다). + +### 결과 +- `TVFieldTravel.cs`(맵만 바꿔 끼우기 · 예전 방식 유지) · 새 파일 `TVLoadingScreen.cs`(검은 막 + "Loading..." + 진행 막대 + 퍼센트 · 게임 화면 영역 한가운데). `TVEquipmentSync` 는 필드가 바뀌면 새 엔티티에 능력치 · 무기 스킬을 다시 넣는다. +- 검증(Play 1회 · 게임 안 시계): + | 이동 | 방식 | 시간 | + |---|---|---| + | 숲 → 묘지(처음 · 굽기 포함) | 맵만 바꿈 | 5.6초 | + | 묘지 → 숲 | 맵만 바꿈 | 1.95초 | + | 숲 → 묘지 | 맵만 바꿈 | 1.4초 | + | 묘지 → 사막(처음 · 굽기 포함) | 맵만 바꿈 | 5.6초 | + | 사막 → 묘지 | 예전 방식(비교용) | 8.65초 · 몬스터는 7초 뒤 | + | 묘지 → 숲 | 맵만 바꿈 | 1.9초 | +- 도착 즉시 몬스터(47~53마리) · 플레이어 시작 자리(맵마다 구워진 자리와 일치) · 체력 510/510 · 무기 1436 기본 피해 34 · 분위기(안개 · 환경광) 일치 · 숲으로 돌아오면 숲의 분위기 복구. +- 맵을 바꾼 뒤 보스전 입장 · 퇴장 정상(숨긴 몬스터 53 → 복구) · 보스전 중 이동은 안내만 띄우고 거절 · 에디터 로그 오류 0. + +### 남은 이슈 +- 처음 가는 맵의 굽기 시간(4~5.5초)은 에디터에서만 남는다 — 스크립트를 고칠 때마다 맵마다 한 번. 줄이려면 Play 시작 때 뒤에서 미리 굽게 하는 방법이 있다(미구현 · PD 결정). +- PC 빌드에서는 테마 맵 서브씬이 빌드에 들어 있지 않다(`mapsInBuild` 꺼짐) — 빌드 미검증. +- 소환수가 있는 상태의 맵 이동은 미실측(소환수도 풀로 돌려보내게 했다). + +## 커밋 · 푸시 +- FR `bt/work` `4bca428e62`(BT42~BT45) push — 에디터가 덧붙인 변경(재질 4 · 글자체 3 · 엔티티 의존성 1)은 넣지 않았다. +- 조직 레포(이 문서 · PD 지시 로그)는 커밋하지 않았다(PD 지시 시). diff --git a/공유/대화로그/FirstRising/2026-09-30_세션인수인계.md b/공유/대화로그/FirstRising/2026-09-30_세션인수인계.md new file mode 100644 index 0000000..154c4a1 --- /dev/null +++ b/공유/대화로그/FirstRising/2026-09-30_세션인수인계.md @@ -0,0 +1,86 @@ +# FR 필드 · 장비 · 최소화 모드 · 맵 이동 — 세션 인수인계서 (2026-09-30 · BT42~BT46) + +> 이 문서 하나로 다음 세션이 이어갈 수 있게 적었다. 원문 · 과정은 `공유/대화로그/FirstRising/2026-09-29.md`(BT42) · `2026-09-30.md`(BT43~45) · `공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md`(BT46), 지시 로그는 `공유/PD_지시_트래킹/개발팀_PD_지시_로그.md`. +> 세션 종료 사유: 컨텍스트 한도(PD 2026-09-30 "본 세션은 컨텍스트 한도에 도달했어 … 인계서 및 프롬프트 제공"). + +## 0. 다음 세션 첫 프롬프트 (그대로 붙여넣기) + +``` +FR(FirstRising) 작업을 이어서 진행한다. +인수인계서: 공유/대화로그/FirstRising/2026-09-30_세션인수인계.md 의 §0-1 · §1 · §2 를 먼저 읽고, +현황을 3줄로 보고한 뒤 내 지시를 기다려라. (로그 전문 읽기 금지, 필요한 절만 Grep) + +고정 사항: +- 커밋 · push 는 PD 지시 시만(FR · 조직 레포 모두). 작업을 마치면 작업 트리에 두고 보고한다 +- 재컴파일 · Play 조작 · 에디터 설정을 바꾸는 명령 앞에는 Play 여부를 단독 명령으로 먼저 확인한다. + PD Play 중에는 코드 수정 · Play 조작 · 무거운 스크립트 금지(읽기 전용 확인만) +- 검증 Play 는 끝나면 즉시 종료 · 임시로 바꾼 값(저장 파일 · 창 크기 값 · 훅)은 즉시 원복 · 백그라운드 작업을 남기지 않는다 +- 오류 확인은 에디터 로그 기준(logscan.py). recompile_status 는 'completed' 글자만 찾는다(따옴표가 이스케이프됨) +- 게임 원본 코드 수정 시 날짜 · 이유 한글 주석. 새 코드 · 고친 코드에는 선언 단위 한글 주석. 스크립트는 BT_Visual 노드에만 +- 파일 삭제는 PD 지시 시만(쓰이지 않게 된 것은 목록으로 보고) +- 허위 · 추정 단정 금지(실측 · 코드 · 미검증을 구분해 보고). 감사 호출 생략, 무거운 에이전트 호출은 사전 승인, 세션 전환 권고 금지 +- 보고는 한국어 · 간결 · 경어, 호칭 PD님. 승인이 필요하면 질문으로 끝낸다 +``` + +## 0-1. 이번에 열 문서(필요한 절만) + +| 하려는 일 | 열 문서 · 절 | +|---|---| +| 필드 이동(맵만 바꿔 끼우기) · 로딩 화면 · 최소화 모드 배경 · 창 크기 메뉴 | `공유/대화로그/FirstRising/2026-09-30.md` §BT44 · §BT45(원인 표 · 결정) + 메모리 `project_fr_widget_travel_bt44_45.md`(함정) | +| 장비 → 싸우는 캐릭터 · 정보 화면 | `2026-09-30.md` §BT43 + 메모리 `project_fr_equipment_sync_bt43.md` | +| 필드 레벨업 끄기 · GameCanvas 창 가려짐 | `2026-09-29.md` + 메모리 `project_fr_gamecanvas_popup_hidden.md` | +| 검증 스크립트 다시 쓰기 | `공유/기획/FirstRising/몬스터보스/tools/unity_eval/bt_evals_0930/README.md` → `%TEMP%\bt_evals\` 로 복사 | +| 화면 구성(세 영역) · 옆 창 · 맵 다시 짓기 · 보스 패턴 | 09-28 인수인계서 `2026-09-28_FR_맵_보스_화면구성_세션인수인계.md` §0-1 의 표 | +| Claude 앱이 트레이에서 사라짐 | `공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md` + 메모리 `reference_claude_app_tray_stealth_update.md` | +| 조직 레포 커밋 · push | 메모리 `reference_org_repo_commit_push.md` | + +## 1. 현황 한눈에 + +| 구분 | 상태 | +|---|---| +| FR `bt/work` | **`4bca428e62`** push 완료(BT42~45). 작업 트리에는 에디터가 덧붙인 변경만 남음(재질 4 · 글자체 3 · `GlobalEntitySceneDependency.asset`) — 건드리지 않는다 | +| 조직 레포 `main` | 이 인계서와 함께 커밋 · push(BT42~46 기록 · 검증 스크립트 사본) | +| BT42 필드 레벨업 끄기 | 완료 · PD 확인 대기(보스전 전용 필드 포함 여부) | +| BT43 장착 무기 모션 · 정보 화면 | 완료 · 검 · 활만 실측 | +| BT44 최소화 모드 지형 숨김 · 창 크기 메뉴 | 완료 · PC 빌드 미검증 | +| BT45 맵 이동 8.5초 → 1.4~2.0초 · 로딩 화면 | 완료 · 처음 가는 맵 굽기 시간(에디터 전용)은 남음 | +| BT46 Claude 앱 트레이 | 원인 확정 · 감시 스크립트 준비. **PD 가 작업 스케줄러 등록 명령을 실행했는지 확인** | +| 저장 파일(`LocalLow\ThunderRisingGames\Dead Archive`) | 시험 전 상태로 원복 완료(백업은 세션 scratchpad `save_backup_0930` — 세션이 끝나면 사라져도 된다) | +| 백그라운드 작업 · 훅 | 없음(travel_hook · wsm 훅 모두 해제 · 검증 스크립트 모두 종료) | + +## 2. 이번 세션이 만든 것(FR) + +| 파일 | 하는 일 | +|---|---| +| `Assets/BurningTimes/Visual/Scripts/TVFieldLevelUpGate.cs` (새) | 로비 씬 동안 `PlayerManager.gemLevelUp` 을 끔 | +| `TVEquipmentSync.cs` (새) | 장비 변경 신호 → 싸우는 캐릭터 무기 · 능력치 → `changed` 이벤트. 필드가 바뀌면(`TVFieldTravel.fieldChanged`) 새 엔티티에 다시 넣음 | +| `TVArcherGear.cs` · `TVCharacterInfoPanel.cs` | 활일 때만 활 장식 / 장비 바뀌면 정보 화면 다시 씀 | +| `TVWidgetScenery.cs` (새) | 최소화 모드 동안 맵 엔티티(DisableRendering + `TVWidgetHiddenTag`) · 지형 · 등록된 배경 묶음 숨김 → 복구 | +| `TVWidgetSizeMenu.cs` (새) | 하단 UI 위쪽 띠에 마우스 → 창 크기 막대(배수 0.5~2.5 · PlayerPrefs `BT_WidgetSize`). 빌드는 `widgetDiameter`, 에디터는 위젯 영역 localScale 미리 보기 | +| `TVRuntimeFont.cs` (새) | 실행 중 만든 화면용 글자체(공용 아틀라스 보호) | +| `TVFieldTravel.cs` | `swapInPlace`(기본 켜짐): 몬스터 · 소환수 · 드롭 회수 → 플레이어 떼기 → 맵 서브씬 교체 → 게임의 플레이어 초기화 재실행 → 구역 켜기. 실패하면 예전 방식(로비 씬 다시 읽기) | +| `TVLoadingScreen.cs` (새) | 검은 막 + "Loading..." + 진행 막대 | +| 게임 원본(정섭 님 · 날짜/이유 주석) | `PlayerManager.gemLevelUp` · `Character.AddGemScore` · `DesktopWidgetManager`(조작 영역 등록 API · 원형 리전 설정 따르기) | +| `Assets/GameAssets/LobbyPlayScene.prefab` | BT_Visual 에 위 새 구성 요소 부착 · 글자체 참조 | + +## 3. 실측으로 알게 된 것(다시 조사하지 말 것) +- 로비 씬에는 캐릭터가 둘(싸우는 `LevelEssential/Ariel` · 전시용 `Character/CharacterPosition/Ariel`). 게임은 장비를 바꾸면 전시용만 갱신한다. +- **플레이어 엔티티(PlayerTag)는 맵 서브씬에 구워져 있다** — 맵을 내리면 사라진다. 떼지 않으면 게임 시스템 5곳이 매 프레임 예외(15,781회 실측). +- 맵 이동 예전 방식의 시간: 엔티티 정리 1.9 · 씬 읽기 1.6 · 씬 켜짐 3.3 · 첫 프레임 1.0 · 가림 0.7초. 처음 가는 맵은 서브씬 굽기 4~5.5초(에디터 전용 · 스크립트 고칠 때마다). +- 최소화 모드 = `DesktopWidgetManager` 위젯 기능. `WidgetCulling` 레이어만 숨김 → 숲만 그 레이어. 위젯 모드에서 누르면 창 끌기 · 복귀 → 조작 UI 는 `RegisterWidgetControlArea` 필요. +- 가짜 마우스 입력 시험은 `InputSystem.onAfterUpdate`(Dynamic) 훅으로 넣어야 게임이 본다. +- Claude 앱 트레이 사라짐 = 몰래 업데이트 + `windows-app-job` 기능(09-23~)으로 윈도우가 앱을 못 닫음. 앱 결함. + +## 4. 미검증 · 결정 대기(PD) +- PC 빌드: 위젯 창 크기 변경 · 투명 창 · 커서 읽기 · 맵 이동(맵 서브씬이 빌드에 없음 `mapsInBuild`). +- 보스전 전용 필드에서도 레벨업이 꺼짐(BT42) — 의도 확인. +- 검 · 활 외 무기 종류 · 방어구 · 룬 경로(가방에 아이템 없음) · 소환수가 있는 상태의 맵 이동. +- 처음 가는 맵의 굽기 시간을 Play 시작 때 뒤에서 미리 굽게 할지(미구현). +- 최소화 모드에서 스킬 칸 · 가방 버튼을 누르면 게임이 '복귀'로 처리(기존 동작). 필요하면 같은 조작 영역 등록으로 풀 수 있다. +- BT46 감시 스크립트의 실제 끄고 켜기는 시험하지 않았다(세션이 앱 안에서 돔). 등록 뒤 첫 발생 때 `watchdog.log` 로 확인. + +## 5. 남은 이슈(기존) +- 같은 GameCanvas 의 다른 창(사망 결과 · 스킬북 · 게임 메뉴)은 로비 씬에서 게임 화면 아래에 깔림(미수정). +- Editor.log 가 매 프레임 '오디오 리스너 2개 · 이벤트 시스템 2개' 경고로 커짐(게임 측 중복 · 11 GB). +- `Character.ApplyAbility` 의 능력치 갱신 요청은 값을 종류로 넣는 것으로 보임(정섭 님 확인 필요). +- 일일 도전 진입 에디터 충돌(09-24) 원인 미확정 — 재발 시 `TVMaterialProbe.Install()` 절차. diff --git a/공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md b/공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md new file mode 100644 index 0000000..1fae863 --- /dev/null +++ b/공유/대화로그/조직운영/2026-09-30_Claude앱_트레이.md @@ -0,0 +1,45 @@ +# 조직운영 대화로그 — 2026-09-30 · BT46-Org Claude 윈도우 앱이 트레이에서 사라짐 + +## PD 지시(원문 · 09:40) +"업데이트 관련 오류 때문인지 클로드 윈도우앱이 일정시간 동작하지 않을 때 자꾸 시스템 트레이에서 사라지고 있어. 원인 체크해보고 정상적으로 윈도우 트레이에 남아있도록 수정해줘." + +## 원인(실측 — 앱 로그 `%LOCALAPPDATA%\Claude\logs\main*.log` + 윈도우 이벤트 로그) +1. 앱(MSIX 설치 · `Claude_pzs8sxrjxfjjc`)은 업데이트를 받아 둔 상태에서 세션이 없고 창이 앞에 있지 않은 채 10분쯤 지나면 '몰래 업데이트'를 한다 — 세션 · 터미널을 멈추고 창을 숨긴 뒤, 윈도우에 "앱을 강제로 닫고 새 버전을 등록"해 달라고 한다(로그 `[stealth-update] Triggering stealth update after idle timeout` → `beforeQuitForUpdate handler fired, going down for update`). +2. 2026-09-23 02:56:35 시작분부터 앱이 자기 프로세스를 작업 묶음(Job)에 넣는다 — 로그 `[windows-app-job] joined`(그 전에는 `skipped: flag off`). 설정 파일의 `preferences.windowsAppJobLatched = true`(서버의 기능 스위치가 켜지면서 고정된 값 · 보안 분류 항목이라 손대지 않았다). +3. 그 뒤로 윈도우가 앱을 닫지 못한다 — 이벤트 로그 `Microsoft-Windows-AppXDeploymentServer/Operational`: 638 "영향을 받는 앱이 계속 실행 중이므로 패키지가 업데이트되지 않았습니다" → 419 "오류 0x800700E9(또는 0x8007001F): 다음 앱을 닫아야 하므로 설치할 수 없습니다. 앱: Claude_pzs8sxrjxfjjc!Claude" → 401 · 404(0x80073CF9). + 성공하던 때는 646 "앱이 설치를 위해 종료되었습니다"가 남고 앱 로그에 `Windows session ending (close-app)`이 찍혔다. 09-23 02:56:32 가 마지막이다. +4. 실패해도 앱은 되돌리지 않는다 — 창 · 트레이 없이 숨어서 남고(메모리 기록만 계속), 하나만 뜨게 하는 잠금을 쥐고 있어 아이콘을 눌러도 새로 켠 것이 `Not main instance` 로 바로 끝난다(09-30 00:30:49 · 00:31:13 · 00:31:21 세 번 실패 뒤 00:31:36 에 새 버전으로 시작). + +| 업데이트로 내려간 시각 | 다시 올라온 시각 | 걸린 시간 | +|---|---|---| +| 08-21 ~ 09-23 02:56 (18회) | 자동 | 2~40초 (전부 성공) | +| 09-23 10:44 | 10:55 | 10.6분 | +| 09-25 05:16 | 10:15 | 5.0시간 | +| 09-25 13:21 | 13:57 | 36분 | +| 09-25 14:36 | 19:36 | 5.0시간 | +| 09-29 03:48 | 09-30 00:30 | 20.7시간 | +| 09-30 05:42 | 09:37 | 3.9시간 | + +- 윈도우 쪽 변화는 아니다 — 마지막 부팅 09-17, 09-23 02:56(성공)과 10:44(첫 실패) 사이에 설치된 윈도우 업데이트 없음(Defender 정의만). +- 유니티 에디터 등 다른 프로그램은 Claude 패키지에 속해 있지 않다(프로세스의 패키지 신원 확인). + +## 결정(PM) +- 결함은 앱 안에 있다(작업 묶음 기능 + 실패 시 복구 없음). 서명된 앱 패키지와 보안 분류 설정은 고치지 않는다. +- 멈춘 상태를 감지해 앱을 끝내고 정식 경로로 다시 켜는 감시 스크립트를 둔다. 다시 켜지면 윈도우가 미뤄 둔 새 버전 등록을 마친다(09-30 09:37 에 같은 흐름 확인). 내려간 지 5분 안이면 앱이 내려가기 전 상태(숨은 채)로 돌아온다(앱의 표식 유효 시간 300초). +- 작업 스케줄러 등록은 시스템 설정 변경이라 PM 이 하지 않고 PD 가 명령 1개로 실행한다. + +## 결과 +- `%LOCALAPPDATA%\ClaudeTrayWatchdog\watchdog.ps1`(판단 · 조치) · `run-hidden.vbs`(창 없이 실행). +- 판단 기준: 앱 로그에서 '업데이트로 내려감'이 마지막 '주 인스턴스 시작'보다 뒤 + 90초 경과 + 같은 건에 조치한 적 없음. 사용자가 직접 끝낸 경우는 건드리지 않는다. +- 시험(조치 없이 판단만): ① 지금 상태 → 정상 · 조치 없음 ② 새벽 상황 재현(05:45 시점) → 멈춤으로 판단 ③ 내려간 지 30초 → 기다림 ④ 성공했던 날(09-23 02:56) → 조치 없음. 창 없이 실행하는 파일도 오류 없이 돈다. +- 실제로 앱을 끄고 켜는 동작은 시험하지 않았다 — 이 세션이 그 앱 안에서 돈다. + +## PD 가 할 일 +- 등록: `schtasks /Create /TN ClaudeTrayWatchdog /SC MINUTE /MO 2 /TR "wscript.exe C:\Users\sw\AppData\Local\ClaudeTrayWatchdog\run-hidden.vbs" /F` +- 해제: `schtasks /Delete /TN ClaudeTrayWatchdog /F` +- 기록: `%LOCALAPPDATA%\ClaudeTrayWatchdog\watchdog.log` + +## 남은 이슈 +- 근본 수정은 Anthropic 의 앱 업데이트가 필요하다(앱의 의견 보내기로 위 1~4 를 전달하면 된다). +- 다른 방법: 관리 설정 `disableAutoUpdates`(레지스트리 `SOFTWARE\Policies\Claude`)로 자동 업데이트를 끄면 앱이 내려가지 않는다. 대신 업데이트를 직접 해야 한다 — PD 결정. +- 업데이트 알림이 보이면 쉬기 전에 앱에서 직접 '다시 시작'을 눌러 두면 몰래 업데이트가 돌 일이 없다.