# 노드 맵 UI 강화 설계 작성일: 2026-06-15 브랜치: `feature/node-map-ui` ## 목표 맵 노드 선택 화면(`MapHud`)을 **단색 박스+텍스트** → **공식 메이플 아이콘 노드 + 배경 이미지**로 강화한다. 절차 랜덤 배치·간선·진행 로직은 그대로. 아이콘/배경은 **`data/nodeicons.json` 한 파일로 외부화**해 나중에 RUID만 바꿔 재생성하면 교체되도록 한다. 요청 원문: "노드 창이 단순 네모 박스안에 텍스트 … 백그라운드 이미지 삽입하고 특정 아이콘을 지정해서 노드로 … 랜덤 배치 … 노드 맵 UI 강화. 내가 나중에 변경할 수도 있으니 변경이 쉽게 가능하도록." ## 확정된 결정 (브레인스토밍) | 항목 | 결정 | |---|---| | 노드 표현 | **아이콘만**(박스 제거). 상태는 아이콘 틴트로 | | 배경 | **공식 메이플 배경 이미지** + 반투명 어두운 오버레이 | | 아이콘 세트 | 사용자 확정(아래 표). 공식 maplestory RUID, 썸네일 검수 완료 | | 변경 용이성 | 모든 RUID를 `data/nodeicons.json`로 외부화 → 편집+재생성으로 교체 | ### 확정 아이콘/배경 (공식 maplestory, 흰박스 위험 없음) | 노드 타입 | 아이콘 | RUID | |---|---|---| | combat(전투) | 주황버섯 | `f98db6823e894a4f90308d61f75894ac` | | elite(엘리트) | 돌골렘(Stumpy) | `793ed8a757534b89a82f460747d2df24` | | boss(보스) | 주니어 발록 | `423056cdbbc04f4da131b9721c404d96` | | shop(상점) | 보라 돈주머니 | `da37e1fac55d455b9ade08569f09f798` | | rest(휴식) | 모닥불 | `b86c1b0568bd45f3ae4a4b97e1b4a594` | | treasure(보물) | 금별 보물상자 | `f8a6d58e20f54e2ca899485055df1ce4` | | **background** | 리스항구 | `d84241f17de344a097f5b96ac914f1d2` | ## 현재 구조 (조사 결과) - `MapHud` 루트 = 1920×1080 **단색** 패널(`gen-slaydeck.mjs:1664`, 배경 이미지 없음) + 타이틀. - 노드 = `pushMapNode(id,pos,size,label)`(`:1696`) — `Node_{id}` 단색 박스(56×56, 보스 72×72) + `Label` 텍스트 자식. 그리드 `r1c1~r6c4`(24) + `boss`(`:1727`). - 타입 6종: combat/elite/shop/rest/treasure/boss. 타입→색/라벨은 **Lua `RenderMapNode`**(`:5626~5677`)가 런타임에 박스 `Color` + Label 텍스트로 채움. 상태 4단(현재 금색/방문 회색/도달 타입색/잠김 어둡게). - 절차 생성 `GenerateMap`(`:5505`) → `self.MapNodes[id]={type,row,col,next}`, id `r{r}c{c}`가 UI 엔티티와 1:1. 버튼 바인딩(`:3597`)은 경로 기반. - 이미지 주입 패턴: emit `sprite({dataId: RUID, type:0})`(`sprite()` 헬퍼 `:297`) / 런타임 `e.SpriteGUIRendererComponent.ImageRUID = ""`(`ApplyCardFace :4089`, chest `:5874`). 카드 프레임은 `data/cardframes.json`→`luaFramesTable()`(`:72`)→`self.CardFrames` Lua 테이블. ## 상세 설계 ### 1) `data/nodeicons.json` (신설 — 단일 소스) ```json { "icons": { "combat": "f98db6823e894a4f90308d61f75894ac", "elite": "793ed8a757534b89a82f460747d2df24", "boss": "423056cdbbc04f4da131b9721c404d96", "shop": "da37e1fac55d455b9ade08569f09f798", "rest": "b86c1b0568bd45f3ae4a4b97e1b4a594", "treasure": "f8a6d58e20f54e2ca899485055df1ce4" }, "background": "d84241f17de344a097f5b96ac914f1d2" } ``` - 사용자가 나중에 RUID만 바꾸고 `node tools/deck/gen-slaydeck.mjs` 재실행하면 교체됨. (README/주석에 명시.) ### 2) `gen-slaydeck.mjs` — 로드·검증·직렬화 - 상단에서 `NODEICONS = JSON.parse(readFileSync('data/nodeicons.json'))` 로드. - **fail-fast 검증**: `icons`에 6타입(combat/elite/boss/shop/rest/treasure) 전부 존재 + 32hex RUID, `background` 존재. 누락 시 throw(카드프레임 검증과 동일 패턴). - `luaNodeIconsTable()` 헬퍼: `self.NodeIcons = { combat="...", ... }` Lua 테이블 문자열. OnBeginPlay init에 주입(CardFrames 패턴, `:2906/3361` 인접). `prop('any','NodeIcons')` 선언. ### 3) MapHud emit 변경 - **배경 자식 `MapHud/Bg`**: 루트 직후 push. `uisprite`, 1920×1080, `dataId = NODEICONS.background`, `type:0`, 흰색, `raycast:false`, displayOrder 최하(0). 항상 enable. - **루트 오버레이**: 기존 루트 단색을 **반투명 어두운 오버레이**로(예: `{r:0.04,g:0.05,b:0.08,a:0.55}`)— 배경이 비치되 노드 가독성 확보. raycast 유지(뒤 월드 클릭 차단). - **`pushMapNode` → 아이콘 노드**: `Node_{id}` 본체를 박스 대신 **아이콘 스프라이트**로 — `sprite({ color:{1,1,1,1}, type:0, raycast:true })`(emit 시 dataId 미지정, 런타임에 타입별 ImageRUID 주입) + `button()`. **`Label` 자식 제거**(아이콘만). 노드 크기 키움: 그리드 64×64, 보스 88×88. (좌표 헬퍼 `nodeX/nodeY`·그리드 생성 루프·버튼 바인딩은 불변.) ### 4) RenderMapNode Lua 변경 - 타입→박스색/라벨 매핑(`:5630~5656`) 제거. 대신: - `e.SpriteGUIRendererComponent.ImageRUID = self.NodeIcons[type]` (없으면 combat 폴백). - 상태별 `Color` 틴트(박스가 아니라 **아이콘**에): - 현재(`CurrentNodeId`): `Color(1, 0.82, 0.3, 1)` 금색 - 도달가능: `Color(1, 1, 1, 1)` 원색 + `ButtonComponent.Enable=true` - 방문: `Color(0.5, 0.5, 0.55, 0.9)` 회색 - 잠김: `Color(0.4, 0.4, 0.45, 0.45)` 어둡고 반투명 + 버튼 비활성 - `SetText(.../Label ...)` 호출 제거(라벨 없음). 간선 도트(`RenderMapDots`)·`RenderMap` 루프는 불변. ### 5) 미러/테스트 영향 - 전투 규칙·맵 그래프 알고리즘 **미변경** → `sim-balance`/`rogue-map` 미러 동기화 불필요. - 검증(카운트): `MapHud/Bg` 1개, `NodeIcons` 주입, 노드 ImageRUID 주입 코드 존재, 6 RUID 등장. 내용 출력 금지(`tools/verify/count.mjs`). - 동작: 메이커 플레이테스트(아이콘 렌더·상태 틴트·랜덤 배치·노드 클릭 진행). ## 리스크 - **아이콘 비정사각/큰 스프라이트** → 64px UI에서 잘림/왜곡 가능(보스 발록은 확인됨, 골렘·버섯은 정사각 양호). type 0 렌더의 aspect 처리 확인, 필요 시 노드별 size 패딩 조정. - **아이콘만 상태 가독성**: 잠김/방문 틴트 대비가 약하면 플레이테스트로 알파/명도 튜닝. - **배경 오버레이 알파**: 너무 밝으면 노드가 묻힘 — 0.5~0.65 사이 튜닝. - 흰박스: 전부 공식 maplestory(검증) — 위험 없음. 단 로컬 워크스페이스 reload 필요. ## 변경 파일 요약 | 파일 | 변경 | |---|---| | `data/nodeicons.json` | **신설** — 아이콘 6 + 배경 RUID (단일 소스) | | `tools/deck/gen-slaydeck.mjs` | 로드·검증·luaNodeIconsTable, MapHud Bg/오버레이, pushMapNode 아이콘화, RenderMapNode ImageRUID+틴트 | | `ui/DefaultGroup.ui`·`SlayDeckController.codeblock` | 재생성 산출물 |