はじめに
マインクラフト統合版では、アドオンを制作すれば大抵のものは作れることが知られています。それは簡単なブロックやアイテムに始まり、エンティティや複雑なシステムに至るまで、多岐にわたります。
アドオンそのものの作り方についてはこちらをご覧ください:
しかし、そうは言ってもアドオンは万能ではありません。作れないとされるものもいくつかあります。その一つが、チェスト。より一般的に言うと、「インベントリを持つブロック」は現バージョンにおいて制作不能なのです。
これは、ブロックのコンポーネントにインベントリを定義するものがないためです。したがって、Javaで制御できるMODとは異なり、jsonを使う方法しか残されていない統合版においては、インベントリを持つブロックそのものを作る道が閉ざされていることになります。
では、この類のものは完全に制作不能なのか。そうでもありません。後述する方法を使えば、「疑似的なチェスト」は制作が可能です。

本記事では、特殊な方法を用いて疑似的なチェストを制作していきます。
準備
まず、カスタムチェストを作るにあたってアドオンを制作する環境そのものが必要です。アドオンそのものの概要や基礎事項は上記のリンクでも解説していますが、準備を行うのみであれば以下のジェネレーターを使うのが早いと思います:
ビヘイビアパックとリソースパックの2つを準備し、制作できるようにしておきましょう。
ビヘイビアパック
さて、以下ではカスタムチェストを作るにあたっての、ビヘイビアパック部分を制作していきます。必要になる要素は、以下の3つです:
- チェスト用のブロック
- チェスト用のエンティティ
- システム
これを見てお分かりかと思いますが、カスタムチェストの制作にあたってはエンティティを介す必要があります。これは、ブロックのコンポーネントにはインベントリを定義するものはない一方で、エンティティには“minecraft:inventory”というインベントリを定義するものが存在するためです。
したがって、カスタムチェストを作る場合には「エンティティを召喚し、それを疑似的にブロックとみなす」という方法を取る必要がありますね。
ブロックの制作
エンティティを介すとはいえ、ブロック(またはそれに相当するアイテム)は制作する必要はあります。これは、「チェスト用エンティティ」を召喚するためのものです。インベントリに表示され、チェストを設置するために必要になります。
まずは、ビヘイビアパック内にblocksというフォルダを作り、任意の名前のjsonファイル(例:custom_chest_block.json)を作成しましょう。jsonファイルの内容は以下:
{
"format_version":"1.20.20",
"minecraft:block":{
"description":{
"identifier":"lq:custom_chest_block",
"menu_category":{
"category":"items"
}
},
"components":{
"minecraft:display_name":"カスタムチェスト",
"minecraft:destructible_by_mining":{
"seconds_to_destroy":1.0
},
"minecraft:geometry":"geometry.custom_chest_block"
}
}
}
コード中の「lq:custom_chest_block」はブロックのIDです。各自任意の名前を設定してください。
このブロック自体は極論を言えばダミー用のブロックですので、細かな設定はしていないですし、おそらく不要だと思われます。ただ、ブロックがインベントリ内で表示される都合上、一応ジオメトリを設定しています。ジオメトリの名前は「geometry.custom_chest_block」としました。この名前は後で使います。
エンティティの制作
こちらはカスタムチェストの本体です。先の”minecraft:inventory”を含め、各種エンティティの定義に必要となるコンポーネントを定義していきます。ビヘイビアパック内にentitiesというフォルダを作り、custom_chest_entity.jsonというファイルを作成しましょう。
{
"format_version": "1.20.80",
"minecraft:entity": {
"description": {
"identifier": "lq:custom_chest_entity",
"is_spawnable": true,
"is_summonable": true
},
"components": {
"minecraft:physics": {
"has_gravity": true,
"has_collision": true
},
"minecraft:collision_box": {
"width": 1.0,
"height": 1.0
},
"minecraft:knockback_resistance": {
"value": 1.0
},
"minecraft:health": {
"value": 5,
"max": 5
},
"minecraft:inventory": {
"container_type": "inventory",
"inventory_size": 27,
"private": false,
"restrict_to_owner":false,
"can_be_siphoned_from":true,
"additional_slots_per_strength":0
}
}
}
}
先と同じく、identifierはこのエンティティのIDです。任意の名前を設定しましょう。
「minecraft:inventory」の部分が本アドオンの肝です。これによって、インベントリをもつエンティティを定義することができます。inventory_sizeは27までの自然数を設定でき、一般的にはこれ以上にすることはできませんが、特殊な方法を使えば上限を突破することができるようです。
スクリプト
最後に、ScriptAPIを用いてスクリプトを作成します。ここでのスクリプトの実行内容は、以下の2点です:
- ブロックを設置時にエンティティを召喚し、ブロックを消去
- チェストエンティティを攻撃したときにkillする
これらの機能を実装していきましょう。
ビヘイビアパック内にscriptsという名前のフォルダを作成し、main.jsというファイルを作成しましょう。ファイルの内容は以下:
import * as server from "@minecraft/server";
import * as ui from "@minecraft/server-ui";
const BLOCK_ID = "lq:custom_chest_block";
const ENTITY_ID ="lq:custom_chest_entity";
server.world.afterEvents.playerPlaceBlock.subscribe(ev => {
const block = ev.block;
if (block.typeId == BLOCK_ID){
block.setType("minecraft:air");
server.system.runTimeout(() => {
const dimension = ev.dimension;
const location = block.location;
const spawnLocation = {
x: location.x + 0.5,
y: location.y,
z: location.z + 0.5
};
dimension.spawnEntity(ENTITY_ID, spawnLocation);
},1);
}
})
server.world.afterEvents.entityHitEntity.subscribe(ev => {
const hitEntity = ev.hitEntity;
if(hitEntity && hitEntity.typeId == ENTITY_ID){
hitEntity.kill();
}
})
スクリプトの最初にブロックのIDとエンティティのIDを定義します。これは、先ほど定義したものですね。
続いて、プレイヤーがブロックを設置したことをトリガーして、それを空気に置き換えた後に同じ場所にエンティティを召喚する処理を定義しています。1ティック遅延させることによって、これらが重ならないように配慮しています。
最後に、チェストエンティティを攻撃したときに、それを即時killする処理を定義しています。
- 本スクリプトを正常に機能させるにはmanifest.jsonでScriptAPIの依存関係について記述する必要があります。詳細はこちら
以上をまとめると、ファイルの位置関係は以下のようになっているはずです:
behavior_pack/
├─ blocks/
│ └─ custom_chest_block.json
├─ entities/
│ └─ custom_chest_entity.json
├─ scripts/
│ └─ main.json
├─ manifest.json
リソースパック
ここまでできれば、カスタムチェストの定義もほとんどできたも同然です。あとはテクスチャーとモデル関係を制作するのみですが、これは基本的なエンティティやブロックの制作と何ら変わりません。
ブロックのテクスチャーとモデル
まずは、ダミー用のブロックについて、テクスチャーと3Dモデルを作っていきましょう。この辺りの基本的な事項については下記の記事で解説していますので、本記事では簡単に解説を済ませます。
まずは、リソースパック直下にtexturesという名前のフォルダを作成し、terrain_texture.jsonを作成します。内容は以下:
{
"resource_pack_name": "custom_chest_rp",
"texture_name": "atlas.terrain",
"texture_data": {
"custom_chest_tex": {
"textures": "textures/blocks/custom_chest_block"
}
}
}
resource_pack_nameとcustom_chest_texの部分は任意の文字列ですが、texture_dataにあるcustom_chest_texという文字列は後で使います。
なお、ここで定義した画像ファイルのパス「textures/blocks/custom_chest_block」は後で使います。
続いて、ダミー用のブロックにテクスチャーと音を割り当てます。リソースパック直下に、blocks.jsonを作成し、内容を以下としましょう:
{
"format_version": [1, 1, 0],
"lq:custom_chest_block": {
"textures": "custom_chest_tex",
"sound": "wood"
}
}
ただし、「lq:custom_chest_block」の部分は先ほど定義したチェストのブロックIDです。各自変えておきましょう。また、「custom_chest_tex」についても、先ほどterrain_texture.jsonにて使用した文字列と同じである必要があります。
最後に、ブロックの3Dモデルを定義します。これはBlockBenchなどのソフトを使うと便利でしょう。
詳細なソフトの使い方は上記の記事で解説しています。ブロックのモデルを出力したら、リソースパック直下にmodels/blocks/custom_chest.geo.json等の名前で保存しましょう。このgeometryのidentifierは、先ほどブロックにおいて定義したgeometry(geometry.custom_chest_block)と一致している必要があります。一応例を置いておきます:
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.custom_chest_block",
"texture_width": 16,
"texture_height": 16,
"visible_bounds_width": 2,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "bb_main",
"pivot": [0, 0, 0],
"cubes": [
{
"origin": [-8, 0, -8],
"size": [16, 14, 16],
"uv": {
"north": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"east": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"south": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"west": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"up": {"uv": [9.24167, 1.19167], "uv_size": [3.425, 4.20833]},
"down": {"uv": [9.36667, 10.81667], "uv_size": [3.16667, 4.46667]}
}
}
]
}
]
}
]
}
同様に、このブロックのテクスチャーを定義しましょう。リソースパック直下にtextures/blocks/custom_chest_block.png等の名前で保存しておきます。なお、当然この画像ファイルの名前は先ほどterrain_texture.jsonにて定義したものと同じである必要があるので、注意しましょう。

