첫 블록 등록 — first_block
DeferredRegister.Blocks로 첫 블록을 등록하고, 동반 BlockItem을 ITEMS에 추가해 인벤토리에서 보이게 만드는 전체 과정을 학습합니다.
첫 블록 등록 — first_block
이 챕터에서는 블록을 처음으로 게임에 추가하는 전체 과정을 다룹니다. 아이템 등록과 달리 블록은 두 가지를 함께 등록해야 합니다. DeferredRegister.Blocks에 블록 자체를 등록하고, DeferredRegister.Items에 동반 BlockItem을 등록해야 인벤토리에서 블록을 집어 들 수 있습니다.
1. 블록 등록 코드
1-1. BLOCKS DeferredRegister 선언
DeferredRegister.Blocks는 아이템의 DeferredRegister.Items와 같은 방식으로 동작합니다. ExampleMod.java에 선언을 추가합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java
// "examplemod" 네임스페이스로 블록 레지스트리 바구니 생성
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(MODID);DeferredRegister.createBlocks(MODID)는 DeferredRegister.create(Registries.BLOCK, MODID)의 편의 메서드입니다. 내부적으로 동일하게 동작합니다.
1-2. first_block 등록
BLOCKS.registerSimpleBlock()를 호출해 새 블록을 등록합니다. NeoForge 26.1.2에서는 블록 등록에 registerSimpleBlock/registerBlock 계열을 써야 합니다. 이 메서드들이 내부에서 Properties.setId(ResourceKey)를 호출하므로, 옛 방식인 register("first_block", () -> new Block(new Properties()))는 "id not set"으로 크래시합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java
// registerSimpleBlock — Block::new + Properties 변형을 한 번에 처리
public static final DeferredBlock<Block> FIRST_BLOCK =
BLOCKS.registerSimpleBlock("first_block", p -> p.mapColor(MapColor.STONE).strength(1.5f));BLOCKS.registerSimpleBlock("first_block", p -> ...)를 호출하면:
- 블록의
Identifier가examplemod:first_block으로 결정됩니다. - 람다
p -> ...는 NeoForge가 자동 생성한Block.Properties를 받아 변형(mapColor·strength등)만 적용합니다.setId는 내부에서 처리됩니다. - 반환 타입은
DeferredBlock<Block>(Supplier<Block>서브타입) —.get()으로 실제Block인스턴스를 꺼냅니다.
1-3. 동반 BlockItem 등록 (ITEMS에)
블록을 등록했다고 해서 인벤토리에 자동으로 나타나지 않습니다. 블록을 아이템으로 들고 다닐 수 있게 하려면 ITEMS에 BlockItem을 별도로 등록해야 합니다.
// examplemod-template-26.1.2/src/main/java/com/example/examplemod/ExampleMod.java
// registerSimpleBlockItem — 블록에 대응하는 BlockItem 을 한 줄로 등록
public static final DeferredItem<BlockItem> FIRST_BLOCK_ITEM =
ITEMS.registerSimpleBlockItem("first_block", FIRST_BLOCK);주목할 점:
- 등록 이름은 블록과 동일하게
"first_block"을 씁니다. 아이템 네임스페이스(examplemod:first_block)와 블록 네임스페이스는 별개 레지스트리이므로 충돌하지 않습니다. registerSimpleBlockItem은 두 번째 인자로 블록의DeferredBlock(FIRST_BLOCK)을 직접 받아 내부에서new BlockItem(block, props)를 만들어 줍니다.Item.Properties도 자동 생성됩니다.BlockItem은Item의 서브클래스이므로ITEMS레지스트리에 등록합니다.
1-4. 모드 생성자에서 등록
생성자에서 BLOCKS.register(modEventBus)를 반드시 호출해야 합니다. ITEMS.register(modEventBus)는 이미 있으므로 BLOCKS 줄만 추가합니다.
// ExampleMod.java 생성자
public ExampleMod(IEventBus modEventBus, ModContainer container) {
ITEMS.register(modEventBus); // 기존 — 아이템 + BlockItem 모두 처리
BLOCKS.register(modEventBus); // 추가 — 블록 등록
// ...
}ITEMS.register(modEventBus) 한 줄이 FIRST_BLOCK_ITEM(BlockItem)도 함께 처리합니다. BLOCKS.register(modEventBus)는 FIRST_BLOCK 블록 자체를 처리합니다.
2. 안티패턴 — BlockItem 등록 누락
⚠️ BlockItem 등록 누락 시
BLOCKS.register는 했지만ITEMS.register(BlockItem)을 안 하면:
- 블록은 등록되지만 인벤토리에 표시되지 않습니다.
- 크리에이티브 탭에 나타나지 않습니다.
/give명령으로도 받을 수 없습니다 (Item이 없으므로).항상 Block + BlockItem 쌍으로 등록하세요.
3. 필수 리소스 파일 4종
블록이 인게임에서 보이려면 블록스테이트·블록 모델·아이템 모델·텍스처 네 가지 리소스가 모두 있어야 합니다. 리소스 위치는 src/main/resources/assets/examplemod/ 아래입니다.
3-1. 블록스테이트 JSON
경로: src/main/resources/assets/examplemod/blockstates/first_block.json
{
"variants": {
"": { "model": "examplemod:block/first_block" }
}
}블록스테이트 파일은 블록의 상태(방향·열림 여부 등)에 따라 어떤 모델을 쓸지 결정합니다. first_block은 상태가 없으므로 "" 키 하나만 씁니다.
3-2. 블록 모델 JSON
경로: src/main/resources/assets/examplemod/models/block/first_block.json
{
"parent": "block/cube_all",
"textures": {
"all": "examplemod:block/first_block"
}
}parent: "block/cube_all"— 6면 모두 같은 텍스처를 쓰는 기본 정육면체 모델입니다.all— 6면 텍스처 키."네임스페이스:block/파일명"형식으로 PNG를 참조합니다.
3-3. 아이템 모델 JSON
경로: src/main/resources/assets/examplemod/models/item/first_block.json
{
"parent": "examplemod:block/first_block"
}아이템 모델은 블록 모델을 그대로 상속합니다. 인벤토리에서 블록을 3D로 보여줄 때 이 파일을 씁니다.
3-4. 텍스처 PNG
경로: src/main/resources/assets/examplemod/textures/block/first_block.png
- 반드시 16×16 픽셀 PNG 파일이어야 합니다.
- 아이템 텍스처와 달리
textures/block/폴더에 위치합니다.
3-5. 언어 키
경로: src/main/resources/assets/examplemod/lang/en_us.json
{
"block.examplemod.first_block": "First Block"
}한국어 (ko_kr.json):
{
"block.examplemod.first_block": "첫 블록"
}블록의 언어 키 형식은 block.<네임스페이스>.<블록명>입니다. 아이템 키(item.examplemod.first_block)와 다르므로 주의하세요.
4. 리소스 파일 경로 전체 정리
src/main/resources/assets/examplemod/
├── blockstates/
│ └── first_block.json ← variants: "" → block/first_block 모델
├── lang/
│ ├── en_us.json ← "block.examplemod.first_block": "First Block" 추가
│ └── ko_kr.json ← "block.examplemod.first_block": "첫 블록" (선택)
├── models/
│ ├── block/
│ │ └── first_block.json ← parent: block/cube_all, all 텍스처 참조
│ └── item/
│ └── first_block.json ← parent: examplemod:block/first_block
└── textures/
└── block/
└── first_block.png ← 16×16 PNG 텍스처
5. 인게임 검증
5-1. runClient로 실행
IntelliJ Gradle 패널 → mod development → runClient 더블클릭으로 클라이언트를 실행합니다.
5-2. 명령어로 지급
/give @p examplemod:first_block
위 명령어로 first_block 아이템을 직접 받아볼 수 있습니다. BlockItem이 정상 등록되어 있어야 이 명령이 동작합니다.
5-3. 크리에이티브 탭에서 확인
E키로 인벤토리를 열고 검색창에first를 입력합니다.- 첫 블록 (영어: First Block)이 나타나면 성공입니다.
- 블록을 들고 바닥에 설치해 정육면체로 렌더링되는지 확인합니다.
요약
| 단계 | 파일 | 내용 |
|---|---|---|
| 1 | ExampleMod.java | BLOCKS DeferredRegister 선언 |
| 2 | ExampleMod.java | FIRST_BLOCK = BLOCKS.registerSimpleBlock("first_block", ...) 추가 |
| 3 | ExampleMod.java | FIRST_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("first_block", FIRST_BLOCK) 추가 |
| 4 | ExampleMod.java 생성자 | BLOCKS.register(modEventBus) 추가 |
| 5 | blockstates/first_block.json | 블록스테이트 — 기본 variants |
| 6 | models/block/first_block.json | 블록 모델 — parent: block/cube_all |
| 7 | models/item/first_block.json | 아이템 모델 — 블록 모델 상속 |
| 8 | textures/block/first_block.png | 16×16 PNG 텍스처 |
| 9 | lang/en_us.json | "block.examplemod.first_block": "First Block" 추가 |
다음 챕터에서는 BlockBehaviour.Properties를 활용해 블록에 강도·소리·빛 방출·마찰력 등 속성을 설정하는 방법을 학습합니다.