1. はじめに
本記事では、Minecraft Java Editionのデータパック製作において、Pythonを使ってmcfunctionやjsonを生成する方法を説明していきます。
この記事はMinecraft Java Edition ver.1.21.3 時点の情報を元に執筆しています。
この記事はデータパックのfunction・loot_tableおよびプログラミングの基礎がわかる人向けのものです。
データパックを作っているとき、同じような形式のmcfunction・jsonファイルを大量に作ることがあるかもしれません。カスタム武器、カスタムモブ、NPCとの会話、ショップの取引…こういった処理を沢山作るときとか。
こういった場合に、Pythonなどのプログラミング言語を用いてテンプレート生成プログラムを作っておくと大量作成が楽になります。
この記事はプログラミング言語のPythonを使ったmcfunction・jsonファイルの生成方法の一例を紹介していきます。
※自身でも同じことを試したい場合、Pythonの実行環境を用意する必要があります。
2.カスタム武器を作る
この記事では、カスタム武器の作成を例に解説していきます。
例えば、とあるカスタム武器と、それを入手できるloot tableを以下のように実装します。
(ベースとなるアイテムを仮にニンジン付きの棒としています。)
loot table のサンプル
{
"type": "minecraft:command",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:carrot_on_a_stick",
"weight": 1,
"functions": [
{
"function": "minecraft:set_name",
"entity": "this",
"name": {
"text": "すごい鉄の斧",
"color": "white",
"italic": false
}
},
{
"function": "minecraft:set_lore",
"entity": "this",
"lore": [
{
"text": " なんだかとてもすごいらしい斧。",
"italic": false,
"color": "yellow"
},
{
"text": ""
},
{
"text": " 攻撃力 +8",
"italic": false,
"color": "blue"
},
{
"text": " 攻撃速度 0.8",
"italic": false,
"color": "blue"
}
],
"mode": "replace_all"
},
{
"function": "minecraft:set_attributes",
"modifiers": [
{
"id": "weapon.attack_speed.0.8",
"attribute": "minecraft:attack_speed",
"amount": -3.2,
"operation": "add_value",
"slot": "mainhand"
},
{
"id": "weapon.attack_damage.add.8",
"attribute": "minecraft:attack_damage",
"amount": 8,
"operation": "add_value",
"slot": "mainhand"
}
]
},
{
"function": "minecraft:set_components",
"components": {
"minecraft:item_model": "minecraft:iron_axe"
}
},
{
"function": "minecraft:toggle_tooltips",
"toggles": {
"minecraft:attribute_modifiers": false
}
}
]
}
]
}
]
}
このようなカスタム武器で決められる要素は以下の5つとします。
- 名前
- 説明文
- 攻撃力
- 攻撃速度
- 外見
では、「この5要素を列挙すれば、自動的にloot tableやfunctionを生成してくれるプログラム」をPythonで組んでいきます。
ディレクトリ構成は以下のようにしています。
<Datapack Name>/
├── pack.mcmeta
├── data/
│ └── <namespace>/
│ ├── function/
│ │ └── give/
│ │ └── <custom_weapon_id>.mcfunction
│ └── loot_table/
│ └── custom_weapon/
│ └── <custom_weapon_id>.json
└── python/
├── weapon_database.json
└── generate_weapon_loot_table.py
例として今回はpack.mcmeta
のあるディレクトリ下にpythonディレクトリを用意し、その場所にプログラムとデータベースファイルを作っていきます。
2-1. loot table 生成のサンプル
データベースファイルのサンプル
{
"sugoi_iron_axe": {
"display_name": "すごい鉄の斧",
"lore": "なんだかとてもすごいらしい斧",
"attack_damage": 8,
"attack_speed": 0.8,
"item": "iron_axe"
},
"hayahaya_golden_sword": {
"display_name": "はやはやゴールデンソード",
"lore": "とても速く振れる剣",
"attack_damage": 1,
"attack_speed": 10.0,
"item": "golden_sword"
}
}
loot table 生成プログラムのサンプル
import json
import os
# 指定パスのディレクトリが存在しなければ、ディレクトリを作成する関数を定義
def makedir(path):
if not os.path.isdir(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
# データベースファイルの読み込み
with open('weapon_database.json' , encoding='utf-8') as f:
weapon_database = json.load(f)
# データパック内の名前空間への相対パス
namespace_path = '../data/sample_namespace/'
# 各武器に対する処理
for weapon_id, weapon_data in weapon_database.items():
# 書き込み先のパス指定
path = namespace_path + 'loot_table/custom_weapon/' + weapon_id + '.json'
# 書き込み先のディレクトリの作成
makedir(path)
# 出力内容を辞書形式で定義
output = {
"type": "minecraft:command",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:carrot_on_a_stick",
"weight": 1,
"functions": [
{
"function": "minecraft:set_name",
"entity": "this",
"name": {
"text": weapon_data["display_name"],
"color": "white",
"italic": False
}
},
{
"function": "minecraft:set_lore",
"entity": "this",
"lore": [
{
"text": " " + weapon_data["lore"],
"italic": False,
"color": "yellow"
},
{
"text": ""
},
{
"text": " 攻撃力 +" + str(weapon_data["attack_damage"]),
"italic": False,
"color": "blue"
},
{
"text": " 攻撃速度 " + str(weapon_data["attack_speed"]),
"italic": False,
"color": "blue"
}
],
"mode": "replace_all"
},
{
"function": "minecraft:set_attributes",
"modifiers": [
{
"id": "weapon.attack_damage.add." + str(weapon_data["attack_damage"]),
"attribute": "minecraft:attack_damage",
"amount": weapon_data["attack_damage"],
"operation": "add_value",
"slot": "mainhand"
},
{
"id": "weapon.attack_speed." + str(weapon_data["attack_speed"]),
"attribute": "minecraft:attack_speed",
"amount": weapon_data["attack_speed"]-4.0,
"operation": "add_value",
"slot": "mainhand"
}
]
},
{
"function": "minecraft:set_components",
"components": {
"minecraft:item_model": weapon_data["item"]
}
},
{
"function": "minecraft:toggle_tooltips",
"toggles": {
"minecraft:attribute_modifiers": False
}
}
]
}
]
}
]
}
# jsonファイルの書き込み
with open(path, 'w', encoding='utf-8') as f:
json.dump(output, f, indent='\t', ensure_ascii=False)
サンプルプログラムを実行すると、データパックのloot_table
ディレクトリ内にカスタム武器のjsonファイルが生成されます。
2-2. loot table 生成プログラムの解説
loot table 生成プログラムの中身を少し解説します。
全体で行っていること:
- データベースファイル(各カスタム武器の要素を列挙したファイル)の中身を読み込み、変数に格納する
- 各武器の要素を取り出して以下を実行:
- 書き込み先のファイルパスを指定(=どこにどういう名前のファイルを生成するか指定)する
- 書き込むloot table(jsonファイル)の中身を作る
- jsonファイルに書き込む
データベースファイルの読み込み、変数への格納
データベースファイルの読み込み、変数への格納
今回のサンプルでは、データベースファイル(各カスタム武器の要素を列挙したファイル)をjson形式で作成しています。
# データベースファイルの読み込み
with open('weapon_database.json' , encoding='utf-8') as f:
weapon_database = json.load(f)
'weapon_database.json'
の部分で、データベースファイルのパスを指定します。
weapon_database = json.load(f)
の部分で、読み込んだデータの中身を変数weapon_database
に入れます。
列挙した各武器の要素を取り出す
列挙した各武器の要素を取り出す
サンプルプログラム内では、データベースファイルで列挙した各武器データの要素を取り出して個別の処理を行っています。
# 各武器に対する処理
for weapon_id, weapon_data in weapon_database.items():
/* 各武器に対する個別の処理 */
weapon_database
には辞書形式でデータが列挙されています。(辞書形式=Pythonでのjsonフォーマットみたいなデータ形式)
辞書形式はデータのidと中身の組を列挙するような方式です。データのidに重複が許されないという制約に注意してください。
上のようなfor
文を書けば、そのidと中身をそれぞれ変数weapon_id
, weapon_data
に格納することができます。
書き込むloot table(jsonファイル)の中身を指定
書き込むloot table(jsonファイル)の中身を指定
以下のように書き込むloot table(jsonファイル)の中身を指定しています。
# 出力内容を辞書形式で定義
output = {
"type": "minecraft:command",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "minecraft:carrot_on_a_stick",
"weight": 1,
"functions": [
{
"function": "minecraft:set_name",
"entity": "this",
"name": {
"text": weapon_data["display_name"],
"color": "white",
"italic": False
}
},
/* 略 */
]
}
]
}
]
}
weapon_data
にはカスタム武器の個別データが格納されており、weapon_data["display_name"]
でその武器の表示名を取り出しています。
json形式ではtrue
、false
は小文字ですが、PythonではTrue
、False
のように先頭を大文字にする必要があります。
jsonファイルの書き込み
jsonファイルの書き込み
以下のようにjsonファイルを書き込みます。
# jsonファイルの書き込み
with open(path, 'w', encoding='utf-8') as f:
json.dump(output, f, indent='\t', ensure_ascii=False)
path
の場所にはファイルを書き込む相対パス、output
の場所には書き込むデータの中身を指定します。
2-3. mcfunction 生成のサンプルと簡単な解説
上で生成したloot tableを呼び出すだけの簡単なmcfunctionを生成してみます。
mcfunction 生成プログラムのサンプル
import json
import os
# 指定パスのディレクトリが存在しなければ、ディレクトリを作成する関数を定義
def makedir(path):
if not os.path.isdir(os.path.dirname(path)):
os.makedirs(os.path.dirname(path))
# データベースファイルの読み込み
with open('weapon_database.json' , encoding='utf-8') as f:
weapon_database = json.load(f)
# データパックの名前空間
datapack_namespace = 'sample_namespace'
# データパック内の名前空間への相対パス
namespace_path = '../data/' + datapack_namespace + '/'
# 各武器に対する処理
for weapon_id, weapon_data in weapon_database.items():
# 書き込み先のパス指定
path = namespace_path + 'function/give/' + weapon_id + '.mcfunction'
# 書き込み先のディレクトリの作成
makedir(path)
# 書き込むファイルの中身を指定
output = []
output.append('loot give @s loot '+ datapack_namespace + ':custom_weapon/' + weapon_id + '\n')
# テキストファイルの書き込み
with open(path, 'w', encoding='utf-8') as f:
f.writelines(output)
テキストファイル書き込みの際にはwritelines()
関数が便利です。
リスト形式で文字列データを並べると、まとめてファイルに書き込みをしてくれます。
ただし自動で改行はしてくれないので、複数行のコマンドを記述する際には改行文字'\n'
を自分で書く必要があります。
output = []
で空のリストを用意し、
output.append('loot give @s loot '+ datapack_namespace + ':custom_weapon/' + weapon_id + '\n')
のように1行ずつ追加していくイメージです。
2-4. データベースの形式について
サンプルでは json 形式でデータベースファイルを作っていましたが、 csv 形式も便利です。
csvはコンマと改行を使って文字列を区切る形式です。excelのようなテーブルと相性がよく、excelシートをcsv形式で保存することもできます。VSCodeの拡張機能にcsvをテーブル形式で表示・編集できるものもあります。
また、GoogleのAPIを使って、PythonからGoogleスプレッドシートを読み込むことも可能みたいです。
自分に合ったやり方を選んでみてください。
3. さいごに
このファイル生成が真価を発揮するのは、大規模なデータパック/配布マップ製作だと思います。
RPGマップなどを製作する際には武器や防具、ショップやエリア移動など、様々な処理を大量に作る必要があります。
それを1種類追加するたびにコピペと編集を繰り返すのは疲れるので、今回紹介した方法を使ってテンプレートを用意しておけば、(最初に頑張る必要はありますが)あとで楽できるようになります。製作中に仕様を変更したい場合にも対応が楽になるので、長期的な開発にはおすすめする方法です。
それでは、良きコマンドライフを!