エンティティの定義とモデル
同様に、エンティティについてもモデルを定義します。まずはエンティティがどのモデルとテクスチャーを使うかを紐づけるファイルを作ります。リソースパック直下にentity/custom_chest_entity.jsonを作成し、内容を以下としましょう:
{
"format_version": "1.10.0",
"minecraft:client_entity": {
"description": {
"identifier": "lq:custom_chest_entity",
"materials": {
"default": "entity_alphatest"
},
"textures": {
"default": "textures/entity/custom_chest"
},
"geometry": {
"default": "geometry.custom_chest"
},
"render_controllers": [
"controller.render.default"
]
}
}
}
例に漏れず、identifierは各自に合わせて変えておきましょう。また、geometryの名前(geometry.custom_chest)も後で使います。
同様に、エンティティのモデルを定義します。リソースパック直下にmodels/entity/custom_chest.geo.jsonを作成し、内容を以下とします:
{
"format_version": "1.12.0",
"minecraft:geometry": [
{
"description": {
"identifier": "geometry.custom_chest",
"texture_width": 16,
"texture_height": 16,
"visible_bounds_width": 2,
"visible_bounds_height": 2.5,
"visible_bounds_offset": [0, 0.75, 0]
},
"bones": [
{
"name": "bb_main",
"pivot": [0, 0, 0],
"cubes": [
{
"origin": [-8, 0, -8],
"size": [16, 14, 16],
"uv": {
"north": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"east": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"south": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"west": {"uv": [9.11667, 5.96667], "uv_size": [3.75, 4.55833]},
"up": {"uv": [9.24167, 1.19167], "uv_size": [3.425, 4.20833]},
"down": {"uv": [9.36667, 10.81667], "uv_size": [3.16667, 4.46667]}
}
}
]
}
]
}
]
}
これはブロックのジオメトリとほとんど変わりませんが、identifierだけ先のcustom_chest_entity.jsonに合わせて「geometry.custom_chest」としている点だけ注意しましょう。
最後に、エンティティのテクスチャーとして、リソースパック直下にtextures/entity/custom_chest.pngを保存します。画像ファイルの内容は先と同じものでよいでしょう。
最後に、リソースパックのほうのフォルダの構成を置いておきます:
resource_pack/
├─ blocks.json
├─ entity/
│ └─ custom_chest_entity.json
├─ models/
│ └─ entity/
│ └─ custom_chest.geo.json
| └─ blocks/
│ └─ custom_chest.geo.json
├─ textures/
│ ├─ blocks/
│ │ └─ custom_chest_block.png
│ ├─ entity/
│ │ └─ custom_chest.png
│ └─ terrain_texture.json
└─ manifest.json
確認
以上を終えてこのアドオンを有効化することによって、無事インベントリを持つ「疑似エンティティ」が定義されているはずです:


