기능 구현은 끝났고, 죽은 코드/중복 로직을 정리하는 작업을 함. 시작할 때 "이 정도면 끝나겠지" 싶었던 게, 파다 보니 확신했던 결론을 두 번 세 번 뒤집는 일이 계속 생겼음. 특히 하이라이트 색상 처리 하나 때문에 ButtonComponent API를 실측까지 해가며 파고든 게 오늘의 메인 이벤트.
UIBuffIcon 타이머 — 버그라고 확신했다가 철회SetTimerRepeat으로 등록한 반복 타이머라, remainingSec이 0이 돼도 타이머 자체는 안 멈추는 게 당연해 보였음. "0이 된 뒤에도 Tick이 계속 불려서 낭비 아닌가?" 싶어서 방어 코드(ClearTimer 명시적 호출)를 제안했는데, 로그 + 브레이크포인트로 직접 찍어보니 remainingSec이 0이 되는 시점엔 Tick이 아예 호출되지 않음을 확인함.
공식 문서에 있는 문구 때문이었음:
예약된 액션의 소유자가 파괴될 경우 내부에서 자동으로
ClearTimer()함수를 시도합니다.
HideBuff가 부르는 icon:Destroy() 시점에 엔진이 타이머 소유자 파괴를 감지해서 알아서 정리해주고 있었던 것. 정황상 의심스러워 보이는 코드도 실측 없이는 "버그"로 단정하면 안 된다는 걸 다시 확인함. 결국 방어 코드는 추가하지 않고 원래 코드 그대로 유지, 오히려 "혹시 몰라서" 넣어뒀던 방어 코드도 도달 불가능한 지점이라 판단해서 뺐음.
처음엔 단순하게 "여러 파일에 중복된 하이라이트 색상 상수를 Logic으로 전역화하자"는 생각으로 시작했는데, 파다 보니 완전히 다른 결론에 도달함.
1단계 — ButtonComponent에 SelectedColor가 있다는 걸 실측으로 확인. 클릭 뗀 뒤에도 색이 유지되고, 다른 버튼 누르면 자동으로 꺼짐. 그래서 디버그 패널의 아이템 버튼(UIDebugItemSlot.SetHighlight)은 이걸로 완전히 대체함 — 단, Scale 확대(1.15배) 연출은 ButtonComponent가 못 해주는 커스텀 연출이라 별도로 유지해야 했음.
2단계 — 같은 논리를 인벤토리 카테고리 탭에도 적용하려다가 실측에서 걸림. 최초 진입 시 기본 탭(장비 탭)은 실제 클릭이 발생한 적이 없어서 SelectedColor가 절대 켜지지 않음. "그럼 최초 1회만 수동으로 색을 칠해주면 되지 않나" 싶어서 SpriteGUIRendererComponent.Color를 직접 대입했는데, 장비 탭만 영원히 노란색으로 박제되는 버그가 남. ButtonComponent가 관리하는 상태와 완전히 별개의 값을 건드린 거라, 나중에 다른 탭을 눌러도 자기가 켠 적 없는 값을 되돌려줄 이유가 없었던 것.
ButtonComponent를 코드로 강제 클릭시키는 API도 찾아봤지만 없었음(ButtonComponent는 출력 기능이 없습니다 — 입력만 받고 상태를 밖에서 주입하는 경로 자체가 설계에 없음). 결국 카테고리 탭은 원래 방식(매번 전체 탭을 순회하며 다시 칠하는 방식)을 그대로 유지하기로 함. 다만 매직넘버 Color(1.0, 0.85, 0.3, 1.0)는 normalTabImageRUID/selectedTabImageRUID(string) 프로퍼티로 바꿔서, 색상 대신 스프라이트를 교체하는 방식으로 전환 — 디자이너가 인스펙터에서 이미지를 갈아끼울 수 있게 됨.
method void UpdateCategoryTabHighlight()
if self._T.categoryTabEntities == nil then return end
for category, tabEntity in pairs(self._T.categoryTabEntities) do
if tabEntity.SpriteGUIRendererComponent ~= nil then
tabEntity.SpriteGUIRendererComponent.ImageRUID =
(category == self.currentCategory) and self.selectedTabImageRUID or self.normalTabImageRUID
end
end
end
UIMyInfo의 hpBar/mpBar/hpText/mpText 등이 전부 Entity 타입 프로퍼티였는데, 실제로는 .UITransformComponent나 .TextComponent를 매번 다시 꺼내 쓰는 용도였음. UIItemSlot처럼 스폰되는 템플릿이면 런타임에 자식을 찾아야 하니 어쩔 수 없지만, UIMyInfo는 화면에 고정으로 하나만 배치되는 싱글턴 UI라 애초에 Component 타입 프로퍼티로 선언하고 에디터에서 직접 연결하면 됐음.
같은 패턴이 ExtendPlayerComponent/ExtendMovementComponent/PlayerStats의 statUIEntity에도 있었음 — 셋 다 .UIMyInfo만 꺼내 쓰고 있어서 Component 타입(statUIInfo)으로 통일. Entity로 받아야 할 이유(SetVisible 같은 엔티티 자체 기능 사용)가 있는지 없는지가 판단 기준이 됨.
프로퍼티 — MapleStory Worlds Creator Center
_T 프로퍼티의 정확한 정의(선언 시점, 동기화 불가)를 확인한 문서. _T와 정식 프로퍼티 중 무엇을 쓸지 판단하는 근거가 됨.
TimerService — MapleStory Worlds Creator Center
반복 타이머가 소유자 파괴 시 자동으로 정리 시도된다는 "유의 사항"을 확인한 문서. UIBuffIcon 버그 오판을 바로잡은 근거.
기본 UI 컴포넌트 — MapleStory Worlds Creator Center
ButtonComponent가 ButtonState(Normal/Hover/Pressed/Released/Clicked)로만 동작하고 "출력 기능이 없다"는 걸 확인한 문서. 카테고리 탭에 SelectedColor를 못 쓰는 이유의 근거가 됨.