9슬라이스 편집기 · Godot·Unity용 나인 패치 테두리
테두리는 원본 픽셀 기준 숫자 네 개입니다. 편집기는 가장 긴 동일 열·행 구간에서 값을 제안하고, 드래그·입력·미세 이동을 지원하며, 모든 목표 크기를 모서리가 변하지 않게 그립니다.
나인 슬라이스(9패치) 테두리는 원본 픽셀 기준 숫자 네 개(왼쪽·오른쪽·위·아래)로, 이미지를 3 × 3 격자로 나눕니다. 네 모서리는 항상 원래 크기로 그려지고, 위·아래 변은 가로로만, 왼쪽·오른쪽 변은 세로로만 늘어나거나 반복되며, 가운데는 양쪽으로 변합니다. 여기서 테두리를 한 번 정하고(동일한 열·행에서 제안), 원하는 크기로 미리 본 뒤, 같은 숫자를 Godot 4(NinePatchRect.patch_margin_*), Unity(Sprite Editor 테두리, Image Type Sliced), Phaser(add.nineslice)에 넣으면 됩니다. 결과물은 PNG + nine-slice.json + SETUP.md이며 엔진 안에서의 렌더링은 실행해 보지 않았습니다.

- Nine-slice측정 확인· Pillow
- Bitmap font측정 확인· independent .fnt parser
- Button states측정 확인· Pillow
- Engine import미검증
아홉 영역과 각 영역이 하는 일
W × H 이미지에 테두리 L·R·T·B를 주면 모서리 넷(L × T, R × T, L × B, R × B), 너비 W − L − R인 위·아래 변, 높이 H − T − B인 왼쪽·오른쪽 변, 그리고 가운데가 생깁니다. 패널 크기를 바꾸면 모서리는 그대로 복사되고, 가로 변은 너비만, 세로 변은 높이만, 가운데는 양쪽이 바뀝니다. 그래서 둥글거나 경사진 모서리가 어떤 크기에서도 유지되며, 모든 모서리를 온전히 보여 주는 최소 크기는 (L + R) × (T + B)입니다.
늘이기(Stretch)는 변이나 가운데를 새 길이로 리샘플링하므로 단색이나 반대 축 방향 그라데이션에 맞습니다. 반복(Tile)은 원래 크기로 되풀이해 무늬(리벳, 바느질, 벽돌 테두리)가 일그러지지 않으며, Nerulio는 마지막 조각을 잘라 냅니다. Godot 4에는 조각이 모두 온전히 보이도록 살짝 늘이는 Tile Fit도 있지만 Nerulio 미리보기는 이 모드를 그리지 않습니다.
엔진마다 같은 숫자 네 개를 다른 이름과 순서로 저장합니다. Godot 4는 NinePatchRect의 patch_margin_left/right/top/bottom(정수, 픽셀)과 StyleBoxTexture의 texture_margin_*를 씁니다. Unity Sprite Editor에는 L·R·T·B 칸이 있지만, 스크립트 값 Sprite.border는 왼쪽·아래·오른쪽·위 순서의 Vector4입니다. Phaser NineSlice는 크기 뒤에 leftWidth, rightWidth, topHeight, bottomHeight를 받고, 왼쪽·오른쪽 폭만 주면 3슬라이스가 됩니다.
- 테두리(patch margin)
- 이미지 한쪽 끝에서 자르는 선까지의 거리(원본 픽셀).
- 늘이기(Stretch)
- 변이나 가운데를 새 길이에 맞게 확대·축소.
- 반복(Tile)
- 변이나 가운데를 원래 크기로 되풀이.
- 최소 크기
- (L + R) × (T + B). 이보다 작으면 모서리를 온전히 그릴 수 없어 찌그러집니다.
예시: 48 × 48 패널을 300 × 80으로
모든 변의 테두리가 12px입니다. 편집기의 그리기 계획이 만드는 사각형이며, 내보낸 미리보기도 같은 계획을 씁니다.
원본 48 × 48 테두리 L 12 · R 12 · T 12 · B 12 목표 300 × 80 늘이기
모서리 4 × (12×12 → 12×12) 1:1 복사, 리샘플링 없음
위/아래 24×12 → 276×12 276 = 300 − 12 − 12 (가로로만 ×11.5)
왼/오른 12×24 → 12×56 56 = 80 − 12 − 12 (세로로만 ×2.33)
가운데 24×24 → 276×56
반복 모드 위 변 = 24px 조각 11개 + 잘린 12px 조각 1개 (11 × 24 + 12 = 276)
너무 작음 목표 20 × 80: 20 < 12 + 12 → 모서리가 10 + 10으로 찌그러짐 (경고 "narrow")
비대칭 L 10 · R 6 · T 8 · B 14 → Unity Sprite.border (x, y, z, w) = (L, B, R, T) = (10, 14, 6, 8)
Godot patch_margin_left 12 · right 12 · top 12 · bottom 12
Phaser this.add.nineslice(x, y, "panel", null, 300, 80, 12, 12, 12, 12)정수 배율 2를 쓰면 모서리가 정확히 24 × 24로(원본 픽셀 하나가 2 × 2로) 그려져, 픽셀 아트 테두리가 두 배 크기에서도 선명합니다.
이렇게 작동합니다
- 동일한 열·행으로 테두리 제안. 근거 구간을 함께 보여 주며 몰래 적용하지 않습니다.
- 가장자리 늘이기 또는 반복. 반복은 원래 크기로 되풀이되고 마지막 조각은 잘립니다.
- 모든 크기에서 모서리를 늘이지 않습니다(100×40·300×80·800×200·420×120에서 확인). Chromium에서는 바이트 동일, Firefox에서는 반투명 모서리 픽셀이 알파 반올림 한 단계만큼 다를 수 있습니다.
- 내보내기: 미리보기, 픽셀·정규화 값·Godot patch_margin_*·Unity border(L·B·R·T)가 든 nine-slice.json, SETUP.md.
사용 방법
- 패널이나 버튼 PNG를 끌어다 놓습니다.
- 제안된 테두리를 받아들이거나 드래그·입력합니다(화살표 1px, Shift 10px).
- 목표 크기로 늘이기·반복을 미리 봅니다.
- ZIP으로 내보냅니다.
받게 되는 파일
| 파일 | 용도 |
|---|---|
panel.png | 원본 이미지. 바꾸지 않고 파일 이름을 따릅니다. |
previews/100x40.png, 300x80.png, 800x200.png + custom size | 선택한 모드와 배율로 각 미리보기 크기에 그린 패널(사용자 크기 기본값 420 × 120). |
nine-slice.json | 테두리를 pixels, normalized(테두리 ÷ 너비 또는 높이), godot4(patch_margin_*), unity(L·B·R·T 순서의 border, 순서 이름 포함)로 담고, 모드·배율·미리보기 크기도 기록. |
SETUP.md | 숫자를 채운 Godot 4·Unity 설정 단계. 엔진 안에서 실행하지 않았다고 표시. |
Godot·Unity·Phaser에 테두리 넣기
- Godot 4:
NinePatchRect를 추가하고 Texture에 PNG를,patch_margin_left·patch_margin_right·patch_margin_top·patch_margin_bottom에nine-slice.json의 숫자 네 개를 넣습니다. - Godot 4 반복 모드:
axis_stretch_horizontal과axis_stretch_vertical을 Tile(또는 Tile Fit)로 둡니다. Button·Panel 테마라면StyleBoxTexture의texture_margin_*에 같은 숫자를, 축 늘이기도 같게 설정합니다. - Unity: 텍스처를 선택해 Mesh Type을 Full Rect로 바꾸고 Apply한 뒤, Sprite Editor를 열어 초록 핸들을 끌거나 L·R·T·B 칸에
pixels값을 입력합니다. - Unity: Sprite Renderer라면 Draw Mode를 Sliced(또는 Tiled, Tile Mode는 Continuous나 Adaptive)로, UI Image라면 Image Type을 Sliced(또는 Tiled)로 둡니다.
- Phaser 3.60 이상:
this.add.nineslice(x, y, key, frame, width, height, leftWidth, rightWidth, topHeight, bottomHeight). Phaser 4.1 API에는 늘이지 않고 반복하는tileX·tileY가 있습니다. - 픽셀 아트라면 Nearest 필터를 유지하고 원본 픽셀의 정수배로 크기를 바꿔 모서리를 선명하게 두세요.
결과 확인
- 엔진 노드를 300 × 80으로 바꾸고
previews/300x80.png와 비교합니다. 12 × 12 모서리 네 개가 픽셀 단위로 같아야 합니다. - L + R보다 좁게 줄여 봅니다. 같은 크기에서 Nerulio는 "narrow" 경고를 내고, Phaser 문서도 L + R을 최소 너비로 적고 있습니다.
- Unity에서
Debug.Log(sprite.border)가 JSON의unity.border숫자(왼쪽·아래·오른쪽·위)를 출력해야 합니다.
문제 해결
| 증상 | 유력한 원인 | 확인 방법 | 해결 |
|---|---|---|---|
| Unity에서 모서리가 늘어남 | Mesh Type이 Tight이거나 Image·Sprite Renderer가 아직 Simple로 그림 | 텍스처 가져오기 설정과 Image Type 또는 Draw Mode | Mesh Type을 Full Rect로 바꾸고 Apply한 뒤 Image Type Sliced나 Draw Mode Sliced로 |
| Unity에서 위·아래 테두리가 뒤바뀐 듯 보임 | Vector4 순서(왼쪽·아래·오른쪽·위)를 L·R·T·B 칸에 넣었거나 그 반대 | Sprite Editor 칸과 JSON의 pixels 비교 | Sprite Editor에는 pixels를 입력하고, unity.border는 스크립트의 Sprite.border에만 사용 |
| 작은 크기에서 모서리가 찌그러짐 | 컨트롤이 L + R 또는 T + B보다 작음 | Nerulio가 그 크기에 "narrow"·"short" 경고 표시 | 컨트롤을 키우거나 테두리를 줄이기 |
| 무늬가 있는 변이 번져 보임 | 변을 늘여서 반복 무늬가 리샘플링됨 | 미리보기를 반복으로 바꿔 비교 | Godot은 축 늘이기 Tile·Tile Fit, Unity는 Tiled, Phaser 4는 tileX·tileY |
| 픽셀 아트 모서리가 흐림 | Linear 필터이거나 배율이 정수가 아님 | 확대하면 모서리 계단이 부드러움 | Nearest 필터와 정수 배율(편집기 배율은 1~4) |
| TexturePacker 아틀라스의 Phaser NineSlice가 이상함 | 프레임이 트림됨. Phaser 문서는 NineSlice가 트림된 TexturePacker 텍스처를 지원하지 않는다고 적음 | 아틀라스 JSON에서 트림된 프레임 확인 | 패널을 트림 없이 패킹하거나 별도 이미지로 불러오기 |
| "테두리 제안"이 뚜렷한 구간을 못 찾음 | 가운데가 그라데이션이나 노이즈라 같은 열·행이 없음 | 제안에 반복되는 열·행이 없다고 나옴 | 모서리 그림이 끝나는 곳에 가이드를 직접 놓기 |
다른 방법
| 방법 | 이럴 때 더 낫습니다 |
|---|---|
| Godot NinePatchRect 인스펙터나 Unity Sprite Editor에서 직접 설정 | 패널이 한두 개이고 이미 엔진에서 작업할 때. 끌면서 실제 엔진 렌더링을 봅니다. |
| UI 랩의 아틀라스 단계 | 한 시트에 패널이 많을 때. 요소마다 테두리를 주고 모두 ui-atlas.json 하나에 담깁니다. |
출력 파일과 검증 방법
"측정 확인"은 내려받은 파일을 페이지 밖에서(Pillow·numpy·별도 파서) 다시 열어 그 속성을 측정했다는 뜻입니다. 게임 엔진에서 불러오는 것은 시험하지 않았습니다.
| 대상 | 파일 | 검증 |
|---|---|---|
| Nine-slice | nine-slice.json · previews · SETUP.md | 측정 확인 Pillow크기를 바꾼 모든 미리보기에서 네 모서리 블록을 늘이지 않습니다. Chromium에서는 바이트 동일, Firefox에서는 반투명 픽셀이 알파 반올림 한 단계만큼 다를 수 있습니다. 빈틈·겹침 0. |
| Bitmap font | font.png · font.fnt (BMFont text) · font.json | 측정 확인 independent .fnt parser테스트에 따로 작성한 파서로 .fnt를 다시 읽어 헤더와 모든 글리프 영역이 일치했습니다. |
| Button states | states/*.png · strip · states.json | 측정 확인 Pillownormal은 원본과 같습니다(Chromium에서는 바이트 동일, Firefox에서는 반투명 픽셀의 색이 알파 반올림 한 단계 이내). states.json의 모든 영역이 스트립에서 해당 상태를 정확히 잘라 냅니다. |
| Engine import | Godot 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단계). 알파와 불투명 픽셀은 그대로입니다.
한계
- Godot·Unity·Phaser 안에서 렌더링해 보지 않았습니다. 숫자는 엔진 문서와 맞춘 것이지 엔진 시험 결과가 아닙니다.
- 엔진 파일(
.tres,.meta, Android.9.png)은 쓰지 않습니다. - 1보다 큰 정수 배율의 반복 모드에서는 마지막 조각이 원본 픽셀의 (배율 − 1) ÷ 배율만큼 찌그러질 수 있습니다.
- 커닝 쌍 없음, 폰트 페이지 1장, MSDF 없음. SDF 출력은 베타입니다.
- TTF의 cmap을 해석하지 않으므로 누락 글리프 점검에는 .fnt나 랩의 폰트가 필요합니다.
- 아틀라스는 1페이지이고, 서로 닿은 요소는 하나로 셉니다.
- Godot·Unity에서 렌더링해 보지 않았습니다. 엔진용 값(patch margin, L·B·R·T 테두리)은 문서 기준이며 엔진에서 시험하지 않았습니다.
버전과 출처
docs/UI-LAB.md를 위해 Chromium에서 측정했습니다. 내보낸 모든 미리보기의 6 × 6 모서리 블록이 100 × 40, 300 × 80, 800 × 200, 420 × 120에서 원본과 바이트 동일했고, 그리기 계획은 300 × 80, 배율 3, 8 × 4로 찌그러뜨린 경우 모두 빈틈·겹침 0이었습니다. 줄무늬 12 × 12 패널은 4px 주기로 반복되고, 제안은 동일한 열·행 12개 구간에서 6 · 6 · 6 · 6을 찾았습니다. Firefox에서는 반투명 모서리 픽셀이 알파 반올림 한 단계만큼 다를 수 있습니다. Godot·Unity 가져오기는 UNVERIFIED입니다. 위 엔진 설정은 Godot 4.7, Unity 6, Phaser API 공식 문서를 따릅니다.
참고한 공식 문서
자주 묻는 질문
Unity는 어떤 순서를 쓰나요?
Unity 스크립트 값 Sprite.border는 왼쪽·아래·오른쪽·위 순서의 Vector4이며, JSON에 그 순서로 쓰고 이름을 붙입니다. 스프라이트 편집기 자체는 같은 숫자를 L·R·T·B 칸으로 보여 주므로 거기에는 픽셀 값을 입력하세요.
Godot·Unity 안에서 시험했나요?
아니요. 값은 두 엔진이 문서화한 것이며 엔진에서 렌더링하지는 않았습니다.
무료인가요? 계정이 필요한가요?
이 페이지의 기능은 모두 무료이며 계정 없이 쓸 수 있습니다. 워터마크나 막혀 있는 내보내기가 없습니다.
스프라이트가 업로드되나요?
아니요. Studio는 이 브라우저 탭 안에서 디코딩·패킹·내보내기를 합니다. 프로젝트는 브라우저(IndexedDB)에 자동 저장되며 .nerulio 파일로 저장할 수 있습니다.
Aseprite나 엔진 플러그인을 설치해야 하나요?
아니요. .aseprite 파일은 브라우저에서 읽고 씁니다. 엔진별 번들에는 그 엔진에 필요한 것이 들어 있습니다(Godot는 가져오기 설정과 씬, Unity와 Godot 타일셋은 작은 편집기 스크립트).