この記事では、前回作ったスコアボード付きDatapackを大幅に改善しながら、Datapack制作の鬼門である /execute コマンドを基本から解説します。これまで「全プレイヤー対象」で処理していたウェルカム演出を、tick.json と組み合わせた「参加を検知してその人だけに実行する」正しい設計に作り直します。さらに leave_game スコアを使ってログアウトも検知することで、毎回ログインするたびにウェルカム演出が流れる、完成形のDatapackを作り上げましょう。
対応バージョン:Java Edition 1.21.11
前提知識
- Datapackの基本フォルダ構成(Datapackの作り方 入門編)
- スコアボードの基本(
objectives add/players add)(スコアボード入門)
今回の設計方針
前回の実装では load.json から welcome.mcfunction を呼び出していましたが、この方法には2つの問題がありました。
/reloadのたびに演出が流れてしまう@aで書いていたため、誰かが参加したとき全員のカウントが増えてしまう
今回は次の役割分担に変えます。
setup.mcfunction:起動時にスコアボードを初期化する(load.json経由)tick.mcfunction:毎tickプレイヤーを監視し、「参加」と「再ログイン」を検知する(tick.json経由)reset.mcfunction:ログアウトを検知したプレイヤーのタグとleave_gameをリセットするwelcome.mcfunction:1人のプレイヤーを対象に演出・条件分岐を実行する
フォルダ構成の確認
前回の記事から続けている場合、load.json は現在このようになっているはずです。
{
"values": [
"my_datapack:setup",
"my_datapack:welcome"
]
}
今回から welcome は tick.mcfunction 経由で呼び出す設計に変わるため、load.json から my_datapack:welcome の行を削除してください。また、もし my_datapack:welocm のようなスペルミスがある場合はこのタイミングで修正しましょう。編集後は次のようになります。
{
"values": [
"my_datapack:setup"
]
}
my_datapack/
├── pack.mcmeta
└── data/
└── my_datapack/
├── function/
│ ├── setup.mcfunction ← 変更
│ ├── tick.mcfunction ← 新規
│ ├── welcome.mcfunction ← 変更
│ └── reset.mcfunction ← 新規
└── tags/
└── function/
└── minecraft/
├── load.json
└── tick.json ← 新規

ステップ1:setup.mcfunction を更新する
今回は2つのobjectiveを用意します。minecraft.custom:minecraft.leave_game は dummy とは異なる特別なcriteriaです。このobjectiveを作るだけで、プレイヤーがゲームを退出するたびにMinecraftが自動でスコアを加算してくれます。手動で scoreboard players add する必要はありません。
scoreboard objectives add join_count dummy "参加回数"
scoreboard objectives add leave_game minecraft.custom:minecraft.leave_game "退出回数"
scoreboard objectives setdisplay sidebar join_count

ステップ2:tick.json と tick.mcfunction を作る
tick.json に登録されたファンクションは毎tick(1秒間に20回)自動実行されます。まず tags/function/minecraft/tick.json を新規作成します。
{
"values": [
"my_datapack:tick"
]
}
次に tick.mcfunction を新規作成します。2行で「ログアウト検知→リセット」と「参加検知→ウェルカム」の両方をこなします。順番が重要です。リセットを先に書くことで、再ログイン時にリセットとウェルカムが同じtick内で連続して走ります。

execute as @a[tag=welcomed] at @s if score @s leave_game matches 1.. run function my_datapack:reset
execute as @a at @s unless entity @s[tag=welcomed] run function my_datapack:welcome
1行目を分解してみましょう。
as @a[tag=welcomed]:welcomedタグを持つプレイヤーだけを対象にする(タグのない新規参加者はスキップ)at @s:実行位置をその @s の場所に切り替えるif score @s leave_game matches 1..:leave_gameが1以上=ウェルカム後に1回以上ログアウトしたrun function my_datapack:reset:reset.mcfunctionを呼び出してタグとleave_gameをリセットする
リセットでタグが外れた直後、同じtick内で2行目が走ります。unless entity @s[tag=welcomed] の条件を満たすため、再ログインプレイヤーも含めてウェルカム処理がそのまま続けて実行されます。
2行目を分解するとこうなります。
as @a:オンライン中の全プレイヤーを「実行者(@s)」として1人ずつ順に処理するat @s:実行位置をその @s の場所に切り替える(as @a at @sはセットで定番の書き方)unless entity @s[tag=welcomed]:@s がwelcomedタグを持っていないときだけ実行するrun function my_datapack:welcome:welcome.mcfunctionを呼び出す

