화면 위젯 — 진행 바와 슬롯 시각화
CrusherScreen의 extractContents에 진행 바를 그리고, 슬롯 호버 강조와 텍스트 렌더링으로 GUI를 완성합니다.
화면 위젯 — 진행 바와 슬롯 시각화
이 챕터에서는 분쇄기 GUI에 시각적 피드백을 추가합니다. 진행 바로 가공 상태를 표시하고, 슬롯 호버 강조와 텍스트 렌더링으로 플레이어가 GUI 상태를 한눈에 파악할 수 있게 만듭니다.
03-menu-screen에서 만든 기본
CrusherScreen은extractContents에서 배경 텍스처만 그립니다. 이 챕터의 진행 바·호버 강조·텍스트는 그 위에 얹는 확장 예시입니다.
1. 텍스처 좌표 시스템 이해
NeoForge 26.1.2의 GuiGraphicsExtractor.blit은 텍스처 영역을 **정규화 UV(0.0~1.0)**로 지정합니다. 픽셀 좌표가 아니라 픽셀 / 텍스처크기 비율을 넘깁니다.
blit(texture, screenX, screenY, width, height, u0, v0, u1, v1)
│ │ │ │ │ └─────┬─────┘
│ 화면 좌표 그릴 크기 정규화 UV (0.0~1.0)
└── Identifier (텍스처 경로)
256×256 텍스처에서 픽셀 (u, v)는 정규화하면 u/256, v/256 입니다.
| 영역 | 텍스처 픽셀 | 정규화 UV |
|---|---|---|
| GUI 배경 | (0, 0) ~ (176, 166) | 0.0, 0.0 ~ 1.0, 1.0 (텍스처 전체) |
| 진행 바 (채워진 상태) | (176, 14) ~ (200, 30) | 0.6875, 0.0547 ~ 0.7813, 0.1172 |
왜 176부터 시작하나?
GUI 배경이
imageWidth = 176을 차지하므로, 진행 바 화살표 텍스처는 같은 시트의 오른쪽(u = 176)에 배치하면 배경과 겹치지 않습니다. (배경 전용 텍스처를 쓴다면 화살표용 시트를 별도로 확장하세요.)
2. extractContents — 진행 바 렌더링
26.1.2에서 배경·위젯을 그리는 메서드는 extractContents(GuiGraphicsExtractor gg, int mx, int my, float partialTick) 입니다(구버전 renderBg가 이 메서드로 바뀌었습니다). 배경 텍스처를 먼저 그린 뒤, progress 값에 비례한 너비로 진행 바를 덧그립니다.
// CrusherScreen.java — 위젯 확장 예시 (기본 extractContents 에 진행 바를 더한 버전)
@Override
public void extractContents(GuiGraphicsExtractor gg, int mx, int my, float partialTick) {
super.extractContents(gg, mx, my, partialTick);
// 1. GUI 배경 텍스처 전체 출력 (정규화 UV 0.0~1.0)
gg.blit(TEXTURE, leftPos, topPos, imageWidth, imageHeight, 0.0f, 0.0f, 1.0f, 1.0f);
// 2. 진행 바 (가로 길이를 progress에 비례)
int progress = menu.blockEntity.getProgress(); // 0 ~ 200
int maxProgress = 200;
int barWidth = 24;
int filledWidth = (progress * barWidth) / maxProgress;
// 채워진 화살표 영역(텍스처 u=176, v=14)을 filledWidth 만큼만 — UV는 256px 기준 정규화
gg.blit(TEXTURE,
leftPos + 79, topPos + 35, // 화면 좌표 (GUI 중앙 부근)
filledWidth, 16, // 동적 너비 × 고정 높이
176f / 256f, 14f / 256f, // u0, v0 (채워진 화살표 시작)
(176f + filledWidth) / 256f, 30f / 256f); // u1, v1
}좌표 계산 방법
leftPos와 topPos는 AbstractContainerScreen이 자동으로 계산하는 GUI 좌상단 절대 좌표입니다. 진행 바 위치는 항상 이 값에 상대 오프셋을 더해 지정합니다.
화면 절대 좌표 = leftPos + 상대 X
topPos + 상대 Y
3. render — 슬롯 호버 강조
render는 extractContents보다 나중에 호출됩니다. 마우스 아래 슬롯을 감지해 반투명 오버레이를 그립니다. 26.1.2에서는 렌더 파라미터 타입도 GuiGraphicsExtractor 입니다.
// CrusherScreen.java — 위젯 확장 예시
@Override
public void render(GuiGraphicsExtractor gg, int mx, int my, float partialTick) {
renderBackground(gg, mx, my, partialTick);
super.render(gg, mx, my, partialTick);
renderTooltip(gg, mx, my);
// 호버 슬롯 강조 overlay
Slot slot = getSlotUnderMouse();
if (slot != null && !slot.getItem().isEmpty()) {
gg.fill(
slot.x + leftPos, slot.y + topPos,
slot.x + leftPos + 16, slot.y + topPos + 16,
0x80FFFFFF // ARGB: 알파 50%, 흰색
);
}
}0x80FFFFFF는 ARGB 형식입니다. 앞의 0x80이 알파값(약 50% 투명)이고, 뒤의 FFFFFF가 흰색입니다.
📷 스크린샷 자리 (직접 캡처해 추가하세요)
CrusherScreen — 슬롯 호버 강조 및 툴팁 렌더링
4. drawString — 텍스트 렌더링
진행률을 숫자로 표시하려면 drawString을 사용합니다. (gg는 extractContents/render가 받는 GuiGraphicsExtractor 입니다.)
// extractContents 안에서 호출 (배경 위, 아이템 아래)
int progress = menu.blockEntity.getProgress();
int percent = progress * 100 / 200;
gg.drawString(font, percent + "%", leftPos + 8, topPos + 5, 0x404040);| 파라미터 | 설명 |
|---|---|
font | AbstractContainerScreen이 제공하는 폰트 렌더러 |
| 텍스트 | 표시할 문자열 |
leftPos + 8 | 화면 X 좌표 |
topPos + 5 | 화면 Y 좌표 |
0x404040 | 텍스트 색상 (어두운 회색) |
5. 안티패턴 — 매 프레임 객체 생성 금지
⚠️ 매 프레임 새 객체 생성 → GC 압박
// 매 렌더마다 String 생성 (60fps × String.format) gg.drawString(font, "Progress: " + String.format("%.2f", progress / 200.0 * 100) + "%", ...);60fps 기준으로 초당 60번
String.format이 호출되고, 매번 새String객체가 힙에 쌓입니다. GC가 자주 발동하면 프레임 드롭이 생깁니다.// progress 변경 시에만 갱신 + 캐시 private String progressLabel = ""; private int lastProgress = -1; // extractContents 또는 tick에서 if (lastProgress != progress) { progressLabel = (progress * 100 / 200) + "%"; lastProgress = progress; } gg.drawString(font, progressLabel, leftPos + 8, topPos + 5, 0x404040);
progress가 바뀔 때만 문자열을 새로 만들고, 나머지 프레임에서는 캐시된 값을 재사용합니다.
6. 렌더 호출 순서
Minecraft GUI 렌더링은 레이어 순서가 중요합니다.
render()
├── renderBackground() ← 어두운 배경 오버레이
├── super.render()
│ ├── extractContents() ← 텍스처 + 진행 바 (이 챕터)
│ └── 슬롯 아이템 렌더링
├── 슬롯 호버 강조 ← 아이템 위에 오버레이 (이 챕터)
└── renderTooltip() ← 툴팁 (최상단)
extractContents에서 그린 진행 바는 슬롯 아이템보다 아래에 위치합니다. 슬롯 강조 오버레이는 super.render() 이후에 그려야 아이템 위에 올라옵니다.
7. 기본 CrusherScreen (검증된 소스)
다음은 03-menu-screen에서 만든 실제 빌드되는 기본 화면입니다. 위의 진행 바·호버 강조·텍스트는 이 extractContents 위에 얹는 확장입니다.
// examplemod-master-projects/machine/src/main/java/com/example/master/machine/client/CrusherScreen.java
package com.example.master.machine.client;
import com.example.master.machine.menu.CrusherMenu;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.player.Inventory;
public class CrusherScreen extends AbstractContainerScreen<CrusherMenu> {
private static final Identifier TEXTURE =
Identifier.fromNamespaceAndPath("master_machine", "textures/gui/crusher.png");
public CrusherScreen(CrusherMenu menu, Inventory inv, Component title) {
super(menu, inv, title, 176, 166);
}
@Override
public void extractContents(GuiGraphicsExtractor gg, int mx, int my, float partialTick) {
super.extractContents(gg, mx, my, partialTick);
gg.blit(TEXTURE, leftPos, topPos, imageWidth, imageHeight, 0.0f, 0.0f, 1.0f, 1.0f);
}
}위젯(진행 바·호버 강조·텍스트)을 더하려면 2~4절의 코드를 이 extractContents/render에 통합하고, Slot import(net.minecraft.world.inventory.Slot)와 진행률 캐시 필드를 추가합니다.
전체 파일 구조
이번 챕터 완료 후 machine 프로젝트의 주요 파일 구조입니다.
examplemod-master-projects/machine/src/main/java/com/example/master/machine/
├── MasterMachineMod.java
├── block/
│ ├── CrusherBlock.java
│ └── CrusherBlockEntity.java
├── menu/
│ └── CrusherMenu.java ← 03-menu-screen에서 구현
└── client/
└── CrusherScreen.java ← 이번 챕터에서 완성다음 단계
- 05-recipe-type — 커스텀
RecipeType으로 분쇄 레시피 데이터팩 지원 - 06-server-tick —
ServerLevel.getBlockEntity()+ tick으로 실제 가공 로직 완성