특수 블록 타입 (계단·슬랩·펜스)
StairBlock, SlabBlock, FenceBlock을 등록하고 각 타입에 맞는 blockstate variant 패턴을 작성하는 방법을 학습합니다. variant 누락 시 보라/검정 큐브가 나타나는 원인과 해결법도 다룹니다.
특수 블록 타입 (계단·슬랩·펜스)
단순 정육면체 블록을 넘어서면 Minecraft가 제공하는 특수 블록 서브클래스를 활용할 수 있습니다. 이 챕터에서는 가장 자주 쓰이는 세 가지를 다룹니다.
| 클래스 | 용도 | blockstate 방식 |
|---|---|---|
StairBlock | 계단 형태 | variants (facing × half × shape) |
SlabBlock | 반 블록 | variants (type) |
FenceBlock | 펜스 연결 | multipart (방향 boolean) |
세 클래스 모두 바닐라 구현을 그대로 상속하므로, 등록 코드는 짧고 blockstate JSON 작성이 핵심입니다.
1. StairBlock 등록
1-1. 등록 코드
public static final Supplier<StairBlock> RUBY_STAIRS = BLOCKS.register("ruby_stairs",
() -> new StairBlock(RUBY_BLOCK.get().defaultBlockState(),
BlockBehaviour.Properties.ofFullCopy(RUBY_BLOCK.get())));StairBlock 생성자의 첫 번째 인자는 기준 블록의 기본 상태입니다. 계단이 "이 블록의 계단 버전"임을 Minecraft에 알려주는 역할을 합니다. 두 번째 인자는 기준 블록의 속성을 그대로 복사합니다.
1-2. blockstate JSON
계단은 facing(4방향) × half(bottom/top) × shape(5가지) 조합으로 총 40가지 variant가 필요합니다.
{
"variants": {
"facing=east,half=bottom,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "y": 270, "uvlock": true },
"facing=east,half=bottom,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner" },
"facing=east,half=bottom,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "y": 270, "uvlock": true },
"facing=east,half=bottom,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer" },
"facing=east,half=bottom,shape=straight": { "model": "examplemod:block/ruby_stairs" },
"facing=east,half=top,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "uvlock": true },
"facing=east,half=top,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 90, "uvlock": true },
"facing=east,half=top,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "uvlock": true },
"facing=east,half=top,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 90, "uvlock": true },
"facing=east,half=top,shape=straight": { "model": "examplemod:block/ruby_stairs", "x": 180, "uvlock": true },
"facing=north,half=bottom,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "y": 180, "uvlock": true },
"facing=north,half=bottom,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "y": 270, "uvlock": true },
"facing=north,half=bottom,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "y": 180, "uvlock": true },
"facing=north,half=bottom,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "y": 270, "uvlock": true },
"facing=north,half=bottom,shape=straight": { "model": "examplemod:block/ruby_stairs", "y": 270, "uvlock": true },
"facing=north,half=top,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 270, "uvlock": true },
"facing=north,half=top,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "uvlock": true },
"facing=north,half=top,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 270, "uvlock": true },
"facing=north,half=top,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "uvlock": true },
"facing=north,half=top,shape=straight": { "model": "examplemod:block/ruby_stairs", "x": 180, "y": 270, "uvlock": true },
"facing=south,half=bottom,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner" },
"facing=south,half=bottom,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "y": 90, "uvlock": true },
"facing=south,half=bottom,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer" },
"facing=south,half=bottom,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "y": 90, "uvlock": true },
"facing=south,half=bottom,shape=straight": { "model": "examplemod:block/ruby_stairs", "y": 90, "uvlock": true },
"facing=south,half=top,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 90, "uvlock": true },
"facing=south,half=top,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 180, "uvlock": true },
"facing=south,half=top,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 90, "uvlock": true },
"facing=south,half=top,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 180, "uvlock": true },
"facing=south,half=top,shape=straight": { "model": "examplemod:block/ruby_stairs", "x": 180, "y": 90, "uvlock": true },
"facing=west,half=bottom,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "y": 90, "uvlock": true },
"facing=west,half=bottom,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "y": 180, "uvlock": true },
"facing=west,half=bottom,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "y": 90, "uvlock": true },
"facing=west,half=bottom,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "y": 180, "uvlock": true },
"facing=west,half=bottom,shape=straight": { "model": "examplemod:block/ruby_stairs", "y": 180, "uvlock": true },
"facing=west,half=top,shape=inner_left": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 180, "uvlock": true },
"facing=west,half=top,shape=inner_right": { "model": "examplemod:block/ruby_stairs_inner", "x": 180, "y": 270, "uvlock": true },
"facing=west,half=top,shape=outer_left": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 180, "uvlock": true },
"facing=west,half=top,shape=outer_right": { "model": "examplemod:block/ruby_stairs_outer", "x": 180, "y": 270, "uvlock": true },
"facing=west,half=top,shape=straight": { "model": "examplemod:block/ruby_stairs", "x": 180, "y": 180, "uvlock": true }
}
}40개 항목이 많아 보이지만 패턴은 단순합니다. facing마다 y 회전값이 달라지고, half=top이면 x: 180이 추가됩니다.
💡 참고: 바닐라
data/minecraft/blockstates/oak_stairs.json을 복사한 뒤 모델 경로만 교체하는 방법이 가장 빠릅니다.
1-3. 모델 JSON (3종)
계단은 세 가지 모델이 필요합니다.
// models/block/ruby_stairs.json (기본 계단)
{
"parent": "minecraft:block/stairs",
"textures": {
"bottom": "examplemod:block/ruby_block",
"top": "examplemod:block/ruby_block",
"side": "examplemod:block/ruby_block"
}
}// models/block/ruby_stairs_inner.json (안쪽 모서리)
{
"parent": "minecraft:block/inner_stairs",
"textures": {
"bottom": "examplemod:block/ruby_block",
"top": "examplemod:block/ruby_block",
"side": "examplemod:block/ruby_block"
}
}// models/block/ruby_stairs_outer.json (바깥쪽 모서리)
{
"parent": "minecraft:block/outer_stairs",
"textures": {
"bottom": "examplemod:block/ruby_block",
"top": "examplemod:block/ruby_block",
"side": "examplemod:block/ruby_block"
}
}2. SlabBlock 등록
2-1. 등록 코드
public static final Supplier<SlabBlock> RUBY_SLAB = BLOCKS.register("ruby_slab",
() -> new SlabBlock(BlockBehaviour.Properties.ofFullCopy(RUBY_BLOCK.get())));SlabBlock은 기준 블록 상태가 필요 없습니다. 속성 복사만으로 충분합니다.
2-2. blockstate JSON
슬랩은 type 속성 하나로 3가지 variant만 있습니다.
{
"variants": {
"type=bottom": { "model": "examplemod:block/ruby_slab" },
"type=top": { "model": "examplemod:block/ruby_slab_top" },
"type=double": { "model": "examplemod:block/ruby_block" }
}
}type=bottom— 아래쪽 반 블록type=top— 위쪽 반 블록type=double— 두 개 쌓인 상태. 기준 블록 모델을 그대로 참조합니다.
2-3. 모델 JSON (2종)
// models/block/ruby_slab.json (아래쪽)
{
"parent": "minecraft:block/slab",
"textures": {
"bottom": "examplemod:block/ruby_block",
"top": "examplemod:block/ruby_block",
"side": "examplemod:block/ruby_block"
}
}// models/block/ruby_slab_top.json (위쪽)
{
"parent": "minecraft:block/slab_top",
"textures": {
"bottom": "examplemod:block/ruby_block",
"top": "examplemod:block/ruby_block",
"side": "examplemod:block/ruby_block"
}
}type=double은 별도 모델 없이 ruby_block 모델을 재사용합니다.
3. FenceBlock 등록
3-1. 등록 코드
public static final Supplier<FenceBlock> RUBY_FENCE = BLOCKS.register("ruby_fence",
() -> new FenceBlock(BlockBehaviour.Properties.ofFullCopy(RUBY_BLOCK.get())));3-2. blockstate JSON (multipart)
펜스는 variants 대신 multipart를 씁니다. 인접 블록 연결 여부에 따라 부분 모델을 조합하기 때문입니다.
{
"multipart": [
{ "apply": { "model": "examplemod:block/ruby_fence_post" } },
{
"when": { "north": "true" },
"apply": { "model": "examplemod:block/ruby_fence_side", "uvlock": true }
},
{
"when": { "east": "true" },
"apply": { "model": "examplemod:block/ruby_fence_side", "y": 90, "uvlock": true }
},
{
"when": { "south": "true" },
"apply": { "model": "examplemod:block/ruby_fence_side", "y": 180, "uvlock": true }
},
{
"when": { "west": "true" },
"apply": { "model": "examplemod:block/ruby_fence_side", "y": 270, "uvlock": true }
}
]
}- 첫 번째 항목(
when없음) — 항상 렌더링되는 중앙 기둥 - 나머지 4개 — 해당 방향 이웃이 연결 가능할 때만 추가
3-3. 모델 JSON (2종)
// models/block/ruby_fence_post.json (기둥)
{
"parent": "minecraft:block/fence_post",
"textures": {
"texture": "examplemod:block/ruby_block"
}
}// models/block/ruby_fence_side.json (연결 부분)
{
"parent": "minecraft:block/fence_side",
"textures": {
"texture": "examplemod:block/ruby_block"
}
}4. 안티패턴 — blockstate variant 누락
⚠️ blockstate variant 누락
StairBlock의
facingvariant 일부를 빠뜨리면:// ❌ facing=west 항목 전체 누락 { "variants": { "facing=east,half=bottom,shape=straight": { "model": "..." }, "facing=north,half=bottom,shape=straight": { "model": "..." }, "facing=south,half=bottom,shape=straight": { "model": "..." } // facing=west 없음 } }서쪽을 향해 계단을 설치하면 보라/검정 큐브가 나타납니다.
콘솔에
"Missing variant"경고가 출력됩니다.해결: 바닐라
data/minecraft/blockstates/oak_stairs.json을 참조해 40개 항목을 모두 채우세요.
5. 전체 파일 구조 정리
src/main/resources/assets/examplemod/
├── blockstates/
│ ├── ruby_stairs.json ← 40가지 variant
│ ├── ruby_slab.json ← 3가지 variant
│ └── ruby_fence.json ← multipart 5항목
├── models/block/
│ ├── ruby_stairs.json ← parent: stairs
│ ├── ruby_stairs_inner.json ← parent: inner_stairs
│ ├── ruby_stairs_outer.json ← parent: outer_stairs
│ ├── ruby_slab.json ← parent: slab
│ ├── ruby_slab_top.json ← parent: slab_top
│ ├── ruby_fence_post.json ← parent: fence_post
│ └── ruby_fence_side.json ← parent: fence_side
└── models/item/
├── ruby_stairs.json
├── ruby_slab.json
└── ruby_fence.json
아이템 모델은 각각 블록 모델을 참조합니다.
// models/item/ruby_stairs.json
{ "parent": "examplemod:block/ruby_stairs" }
// models/item/ruby_slab.json
{ "parent": "examplemod:block/ruby_slab" }
// models/item/ruby_fence.json
{ "parent": "examplemod:block/ruby_fence_post" }6. 인게임 검증
6-1. 계단 4방향 + 상하 설치
runClient로 클라이언트 실행- 크리에이티브 탭에서
ruby_stairs꺼내기 - 북/동/남/서 방향으로 각각 설치 (바닥 설치 =
half=bottom) - Shift+클릭으로 천장에 설치 (
half=top) - 두 계단을 L자로 붙여
shape=inner_right/outer_right확인
6-2. 슬랩 상하 설치
ruby_slab을 바닥에 설치 (type=bottom)- 같은 위치 위쪽 절반을 클릭해
type=top설치 - 두 슬랩이 합쳐져
type=double이 되는지 확인
6-3. 펜스 연결 동작
ruby_fence를 일렬로 설치- 인접 블록(돌 등)을 옆에 붙여 연결 여부 확인
- F3 화면에서
north=true,east=false등 boolean 값 대조
요약
| 클래스 | 등록 특이사항 | blockstate 방식 | variant 수 |
|---|---|---|---|
StairBlock | 기준 블록 상태 필요 | variants | 40개 |
SlabBlock | 속성 복사만 | variants | 3개 |
FenceBlock | 속성 복사만 | multipart | 5항목 |
다음 챕터에서는 블록에 BlockEntity를 붙여 데이터를 저장하는 방법을 학습합니다.