본문으로 건너뛰기

버튼 상태 생성기 · hover·pressed·disabled·focus

버튼 하나로 다섯 가지 상태. 상태마다 밝기·대비·채도·덧씌우기·오프셋·외곽선·알파 연산이 순서대로 보이고 수정할 수 있습니다. 픽셀 계산일 뿐이라 원본에 없는 것을 그리지 않습니다.

게임 버튼에는 보통 normal·hover·pressed·disabled·focus 다섯 장의 이미지가 필요합니다. 이 생성기는 PNG 한 장에서 읽고 고칠 수 있는 픽셀 연산으로 다섯 장을 만듭니다. 기본값은 hover 밝기 +10%·채도 +8%, pressed 밝기 −10%와 1px 아래로, disabled 채도 완전 제거와 알파 50%, focus 2px #3182f6 링입니다. states/*.png, 패킹된 스트립 하나, 사용한 모든 값을 기록한 states.json을 내보냅니다. 이미 있는 픽셀만 바꾸며 새 그림을 그리지는 않습니다.

UI 패널·버튼·글리프 시트·UI 시트(PNG)를 끌어다 놓으세요
UI 랩 열기
브라우저에서 처리하며 파일은 업로드되지 않습니다.
UI 랩: 나인 슬라이스 가이드와 여러 크기 미리보기가 있는 CC0 UI 패널
CC0 에셋(Kenney)을 연 실제 UI 랩 화면입니다.
출력
  • Nine-slice측정 확인· Pillow
  • Bitmap font측정 확인· independent .fnt parser
  • Button states측정 확인· Pillow
  • Engine import미검증

상태 하나가 계산되는 방식

각 상태는 RGBA 픽셀에 적용하는 순서 있는 연산 목록입니다. 외곽선이 있으면 먼저 캔버스를 키우고, 밝기는 값 × 255를 R·G·B에 더하며, 대비는 중간 회색(128)을 기준으로 늘이고, 채도는 각 채널을 그 픽셀의 휘도(0.2126 R + 0.7152 G + 0.0722 B)에서 멀어지거나 가까워지게 합니다. 이어서 선택형 색 덧씌우기, 픽셀 이동, 모양을 네모로 팽창시킨 외곽선을 그리고, 마지막에 알파를 곱합니다. 완전히 투명한 픽셀은 다시 칠하지 않습니다.

엔진은 이 이미지들을 정해진 칸에 받습니다. Godot 4의 TextureButton에는 texture_normal, texture_hover, texture_pressed, texture_disabled, texture_focused가 있고, 자기 텍스처가 없는 disabled 버튼은 normal을 보여 주며, focused 텍스처는 기본 텍스처 위에 겹쳐 그려집니다. Unity UI Button은 Transition을 Sprite Swap으로 두면 Target Graphic의 기본 스프라이트와 Highlighted·Pressed·Disabled 스프라이트를 받습니다.

게임은 웹 페이지가 아니지만 WCAG 2.2의 두 규칙은 알아 둘 만합니다. 성공 기준 1.4.3은 비활성(disabled) 컴포넌트의 텍스트를 대비 최소 기준에서 제외하므로, 흐린 disabled 상태가 허용됩니다. 2.4.13 Focus Appearance(AAA)는 컴포넌트 둘레 2 CSS px 이상 크기의 포커스 표시와, 포커스 전후 픽셀 사이 3:1 대비를 요구합니다. 기본 링은 2px이고 #3182f6은 흰색 대비 3.71:1로 측정됩니다.

밝기
모든 채널에 더하는 값. +0.1은 +25.5단계입니다.
채도
+0.08은 각 채널을 휘도에서 8% 더 멀리, −1은 픽셀을 회색으로 만듭니다.
오프셋
같은 캔버스 안에서 픽셀을 옮깁니다. 밖으로 나간 것은 버리고 들어오는 곳은 투명합니다.
외곽선
불투명한 모양 둘레의 지정 색·두께 링. 캔버스가 그 두께만큼 커집니다.

예시: 픽셀 하나가 기본 상태 다섯 개를 거치면

색 (200, 120, 40)으로 채운 40 × 16 버튼입니다. 내보낸 PNG에 실제로 들어가는 값입니다.

상태       픽셀 (R, G, B, A)      크기     연산
normal     (200, 120, 40, 255)    40 × 16  없음
hover      (231, 145, 58, 255)    40 × 16  밝기 +0.1 (+25.5), 채도 +0.08
pressed    (174, 94, 14, 255)     40 × 16  밝기 −0.1, 오프셋 0,1 → 0번 줄이 투명해짐
disabled   (131, 131, 131, 128)   40 × 16  채도 −1, 알파 × 0.5
focus      (200, 120, 40, 255)    44 × 20  캔버스 변마다 +2px, 2px 링 (49, 130, 246)

