

파츠 NPC 상호작용 UI(NSPartEquipWidget)가 원래 "항목마다 개별 버튼(장착/구매) + hover 툴팁" 방식으로 짜여 있었는데, 기획이 Docs/2.png로 확정되면서 상호작용 모델 자체가 바뀌었다. 항목 클릭 → 중앙에 설명 표시 → 하단 장착 버튼 하나로 확정하는 선택 모델로 전환 필요
| 결정 | 내용 | 이유 |
|---|---|---|
| 데이터 모델 | 캐릭터당 파츠 1개 장착 유지, 세이브 구조 변경 없음 | 기획 확인 결과 다중 장착 계획 없음. 2.png의 "장착중 3박스"는 슬롯 타입 표시용이지 3개 동시 장착이 아님 |
| 상호작용 | 개별 버튼+툴팁 → 클릭 선택 모델 | 2.png가 항목 클릭 후 중앙 패널에서 확인하고 장착하는 흐름을 확정 레이아웃으로 못 박음 |
| 설명 패널 | 공용 위젯 1개(NSPartDetailWidget), 셋업 메서드만 2개로 분기 | 카탈로그 후보(정의 정보: 비용/등급별 수치범위/리롤가능)와 장착중인 파츠(인스턴스 정보: 현재 등급/현재 수치)는 데이터 소스가 다르지만, 보여주는 자리(왼쪽/중앙)는 UI상 같은 종류의 패널이라 위젯 중복을 피함 |
| 카탈로그 분할 | 바디/암/레그 3개 컨테이너로 라우팅 | FNSPartDefinitionRow::PartSlot 태그 하나로 구분 가능해서 별도 자료구조 없이 태그 매칭만으로 처리 |
신규 — NSPartDetailWidget.h/.cpp
카탈로그 선택 파츠와 장착중 파츠 양쪽에서 쓰는 상시 설명 패널.
SetupFromDefinition(Row, Def) — 카탈로그 후보 표시 (비용/슬롯/리롤가능)SetupFromEquipped(SaveData, Def) — 장착중인 파츠 표시 (현재 등급/현재 수치)ClearDetail() — 선택/장착 없음 상태수정 — NSPartCatalogEntryWidget.h/.cpp
개별 ActionButton(장착/구매)과 hover 툴팁(SetToolTip) 제거. 항목 전체를 감싸는 SelectButton 하나로 바뀌고, 클릭 시 Owner->SelectCatalogPart(Row) 호출. Icon/Name만 남음.
수정 — NSPartEquipWidget.h/.cpp
BodyListContainer/ArmListContainer/LegListContainer 3분할로 라우팅 (GetCatalogContainerForSlot)SelectedRow/bHasSelection) 보관, 카탈로그 클릭 시 중앙 설명·장착버튼 갱신 (SelectCatalogPart, RefreshEquipButton)RequestEquipPart/RequestUnlockPart 분기 (OnEquipButtonClicked)SelectEquippedSlot) — 클릭한 박스의 슬롯 태그와 실제 장착품의 슬롯이 일치할 때만 표시, 아니면 빈 상태수정 — NSPartUtils.h/.cpp
TSoftObjectPtr<UNSPartDefinition>를 직접 받는 ResolvePartDefinition 오버로드 추가. 기존 FNSPartData 버전은 이 오버로드에 위임하도록 리팩터링.
FNSPartDefinitionRow::Definition)과 장착중 조회(FNSPartSaveData::Definition) 둘 다 "소프트포인터에서 Definition을 캐시 경유로 안전하게 꺼내는" 동일한 로직이 필요했는데, 기존 함수는 FNSPartData 구조체 전용이라 재사용이 안 됐음. 중복 구현 대신 공용화.삭제 — NSPartTooltipWidget.h/.cpp
hover 툴팁 방식 자체가 폐기되면서 참조가 완전히 사라짐. 다른 참조 없음을 확인 후 삭제.
WBP_PartTooltip.uasset 삭제 (C++ 부모 클래스 없어짐)WBP_PartEquip.uasset 레이아웃 재구성 (3열 구조, 바인딩 이름 일치)WBP_PartCatalogEntry.uasset을 SelectButton 구조로 재구성WBP_PartDetail.uasset 생성 (부모: NSPartDetailWidget)에디터 작업 중 위젯이 전부 왼쪽 위에 몰리는 문제 발생. 계층 구조(Hierarchy)는 올바르게 잡혀 있었는데도 발생한 원인:
Horizontal Box(3열 컨테이너)가 Canvas Panel의 자식인데 1번 레이어의 Anchor를 "전체 스트레치"로 안 잡아서, 그 안에서 2번 레이어(Vertical Box Fill)를 아무리 조정해도 애초에 나눠 가질 공간 자체가 없었음.UDeveloperSettings에서 NSCommonDataConfig로 이관사용자가 프로젝트의 데이터 로드 아키텍처를 다시 확인하자고 제안: NSDataSubsystem은 공용(Common) / 아웃런(OutGame) / 인런(Run) 페이즈별로 데이터를 로드하고, 페이즈마다 전용 UPrimaryDataAsset(예: UNSCommonDataConfig)에 DT/에셋 참조를 몰아두는 구조로 이미 정리되어 있는데, 파츠 DT(DT_PartDefinition/DT_PartSlot)만 예외적으로 UNSDataSettings(UDeveloperSettings, 프로젝트 세팅 전역 싱글톤)에 붙어 있었음.
UNSCommonDataConfig : UPrimaryDataAsset를 통해 AssetBundles="CommonData" 방식으로 DT를 로드하고 있었음 (예: AbilityBaseStatTable).HubAssetType/"NSHubData")는 코드에 상수만 선언돼 있고 실제 클래스도, AssetManager 스캔 등록도 없는 미완성 상태였음.NSCommonDataConfig.h에 PartsBaseStatTable, PartsSlotBaseStatTable 필드 추가 (AssetBundles="CommonData")NSDataSubsystem.cpp:OnOutGamePrimaryAssetsLoaded/OnOutGameAssetsLoaded에서 파츠 DT 로드·캐시빌드 로직 제거OnCommonAssetsLoaded에 BuildPartRowCache()/BuildSlotRowCache() 호출 추가GetDefault<UNSDataSettings>() 대신 GetCommonDataConfig()를 조회하도록 변경NSDataSettings.h/.cpp 삭제, Config/DefaultGame.ini의 [/Script/NeoSanctum.NSDataSettings] 섹션 삭제UDeveloperSettings라는 별개 메커니즘(프로젝트 세팅, 페이즈 개념 없음)을 쓰고 있어 구조가 어긋나 있었음.UDeveloperSettings는 UPrimaryDataAsset이 아니라서 AssetBundle 시스템에 못 들어가고, 그래서 기존 코드는 PartDefinitionTable/PartSlotTable 경로를 수동으로 RequestAsyncLoad하는 별도 코드가 필요했음. NSCommonDataConfig로 옮기면서 AssetBundles="CommonData" 메타데이터 하나로 LoadPrimaryAssets(..., CommonBundles, ...) 호출에 자동으로 딸려 들어오게 되어, 수동 로드 분기 코드 자체가 통째로 삭제됨.NSHubData)를 새로 만들지 않은 이유: 파츠가 인런에서도 필요한 이상 OutGame 전용 DA에 넣는 건 애초에 범위가 안 맞았음. 없는 클래스를 새로 만드는 것보다, 이미 있고 정상 작동 중인 Common 패턴에 얹는 게 더 적은 변경으로 올바른 구조에 도달하는 길이었음.LoadCommonData()
→ StartLoadCommon(): CommonDataConfigAssetType를 CommonBundles로 LoadPrimaryAssets
(PartsBaseStatTable/PartsSlotBaseStatTable가 AssetBundles="CommonData"라 자동으로 같이 로드됨)
→ OnCommonAssetsLoaded(): CacheLoaded() → BuildPartRowCache() → BuildSlotRowCache()
→ GetCommonDataConfig()로 DA를 찾고 DT를 꺼내 CachedPartRowsByDefId / CachedSlotRowsBySlot에 채움
Content/NeoSanctum/Data/Config/DA_CommonDataConfig.uasset을 열어 PartsBaseStatTable → DT_PartDefinition, PartsSlotBaseStatTable → DT_PartSlot 지정