なお、インベントリのタイトルが「不明」となっていますが、こちらはスクリプトによってnameTagを付与することによって解決します。具体的には、先ほどのスクリプトにおいて、spawnEntityの部分を以下に書き換えることで、nameTagを付与可能です:
const chestEntity = dimension.spawnEntity(ENTITY_ID, spawnLocation);
chestEntity.nameTag = "カスタムチェスト";

今回解説したスクリプトを使って、友人・コミュニティー内で24時間いつでも遊べるマルチサーバーを構築したい場合は、VPS(レンタルサーバー)の導入が近道です。
私の開発・検証環境では「シンVPS」を使用しています。
以前は自宅PCでホストしていましたが、PCの電源を切り忘れるストレスや、電気代を考慮して移行しました。シンVPSを選んだ決め手は、圧倒的なコストパフォーマンスです。
- 月額の安さ 4GBプランでも月額1,200円程度(長期契約なら1,000円以下)と維持費が安く済みます。
- 高速な読み込み 全プランでNVMe SSDを採用しており、ワールドの読み込みや重いコマンド処理でもラグを感じにくいです。
以下のリンクから申し込むと初回利用料金が10%OFFになります。
(1ヶ月以上の契約対象。「安くて速い」サーバーを探している方はぜひ試してみてください)
(10%OFF適用) マルチサーバーの立て方はこちら