disabled 회색  0.2126 × 200 + 0.7152 × 120 + 0.0722 × 40 = 131.2 → 131;  알파 255 × 0.5 = 127.5 → 128
hover 초록     휘도 156.7 + (145.5 − 156.7) × 1.08 = 144.6 → 145

focus 이미지는 다른 상태보다 가로·세로 4px 크고, 외곽선이 네모 팽창이라 모서리 픽셀까지 링이 덮습니다. states.json에 상태마다 크기와 여백이 기록되므로 차이에 놀랄 일은 없습니다.

이렇게 작동합니다

  • 기본값: hover 밝기 +10%, pressed −10%와 1px 아래로, disabled 채도 제거와 알파 50%, focus 2px 링.
  • 상태마다 모든 연산 값이 보이고 수정할 수 있습니다.
  • 출력: 상태마다 PNG 하나, 패킹된 스트립, 영역과 정확한 연산 값이 든 states.json.
  • 투명 픽셀은 다시 칠하지 않습니다.

사용 방법

  1. 버튼 PNG를 끌어다 놓습니다.
  2. 상태별 연산을 조절합니다.
  3. 다섯 미리보기를 나란히 확인합니다.
  4. ZIP으로 내보냅니다.

Godot·Unity에서 상태 쓰기

  1. Godot 4: TextureButton을 추가하고 normal.png·hover.png·pressed.png·disabled.png를 texture_normal·texture_hover·texture_pressed·texture_disabled에 지정합니다.
  2. Godot 4는 texture_focused를 기본 텍스처 위에 그리며, 문서는 외곽선처럼 일부가 투명한 이미지를 권합니다. focus.png에는 버튼 전체가 들어 있으므로 이 칸에 쓰려면 이미지 편집기에서 링 안쪽의 버튼을 지우고 44 × 20 캔버스를 유지하세요.
  3. Unity UI: Button의 Transition을 Sprite Swap으로 두고, Target Graphic의 Image에 normal.png를, Highlighted Sprite·Pressed Sprite·Disabled Sprite에 hover.png·pressed.png·disabled.png를 넣습니다.
  4. 버튼 크기가 바뀌어야 한다면, 상태에는 테두리가 없으므로 지정하기 전에 9슬라이스 편집기에서 상태 PNG마다 같은 나인 슬라이스 테두리를 주세요.

결과 확인

  • states.json의 모든 rect가 button-states.png에서 해당 상태를 정확히 잘라 내고, states.hover.ops에 사용한 값이 나열됩니다.
  • normal.png는 원본과 바이트 동일합니다(Firefox에서는 반투명 픽셀이 알파 반올림 한 단계만큼 다를 수 있음).
  • 게임을 실행해 버튼에 마우스를 올리고, 누르고, 비활성화해 봅니다. 튀는 곳이 없고 pressed는 오프셋만큼만 움직여야 합니다.

문제 해결

증상유력한 원인확인 방법해결
pressed 이미지의 맨 아래 줄이 사라짐1px 오프셋이 같은 캔버스 안에서 픽셀을 옮겨 마지막 줄이 밀려나고 0번 줄은 투명해짐pressed.png 아래 가장자리를 원본과 비교그림 아래에 투명한 줄을 하나 남기거나, 오프셋을 0으로 두고 엔진에서 글자를 움직이기
포커스된 버튼이 튀거나 겹쳐 보임focus.png가 더 크고(40 × 16 버튼이면 44 × 20) Godot는 focus 텍스처를 기본 위에 그림states.json → states.focus.size외곽선 두께만큼 음수로 옮겨 가운데 맞추거나, 위 설명처럼 링만 남긴 이미지 사용
어두운 배경에서 disabled 버튼이 거의 안 보임알파 0.5라 배경이 비쳐 보임흰 바탕이 아닌 실제 배경 위에서 확인disabled 알파를 0.7로 올리거나, 알파는 1로 두고 채도만 빼기
밝은 버튼에서 hover 차이가 거의 없음밝기는 더하는 방식이라 이미 255에 가까운 채널은 잘림원본 픽셀 값이 약 230 이상hover에 밝기 대신 색 덧씌우기나 대비 사용

다른 방법

방법이럴 때 더 낫습니다
Unity Transition = Color Tint상태가 색만 다를 때. 이미지가 더 필요 없지만, 색을 곱할 뿐이라 픽셀을 옮기거나(pressed 오프셋) 링을 더할 수 없습니다.
그림 프로그램에서 상태마다 직접 그리기모양이 달라질 때(뒤집히는 베벨, 바뀌는 아이콘, 빛). 픽셀 계산은 원본에 없는 것을 그릴 수 없습니다.

