execute を使いこなそう!
※この記事では/execute や/scoreboard などのコマンドはある程度理解している前提で話を進めます。
前提知識を求める場合、以下の記事が参考になるかもしれません。
クラフターズコロニー管理人の解説ブログ : https://minecraft-mcworld.com/tutorial/be-execute/
たけのこIIさんの解説ブログ : https://note.com/takenokoii/n/n84725807e5c3
皆さんは/execute コマンドは使ったことがありますか?
私がコマンドで何か作るとなれば、9割はexecuteコマンドで完結させます。
それくらいexecuteは便利なコマンドなんです! (最近はscriptで楽してるけど…)
ここで皆さんに覚えてほしいのは、実行者と実行座標を使い分けることです。
「実行者」と「実行座標」を使い分ける
リピート (常時実行) + チェーン
/execute as @a[tag=attack,scores={team=1}] at @s run damage @a[r=5,scores={team=!1}] 10
/execute as @a[tag=attack,scores={team=2}] at @s run damage @a[r=5,scores={team=!2}] 10
/execute as @a[tag=attack,scores={team=3}] at @s run damage @a[r=5,scores={team=!3}] 10
/execute as @a[tag=attack,scores={team=4}] at @s run damage @a[r=5,scores={team=!4}] 10
/tag @a remove attack
時に、皆さんはこのようなコマンドを作った経験はありませんか?これらのコマンドは、
「attackタグが付与されているプレイヤーを実行者として、その半径5m以内のプレイヤーに対して、実行者のteamのスコアと違うteamのスコアを持つプレイヤーに対してダメージを与える」
という処理を行っています。
アイテムを使用したらattackタグが付与されてスキルが発動するという、PVPなどでよくあるコマンドですね。
しかし、この書き方はやや冗長で保守性も低いです。(同じようなことを何度も書いていて、あとから追加したり修正したりがめんどくさい)
ここで、実行者と実行座標の使い分けをします!
リピート (常時実行) + チェーン
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run damage @s 10
/tag @a remove attack
コマンドをこのように修正することで、コマンドの量が減って、保守性も上がりました!(後から修正するときは1行修正するだけで事足りるし、チームを増やしてもコマンドを増やす必要はない)
このコマンドを日本語に訳してみると、
「attackタグが付与されているプレイヤーを実行者、その場所を実行座標として、実行者を元の実行者以外の全員に変更し、実行座標から一番近い人(つまり元の実行者)のスコアと実行者のスコアが不一致ならば、実行者を実行座標から半径5m以内のプレイヤーに変更し、そのプレイヤーに10ダメージを与える」
となります。なにやら複雑そうで嫌になりますが、、、
ここで注目すべきポイントは、「元の実行者」を保持している点です。
元の実行者は実行座標から最も近いプレイヤーとなるので、@pを使うことで指定することができます。
これが「実行者と実行座標の使い分け」です!
実行者と実行座標を使い分けることで、2種類のエンティティを1つのコマンドで同時に保持できるのが最大の魅力です。
2種類のエンティティを同時に保持しておくことで、「2種類のエンティティのスコアの比較」が容易となります!
例えば、「人狼ゲームの占いコマンドを作りたい!」と思ったとしましょう。
皆さんはどのようにコマンドを組み立てますか?
一番良くないのは「本を捨てたら別の場所にテレポートして、番号ごとあるボタンを押したら占いができる」とかです。
旧executeの時代ならばこれしか方法が無かったかもしれませんが、今は違います。
コマンド主体で作る場合、私なら「何かしらの方法で占う対象を選択して、右クリック等でアイテムを使用すると判定される」とかでしょうか。
(アイテムを捨てて使用でも良いですが、他のプレイヤーと重なっている場合などに意図しない挙動を引き起こす可能性があるので、できればアドオンを使用してクリックや殴りなどで検知するのが賢明です。スニークしたときに占い用アイテムを手に持っているかどうかをhasitemで検知とかでもよさそうですね。)
何かしらの方法というのが曖昧ですが、とりあえずプレイヤーの管理番号と、占い対象の番号を管理するスコアを用意しておいて、
占いを行うプレイヤーが持つ占い対象の番号とプレイヤーの管理番号を比較して、それらが一致するプレイヤーを判定することにしましょう。
占い対象の番号については、スニークすると占い対象を変更できる、上を向いて右クリックすると占い対象を変更できる、などとします(これらの仕組みの解説は省きます)
この時、課題となるのが「プレイヤーの管理番号と占い対象の番号の比較」です。
そこで、「実行者と実行座標の使い分け」の出番です!
占いコマンド用に、プレイヤーの管理用のスコア number, 占い対象管理用のスコア fortune_num, また判定用の fortune_resultの3つのスコアを用意しておきます。(判定用は/tag とかでも良い)
※numberの振り分け、fortune_numの値の決定、fortune_resultの設定などは各自行ってください。
リピート (常時実行) + チェーン
/execute as @a[tag=fortune] at @s as @a if score @s number = @p fortune_num if score @s fortune_result matches 0 run tellraw @p {"rawtext":[{"text":"§b"},{"selector":"@s"},{"text":"§fは§a市民§fです"}]}
/execute as @a[tag=fortune] at @s as @a if score @s number = @p fortune_num if score @s fortune_result matches 1 run tellraw @p {"rawtext":[{"text":"§b"},{"selector":"@s"},{"text":"§fは§4人狼§fです"}]}
/tag @a remove fortune
これらのコマンドを日本語訳すると、
「fortuneタグが付与されているプレイヤーを実行者、その場所を実行座標として、実行者をワールドにいる全員に変更して、実行者と実行座標から一番近い人(つまり元の実行者)のnumberとfortune_numの値が一致している場合、fortune_resultの値に応じてtellrawコマンドで実行座標から一番近い人(元の実行者)に判定結果を伝える」
となります。
このようにして、実行者のスコアと対象のスコアを比較することで、占いのコマンドを容易に実現することが可能です!
「とりあえず at @s を書いておく」からは卒業しよう!
/execute as @a[scores={team=1},c=1] at @s ...
よく初心者のうちは as <セレクター> at @s などと、とりあえず at @sを付けておけばいいと解説されます。(別に悪いわけではないですが)
ただ、とりあえずのままでいるといつまでも理解を放置するだけなので、ここからはもう卒業しちゃいましょう!
例えば、以下のコマンドで at @s の項目は必要か、必要でないかを判断できますか?
/execute as @a at @s run tag @s add test
このコマンドは「全員にtestタグを与える」というものですが、結論 at @sの項目は必要ありません。
atのサブコマンドは実行座標を変更するもので、座標を必要としないコマンドでは必要ないんですね。
実行座標が必要なコマンドは以下のようなものが挙げられます。
- r セレクターや rm セレクターなどの半径指定を必要とする場合
- x, y, z, dx, dy, dzなどの範囲指定を必要とする場合
- fill, setblock, playsoundコマンドなどで ~~~ や ^^^ などの相対座標を扱う場合
etc…
「とりあえず at @s を書いておく」からは卒業です!
応用編 : 複雑なセレクターに対応させるには?
リピート (常時実行) + チェーン
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run damage @s 10
/tag @a remove attack
先ほど例に挙げたこちらのコマンドですが、条件に「最も近いプレイヤー1人」を追加したいとなったらどうすれば良いでしょうか?
単に2つ目のas で@a[rm=0.01,c=1]とするのはダメです。
なぜなら、このコマンドを実行したときに一番近くにいるプレイヤーが同じチームのプレイヤーだった場合、そのプレイヤーだけが判定されてしまうためです。PVPのスキルでは、一番近くにいる敵プレイヤーに攻撃を当てたいはずです。
また、3つ目のas で@s[r=5,c=1]とするのもダメです。
なぜなら、2つ目のas @sでは、元の実行者とteamの値が不一致のプレイヤー各自が実行者となっているからです。
わかりにくいですね。図を使って解説しましょう。
ここで、最初にコマンドを実行したプレイヤーは真ん中の大きな青いプレイヤーとします。また、各プレイヤーはteamの値で色分けしてあります。
まず、最初の「as @a[rm=0.01]」で実行者が真ん中の大きな青いプレイヤー以外のプレイヤーに変更されます。
次に、「unless score @p team = @s team」で他の青いプレイヤーは除外されます。
最後に「@s[r=5]」ですが、ここで実行者は青色以外のプレイヤーとなっています。
つまり、他の各プレイヤーがそれぞれ「/execute as @s[r=5] run …」 を実行している、コマンドが複数実行されていることに変わりありません。
ここで、条件に「c=1」を追加しても、各プレイヤーそれぞれが実行しているため、この条件は意味を成しません。
ではどうすれば良いのか、結論はこうなります。
リピート (常時実行) + チェーン
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run tag @s add damage
/execute as @a[tag=damage,c=1] run damage @s 10
/tag @a remove damage
/tag @a remove attack
このように、まずr=5の条件を満たしている場合にdamageタグを付与し、その次のコマンドで「tag=damage」のプレイヤーを対象に指定をやり直します。
これで半径5m以内の最も近いプレイヤーに対してダメージを与えるコマンドを作ることができます!
実行座標を使った指定を複数行いたいときは、コマンドを分けて指定することが重要です。
番外編 : function を使いこなそう!
ここからは BP の function も交えた話となりますが、functionは実行者と実行座標という観点からは大いに役に立ちます。
どういうことかというと、functionは「複数のコマンドを同時に実行できる」ことが最大の魅力です。
例えば、以下のようなコマンド
リピート (常時実行) + チェーン
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run tag @s add damage
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run tellraw @s {"rawtext":[{"text":"10ダメージを食らった!"}]}
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] at @s run playsound note.bass @s ~~~ 1 1 1
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5] run damage @s 10
/tag @a remove attack
※playsound コマンドは実行座標から音を発生させるため、at @s を追加で記述します。
先ほど解説した「実行者と実行座標を使い分ける」ことでteamの判定に関しては簡潔に書けていますが、実行したいコマンドが多いために結局冗長になり、保守性が下がってしまっています。
これを解決するのが、functionです!
以下は damage という関数です。
tag @s add damage
tellraw @s {"rawtext":[{"text":"10ダメージを食らった!"}]
playsound note.bass @s ~~~ 1 1 1
damage @s 10
function で実行したいコマンドをまとめて、上記コマンドを以下のように変更します。
リピート (常時実行) + チェーン
/execute as @a[tag=attack] at @s as @a[rm=0.01] unless score @p team = @s team as @s[r=5,c=1] at @s run function damage
/tag @a remove attack
このようにすることで、冗長性が減り、保守性が高まりました!
functionを使ってコマンドを実行させることで、実行者と実行座標が全てのコマンドに対して適用されるので、より簡潔に書くことが可能です。
おわり
この記事ではコマンドブロックを使用しての解説をしてきましたが、どちらかというとアドオンのfunction, animation, scriptなどを使った開発者向けかもしれませんね。(「as @a[tag=attack] at @s」で指定するのも冗長な気はするし…)
質問やリクエストなどがあれば、コメント欄でぜひ!
解説は以上です!良いコマンドライフを!
コメント