ハマクラ Advent Calendar 2025 16日目の記事です
カスタム進捗
データパックで追加できる要素の一つに進捗があります。これはプレイヤーが何かをしたときにそれを検知して達成できる進捗を追加するものです。
もちろん「石器時代」とかのように達成して嬉しいだけのものを作ってもいいのですが、報酬として関数を設定することで検知→関数の実行という処理を行ってくれるようになります。というかこっちがメインの使い道といってもいいです。
さらに報酬の関数に進捗を剝奪するコマンドを入れておけば繰り返し検知することもできます。
バニラの進捗を見て分かるように、様々な条件が設定できますので、幅広いニーズに応えられることと思います。
そして進捗には大変便利な点がいくつもあります。まずはこれらを紹介します。
いいところ
1. 条件が豊富
検知の仕方による違いで、@a[○○=xx]にはできない動作の検知を得意とします。例えば、「ブロックを設置する」、「村人を右クリックする」などです。
それを組み合わせた、A or Bとか (A and B) or Cみたいなロジック回路も進捗内で作れてしまいます。
2. プレイヤー毎に処理を分けられる
進捗の達成条件はプレイヤー毎に保存されるので、それだけでスコアボードを使った検知のようなことができます。難しいことを考えなくても検知が混線することはありません。
そしてこれは3.にも関わってきます。
3. functionの実行者が@s
関数は進捗を達成したプレイヤーとして、プレイヤーの座標で実行されます。execute as ○○ at @sみたいなことです。こちらも混線しづらいというアドバンテージがあります。
4. 「1度だけ実行」が得意
当然、報酬で進捗を剝奪しなければ、進捗を達成した後もう一度進捗が達成されることはありませんし、無駄な検知が走ることもありません。
この動作を他の方法でやろうとすると仕組みが複雑になりがちです。
5. 負荷が小さい
常時実行の@aを使った検知では一旦全てのエンティティを読み込んでからプレイヤーに絞り(間違っていたらすみません)、そこから検知するため負荷が大きい(はず)です。一方、進捗は元々プレイヤーのための機能で比較的軽いです。
データパックの準備
進捗を追加するにはデータパックにjsonファイルとして保存します。セットでfunctionのmcfunctionファイルも用意しましょう。
条件の一覧を見るにはWiki、jsonファイルを作成するにはAdvancement Generatorを使うのが便利です。
また、バニラの進捗も同じ形式で書かれているので、client.jarの中身を参考にするのも良いでしょう。
フォルダの構造は下記のようにします。
<namespace>
├advancement
│ ├<進捗1>.json
│ ├ ...
│
└function
├<関数1>.mcfunction
├ ...
ファイルの中身は、項目が1つの進捗のjsonを例にすると下記のようになります。
{
"criteria": {
"a": {
<使うトリガー>
}
},
"rewards": {
"function": "namespace:example"
}
}
advancement revoke @s only namespace:example
<お好きなコマンド>
<使うトリガー>には各種トリガーを書きます。
advancement revokeを使って進捗を剝奪し、もう一度検知ができる状態に戻します。
ちなみに、名前やアイコン等を設定しなければプレイヤーが認識することはできず、検知用の進捗として静かに役目を果たします。
例
ここからは、よく使うトリガー・進捗固有のトリガーを使った進捗の例を紹介します。
consume_item 右クリック検知
食料やそれに準ずるアイテム(consumableのアイテムコンポーネント持ち)を消費したことを検知します。消費時間を0tickにしたアイテムを指定することで、アイテムを持って右クリックをしたことを検知できます。
{
"criteria": {
"a": {
"trigger": "minecraft:consume_item",
"conditions": {
"item": {
"predicates": {
"minecraft:custom_data": "{effect:\"speed\"}"
}
}
}
}
},
"rewards": {
"function": "namespace:example"
}
}
これはcustom_dataにeffect:"speed"があるアイテムを右クリックしたことを検知します。
inventory_changed インベントリ内のアイテムが動いたことの検知
アイテムを拾ったとき、インベントリを開いてアイテムを動かしたとき、アイテムを捨てたときなど、アイテムが動いたときを検知します。
例えば、「特定の装備をしているときにステータスが上がる」といったシチュエーションで、常時実行のコマンドの代わりにこれを使うことで負荷を大きく減らすことができます。
{
"criteria": {
"a": {
"trigger": "minecraft:inventory_changed"
}
},
"rewards": {
"function": "namespace:example"
}
}
conditionを指定していないので、何かしらのアイテムを動かしたことを検知します。
player_interacted_with_entity エンティティを右クリックしたことを検知
右クリックできるエンティティを右クリックしたことを検知します。専らインタラクションのために使われます。押した人を特定できて関数を直接実行できるボタンのように使えるのでとっても便利です。
{
"criteria": {
"a": {
"trigger": "minecraft:player_interacted_with_entity",
"conditions": {
"item": {
"items": "minecraft:diamond"
},
"entity": {
"type": "minecraft:interaction",
"nbt": {
"Tags": [
"goal"
]
}
}
}
}
},
"rewards": {
"function": "namespace:example"
}
}
goalというタグが付いたインタラクションをダイヤモンドを持ってクリックしたことを検知します。
tick 毎ティック何かを検知
これは少し特殊で、ただ毎tick達成されるというトリガーです。conditionsに条件を書くことで、プレイヤーが何らかの状態にあることを毎tick検知できるようになります。execute as @a if ...の代替として使います。
逆に、何も条件を指定しなければ、初めてワールドにログインしたときに関数を実行できます。
{
"criteria": {
"a": {
"trigger": "minecraft:tick",
"conditions": {
"player": {
"type_specific": {
"type": "minecraft:player",
"input": {
"jump": true
}
},
"equipment": {
"mainhand": {
"items": "minecraft:feather"
}
}
}
}
}
},
"rewards": {
"function": "namespace:example"
}
}
羽根を持ってジャンプしたことを検知します。