출력 파일과 검증 방법

"측정 확인"은 내려받은 파일을 페이지 밖에서(Pillow·numpy·별도 파서) 다시 열어 그 속성을 측정했다는 뜻입니다. 게임 엔진에서 불러오는 것은 시험하지 않았습니다.

대상파일검증
Nine-slicenine-slice.json · previews · SETUP.md측정 확인 Pillow크기를 바꾼 모든 미리보기에서 네 모서리 블록을 늘이지 않습니다. Chromium에서는 바이트 동일, Firefox에서는 반투명 픽셀이 알파 반올림 한 단계만큼 다를 수 있습니다. 빈틈·겹침 0.
Bitmap fontfont.png · font.fnt (BMFont text) · font.json측정 확인 independent .fnt parser테스트에 따로 작성한 파서로 .fnt를 다시 읽어 헤더와 모든 글리프 영역이 일치했습니다.
Button statesstates/*.png · strip · states.json측정 확인 Pillownormal은 원본과 같습니다(Chromium에서는 바이트 동일, Firefox에서는 반투명 픽셀의 색이 알파 반올림 한 단계 이내). states.json의 모든 영역이 스트립에서 해당 상태를 정확히 잘라 냅니다.
Engine importGodot NinePatchRect · Unity Sprite border · BMFont import미검증Godot·Unity에서 실행하지 않았습니다.

검증 근거

나인 슬라이스 모서리가 100×40·300×80·800×200·420×120에서 늘어나지 않음, 동일한 열 12개에서 테두리 6·6·6·6 제안. 버튼 상태: normal이 원본과 같음, disabled는 알파 128 회색, focus 링 정확. .fnt 글리프 레코드가 JSON과 일치. .po의 누락 글리프를 정확히 나열. 흰 바탕 검정 글자 21:1 측정. 이미지는 브라우저 캔버스를 거칩니다. Chromium은 바이트가 그대로이고, Firefox는 반투명 픽셀의 색을 미리 곱한 값 한 단계로 반올림합니다(Kenney 버튼에서 측정: 알파 63에서 최대 4단계). 알파와 불투명 픽셀은 그대로입니다.

한계

  • 상태별 나인 슬라이스 테두리는 없습니다. 9슬라이스 편집기에서 상태 PNG에 주세요.
  • 엔진에서 시험하지 않았습니다. 위의 칸 이름은 Godot·Unity 공식 문서에서 가져왔습니다.
  • 커닝 쌍 없음, 폰트 페이지 1장, MSDF 없음. SDF 출력은 베타입니다.
  • TTF의 cmap을 해석하지 않으므로 누락 글리프 점검에는 .fnt나 랩의 폰트가 필요합니다.
  • 아틀라스는 1페이지이고, 서로 닿은 요소는 하나로 셉니다.
  • Godot·Unity에서 렌더링해 보지 않았습니다. 엔진용 값(patch margin, L·B·R·T 테두리)은 문서 기준이며 엔진에서 시험하지 않았습니다.

버전과 출처

docs/UI-LAB.md를 위해 Chromium에서 40 × 16 버튼으로 측정했습니다. normal은 원본과 바이트 동일, hover는 더 밝고, pressed는 오프셋되며 비워진 줄은 투명, disabled는 알파 128의 회색, focus는 #3182f6 링이 있는 44 × 20이고 그림은 그대로였으며, states.json의 다섯 영역이 스트립에서 각 상태를 정확히 잘라 냈습니다. 위 예시 값은 같은 모듈로 다시 계산했습니다. Godot·Unity 가져오기는 실행하지 않았습니다.

참고한 공식 문서

자주 묻는 질문

focus 이미지가 더 큰 이유는?

focus 링이 외곽선 두께만큼 캔버스를 키우며, 그 크기는 states.json에 기록됩니다.

normal 상태도 바뀌나요?

아니요. normal은 원본과 바이트 동일합니다(확인함).

무료인가요? 계정이 필요한가요?

이 페이지의 기능은 모두 무료이며 계정 없이 쓸 수 있습니다. 워터마크나 막혀 있는 내보내기가 없습니다.

스프라이트가 업로드되나요?

아니요. Studio는 이 브라우저 탭 안에서 디코딩·패킹·내보내기를 합니다. 프로젝트는 브라우저(IndexedDB)에 자동 저장되며 .nerulio 파일로 저장할 수 있습니다.

Aseprite나 엔진 플러그인을 설치해야 하나요?

아니요. .aseprite 파일은 브라우저에서 읽고 씁니다. 엔진별 번들에는 그 엔진에 필요한 것이 들어 있습니다(Godot는 가져오기 설정과 씬, Unity와 Godot 타일셋은 작은 편집기 스크립트).