ステップ3:if / unless / if score の使い方
if は「この条件を満たすとき」、unless は「この条件を満たさないとき」に後続の処理を実行します。よく使う条件を3パターン紹介します。
if entity:エンティティが存在するかどうかを条件にする
# "boss" タグを持つエンティティが1体でも存在するとき、全員にメッセージを送る
execute if entity @e[tag=boss] run tellraw @a [{"text":"ボスが出現しました!","color":"red"}]
なお、@a や @e には [条件] でセレクター引数を書いて対象を直接絞り込むこともできます。if entity は「1体でも存在するか」という真偽判定に、セレクター引数は「誰に対して処理するか」の絞り込みに使うのが基本の使い分けです。
# tag=vip を持つプレイヤーにだけパンを渡す方法(2通り)
# セレクター引数で絞り込む書き方
give @a[tag=vip] minecraft:bread 3
# execute as と組み合わせて @s で受け取る書き方(こちらが基本)
execute as @a[tag=vip] run give @s minecraft:bread 3
if score matches:スコアの値を範囲で条件にする
execute if score @s join_count matches 1 run tellraw @s [{"text":"初回!"}]
execute if score @s join_count matches 2.. run tellraw @s [{"text":"おかえり!"}]
execute if score @s join_count matches 1..5 run tellraw @s [{"text":"新人!"}]
execute if score @s join_count matches ..0 run tellraw @s [{"text":"マイナス?"}]
範囲の書き方は 1(ちょうど1)、2..(2以上・上限なし)、1..5(1以上5以下)、..0(0以下・下限なし)の4パターンを覚えておけば大半の場面に対応できます。
ステップ4:reset.mcfunction を作る
reset.mcfunction はログアウトが検知されたプレイヤーに対して走るファイルです。タグの削除と leave_game のリセットをここにまとめています。
tag @s remove welcomed
scoreboard players set @s leave_game 0
このファイルが実行された直後、同じtick内で tick.mcfunction の2行目が走ります。タグが外れているので unless entity @s[tag=welcomed] の条件を満たし、ウェルカム処理がそのまま続けて実行されます。

ステップ5:welcome.mcfunction を作り直す
tick.mcfunction から run function my_datapack:welcome で呼び出されたとき、as @a at @s の文脈がそのまま引き継がれます。welcome.mcfunction の中では最初から「1人のプレイヤーが @s になった状態」で処理が始まるため、このファイルの中では @a を書く必要がなく、すべて @s で完結します。これが「function の中で function を呼ぶ」ことの大きな利点です。
# 再実行を防ぐタグをつける(必ず最初に書く)
tag @s add welcomed
# 参加回数を加算
scoreboard players add @s join_count 1
# 全員共通の演出
tellraw @s [{"text":"ようこそ!","color":"gold","bold":true},{"text":"このサーバーに参加してくれてありがとう。","color":"white"}]
give @s minecraft:bread 5
effect give @s minecraft:speed 30 1 true
playsound minecraft:entity.player.levelup master @s ~ ~ ~ 1 1
# 参加回数の表示
tellraw @s [{"text":"あなたの参加回数:","color":"aqua"},{"score":{"name":"@s","objective":"join_count"},"color":"white"}]
# 参加回数に応じた条件分岐
execute if score @s join_count matches 1 run tellraw @s [{"text":"はじめまして!ゆっくりしていってね。","color":"gold","bold":true}]
execute if score @s join_count matches 2.. run tellraw @s [{"text":"またきてくれましたね!","color":"aqua"}]
execute if score @s join_count matches 5 run give @s minecraft:diamond 1
冒頭の tag @s add welcomed は必ず最初の行に書いてください。毎tick処理されるため、タグを先につけておかないと同じtick内で再度 welcome が呼び出されてしまいます。
playsound コマンドを追加した場合は、ワールドに入りなおして確認してみてください。参加時にレベルアップ音が聞こえるようになっているはずです。聞こえない場合はゲーム内の「設定 → 音楽とサウンド」でマスター音量がオフになっていないか確認してみましょう。


