5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Minecraft CommandAdvent Calendar 2024

Day 3

Pythonを使ってmcfunctionやjsonを生成しよう 〜カスタム武器作成の補助を例に〜

Last updated at Posted at 2024-12-02

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を以下のように実装します。
(ベースとなるアイテムを仮にニンジン付きの棒としています。)

カスタム武器:
スクリーンショット 2024-11-02 11.42.55.png

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 生成のサンプル

データベースファイルのサンプル
weapon_database.json
{
    "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 生成プログラムのサンプル
generate_weapon_loot_table.py
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形式ではtruefalseは小文字ですが、PythonではTrueFalseのように先頭を大文字にする必要があります。

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 生成プログラムのサンプル
genarate_weapon_command.py
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種類追加するたびにコピペと編集を繰り返すのは疲れるので、今回紹介した方法を使ってテンプレートを用意しておけば、(最初に頑張る必要はありますが)あとで楽できるようになります。製作中に仕様を変更したい場合にも対応が楽になるので、長期的な開発にはおすすめする方法です。

それでは、良きコマンドライフを!

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?