動作確認の手順
ファイルをすべて保存したら /reload を実行して、次の手順で動作を確認してみましょう。
- ワールドを一度退出し、再びログインする
- 「ようこそ!」メッセージと「はじめまして!」が表示されることを確認する
- チャットに
/scoreboard players get @s join_countと入力してスコアが1になっているか確認する /tag @s listでwelcomedタグがついているか確認する- 再度ワールドを退出して入りなおし、「またきてくれましたね!」に切り替わることを確認する
- 5回目に入ったときにダイヤモンドが届くことを確認する
うまく動かない場合は、/reload を実行したあとに /scoreboard objectives list で join_count と leave_game の2つのobjectiveが存在するか確認してみてください。objectiveがない場合は setup.mcfunction が正しく登録されていない可能性があります。
また、テスト中に参加回数がズレてしまったときや最初からやり直したいときは、次の2コマンドでまとめてリセットできます。
/scoreboard players reset @a join_count
全プレイヤーの join_count スコアを削除します。これで次回ワールドに入ったとき、全員が初回参加の状態からやり直せます。開発中は積極的に活用しましょう。

完成ファイル一覧(コピペ用)
tags/function/minecraft/load.json
{
"values": [
"my_datapack:setup"
]
}
tags/function/minecraft/tick.json
{
"values": [
"my_datapack:tick"
]
}
function/setup.mcfunction
scoreboard objectives add join_count dummy "参加回数"
scoreboard objectives add leave_game minecraft.custom:minecraft.leave_game "退出回数"
scoreboard objectives setdisplay sidebar join_count
function/tick.mcfunction
execute as @a[tag=welcomed] at @s if score @s leave_game matches 1.. run function my_datapack:reset
execute as @a at @s unless entity @s[tag=welcomed] run function my_datapack:welcome
function/reset.mcfunction
tag @s remove welcomed
scoreboard players set @s leave_game 0
function/welcome.mcfunction
tag @s add welcomed
scoreboard players add @s join_count 1
tellraw @s [{"text":"ようこそ!","color":"gold","bold":true},{"text":"このサーバーに参加してくれてありがとう。","color":"white"}]
give @s minecraft:bread 5
effect give @s minecraft:speed 30 1 true
playsound minecraft:entity.player.levelup master @s ~ ~ ~ 1 1
tellraw @s [{"text":"あなたの参加回数:","color":"aqua"},{"score":{"name":"@s","objective":"join_count"},"color":"white"}]
execute if score @s join_count matches 1 run tellraw @s [{"text":"はじめまして!ゆっくりしていってね。","color":"gold","bold":true}]
execute if score @s join_count matches 2.. run tellraw @s [{"text":"またきてくれましたね!","color":"aqua"}]
execute if score @s join_count matches 5 run give @s minecraft:diamond 1
まとめ
execute as @a at @sで全プレイヤーを1人ずつ処理でき、@sがその1人1人を指すunless entity @s[tag=タグ名]で「タグを持っていないとき」だけ処理できるminecraft.custom:minecraft.leave_gameはログアウト回数をMinecraftが自動カウントしてくれる特別なcriteriatick.mcfunctionはリセットを先に書くことで、再ログイン時にリセット→ウェルカムが同じtick内で連続して走るrun functionで呼び出した先のファイルには、呼び出し元のas/atの文脈がそのまま引き継がれる
次の記事「スコアボード × /execute:スコアで条件分岐しよう」では、今回使った if score matches をさらに深掘りして、複数の条件を組み合わせた処理分岐の書き方を解説します。今回作ったDatapackがそのまま題材になるので、ぜひ続けて読んでみてください。
