見出し画像

【Minecraft統合版】新executeのまとめ(応用編)

最終更新: 2024/1/21

1.19.50のアップデートによって、他のあらゆるコマンドと比較しても最高クラスの難易度を誇るようになったexecuteコマンド。
この記事ではその新しくなったexecute、通称「新execute」の解説を行いますが、応用編となります。
基礎的な知識の解説は行いません。
基礎編については以前の記事をご覧ください。


はじめに


この記事には「応用編」なんて大層な名前がついていますが、そもそもの新executeの難易度が高すぎるため、あまり高度な話をすると前回の記事との難易度の差が開きすぎてしまいます。
しかし応用編は応用編。そういう話をしないわけにも行きません。
ですので、今回の記事は二部構成とします。
新executeを自力で書けるようになるには、そしてその上でちょっと高度な応用技術を紹介していこうと思います。

新executeは何故難しいのか


「旧executeに比べて難しい」ということ自体は触れてみればすぐにわかると思いますが、なぜここまで新と旧には差があるのかと言われると、まあ答えは様々だと思います。
最も多いと予想される回答は「サブコマンドの種類の量」でしょうか。

私はもうひとつ大きなものがあると思っています。「マルチな処理を脳内で行う必要があるから」です。
旧executeとはどのようなコマンドか?と問われたとき、答えるのは簡単でしょう。「コマンドを誰かに実行させるコマンド」。
このコマンドを書くとき常に考えるのは「今実行者は誰なのか」です。
新executeでは、実行者が誰なのかを考えるのはもちろん、実行座標、実行方向、もしかすると実行ディメンションまで考えければならないかもしれません。複数の情報を一度に並行して扱うのは並の人間ができる所業では決してありません。

マルチな処理を脳内で正確に行えることを前提としたコマンド。そんなの、できないのがむしろ普通では?
何が難しいのかがはっきりしたところで、新executeはどのようなことを考えて書くべきなのかを書いていきます。

新executeの考え方


新executeを書くとき、意識するべき情報は主に3つです。

  • 「実行者」…… “@s” が指し示すエンティティであり、また/sayなどの一部のコマンドに影響を及ぼす

  • 「実行座標」…… 相対座標(~ ~ ~)の基準となる位置であり、デフォルトでセレクターの探索の基準位置となる

  • 「実行方向」…… ローカル座標(^ ^ ^)の移動の軸となる方向

ディメンションを考える必要がある場合もないとは言えませんが、あまり使わないので今回は無視します。

この3つを並行して処理します…が、流石に脳がパンクするので、いったん実行方向は置いておいて、「実行者」と「実行座標」について考えていきたいと思います。

処理は分岐する


まず読み解くところからやっていきます。

execute as @a at @s run playsound note.pling @s ~ ~ ~ 10 2

このコマンドは、まずasで実行者を変更しています。
実行者とは、「@sが指し示すもの」でした。
つまり、as @aは実行者をすべてのプレイヤーにしています。

次に来ているのはat @sです。atは実行座標を変更します。@sは実行者を指すので、先程実行者を変更した影響がここで出てきています。

ここで重要なのが、「全プレイヤーを実行者として、実行座標を実行者の位置に変更する。」
この文の意味を正しく理解することです。
実行者 = 全プレイヤーなので、実行座標 = 全プレイヤー…という捉え方でも悪くはありませんが、もうすこし正確な捉え方をしておくべきです。

セレクターを指定することができるサブコマンドにおいて、
そのセレクターを満たすエンティティが複数体いるとき、
サブコマンドの処理は分岐します。

分岐とはどういうことかというと、処理の順番という概念が登場するということです。先程のコマンドを例にすると、as @aの時点で処理は分岐しています。
具体的には、プレイヤーがA, Bのように複数体いたとき、Aがその続きであるat @s run ~を実行し終えてから、Bも続きのat @s run ~を実行し始めるということです。

この考え方をすると非常に新executeの動きは捉えやすいです。
まずas @aによって処理が分岐し、一人目のプレイヤーが続きを実行し始めます。
at @s、実行する位置を実行者の位置に、つまり一人目のプレイヤー自身の位置に変更します。
そうして実行者 = @sが指し示すもの、実行座標 = ~ ~ ~の基準位置の二つが一人目のプレイヤー自身のものに変更され、playsoundコマンドによって音が再生されます。「@s ~ ~ ~ 10 2」、すなわち一人目のプレイヤー自身に対して、一人目のプレイヤーのいる位置から、音量10のピッチ2でnote.plingというサウンドが再生されるのです。
そうして一人目のプレイヤーの処理をすべて終えたサブコマンドasは、二人目のプレイヤー以降も同様に処理していきます。

先程のコマンドを少しだけ変えてみましょう。

execute as @a at @a run playsound note.pling @s ~ ~ ~ 10 2

atの後に来ていた@sが、@aに変わったのがわかりますか?
こうしても流れの追い方は一緒です。

まずas @aによって処理が分岐し、一人目のプレイヤーが続きを実行し始めます。同じですね。
at @a、実行する位置を全プレイヤーの位置に変えます。
ここはどう捉えるべきでしょうか?
少し前に言ったはずです。

セレクターを指定することができるサブコマンドにおいて、
そのセレクターを満たすエンティティが複数体いるとき、
サブコマンドの処理は分岐します。

atは「セレクターを指定することができるサブコマンド」ですし、
今回@aですので、そのセレクターを満たすエンティティも複数体ですよね。
つまりここにも分岐が発生します。

わかりやすく図っぽくしてみます。

# (数字) = 実行される順番

execute
┃
┣実行者一人目
┃  ┣実行座標一人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (1)
┃  ┣実行座標二人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (2)
┃  ┗実行座標三人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (3)
┣実行者二人目
┃  ┣実行座標一人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (4)
┃  ┣実行座標二人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (5)
┃  ┗実行座標三人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (6)
┗実行者三人目
   ┣実行座標一人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (7)
   ┣実行座標二人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (8)
   ┗実行座標三人目━━━ playsound note.pling @s ~ ~ ~ 10 2 (9)

なんとなく理解できますでしょうか。
asによる実行者の分岐ののち、at @aによりもう一度分岐が発生しますが、分岐した処理は一人目が終わらないと二人目に移りません。
そのため、実行者一人目の処理で、全員を一人ずつ実行座標としたplaysoundの実行が終わらなければ実行者二人目の処理には行きません。
つまりこのコマンドは、全プレイヤーがそれぞれ、全プレイヤーの位置から自身に対してサウンドを再生するものです。

ここまでの内容を理解できてしまえば基礎で躓くことはまずないと思います。

@sは何を指しているのか


今からするのはあたりまえの話です。
しかし重要です。
次のコマンドを見てみます。

execute as @e[type=armor_stand] at @s at @p run say @s[c=1]

どのような動作をすると思いますか?
直訳するなら、全ての防具立てを実行者として、実行者から最も近いプレイヤーの位置から、最も近い位置にいる実行者をsay…でしょうか?
ここまでの内容を理解しているのであれば、この”直訳”が適切でないことに気付くかもしれません。
何故なら、サブコマンドの実行は、処理を分岐させる可能性があるからです。

まず、as @e[type=armor_stand]。ここで既に分岐してしまいます。
分岐するということは、一体目の防具立てが後続のサブコマンドをすべて実行してから二体目も同じことをし、そして三体目…のように処理されていきます。

ではそれぞれは後続のサブコマンドを読んでどのような処理をするのかというと、at @s at @p run say @s[c=1]。
まず実行位置を自身の位置とし、さらにそこから最も近いプレイヤーの位置に実行位置を上書きします。
最後にそこから最も近い位置にいる「実行者」をチャットに表示…
ここでの「実行者」って、誰のことでしょうか。
as @e[type=armor_stand]とあるので、防具立て全員?処理は分岐しているので、現在処理している防具立て一体のみ?

答えは後者です。
asで既に処理は分岐しています。そうすると、一人目の処理において登場する「@s」は一人目自身を指し、二人目の処理において登場する「@s」は二人目を指します。
つまり、「最も近い実行者」という概念自体存在しません。
このコマンドは、常に全防具立てが自分自身をチャットに出力するだけのコマンドです。
実行者を指定できるセレクター@sは、常に、いかなる状況であろうとも、必ず一体のみを指し示すものであるのです。

「マルチな処理」をする


ここからは話がちょっと変わります。
一旦分岐の話は終わりにして、実行者・実行座標・実行方向をフル活用した「マルチな処理」をするコマンドを書いていきます。

とりあえず例文を出します。

execute as @e[type=armor_stand] at @s at @p positioned ~ ~-5 ~ facing entity @s feet facing ^ ^ ^-1 positioned as @s run tp ^ ^ ^1 

長いですね。でも読めます。
まず、全ての防具立てを実行者とします。
それぞれが自分自身の位置を基準に、最も近くのプレイヤーの位置に実行座標を移します。
ここまでは理解できますでしょうか。
このような長い文を紐解くときには、このあたりで一旦具体的にイメージしてみるといいです。
慣れないうちは、手書きメモ帳的なアプリに図解してもいいですね。
ではその次のpositioned ~ ~-5 ~から先を図解して考えていきます。

🔴=防具立て、🔵=最寄りのプレイヤー

今実行座標は防具立てから最寄りのプレイヤーの位置です。
positionedは実行座標を変更する力を持っているので、実行座標を~ ~-5 ~に変更します。
ではそれはどこのことを指しているのかというと、実行座標を基準に5m下の点です。
相対座標の基準となる位置、スタートとなる位置は実行座標です。よってまずは実行座標の5m下に下がります。

お次は「facing entity @s feet」とあります。
サブコマンドfacingは、実行座標を基準に、指定の座標への方向をそのまま実行方向に変えることができます。
つまり、現在の実行座標から、@s(実行者)のfeet(足先)への方向がそのまま実行方向になります。

緑矢印=実行方向

次は簡単です。facing ^ ^ ^-1とありますね。
facingは指定の座標への方向をそのまま実行方向にするので、^ ^ ^-1、つまり現在の実行方向の真逆の方向へ1m進んだ位置への方向を実行方向とします。
一言で言えば、実行方向を逆向きにする。それだけです。

そろそろrunに近づいてきました。
positioned as @sがrunの前の最後のサブコマンドです。
「positioned as (セレクター)」は実行座標をそのエンティティの位置に変えるだけなのですが、「at (セレクター)」とは若干異なる点があります。
それは、atは実行方向とディメンションもそのエンティティに合わせてしまうのに対し、positioned asは実行座標のみをそのエンティティに合わせるというものです。
この違いを利用して、今のこの実行方向を維持したまま、実行座標だけを@sに、つまり防具立ての位置に移動させます。

橙矢印=実行座標の移動によって移動した実行方向

最後のコマンドはrun tp ^ ^ ^1、防具立てが実行方向と同じ方向に1m前進するだけですね。
ここまで図にできるとわかるのではないでしょうか。
このコマンドは、「防具立てが、最寄りのプレイヤーの5m下の点に向かって移動していくコマンド」です。

新executeを書くには


勘のいい方ならお気付きかもしれません。

「コマンド読んでばっかで書いてなくね?」

はい。さっきからずっと読んでます。
しかし、読めれば読めるほど書けるようになるんです。

どういうことかと言いますと、そもそもexecuteに限らずコマンドはパクりながら覚えていくのが一番の近道です。
「こういうふうに書くとこういうことができる」というのがわかれば、それだけで着実に書けるようになっています。
そのためスラスラ書けるようになるには慣れるしかありませんが、その過程でまだ知らない書き方を見かけたら、この記事のように図解などで紐解いてみてください。それができれば、さらに高度な文を書けるようになっていきます。
この記事で先程まで解説してきたことを頭の片隅に入れながら書くのもいいと思います。
コマンドは「習いながら慣れろ」かもしれませんね。

新executeの応用技術


ここからは、新executeにアップデートされたことによって可能となった、様々な応用技術を原理の解説と一緒に紹介していきます。

OR条件


1つ目はOR条件です。
例えば、タグAとタグBの2つのうちどちらかのタグを持っているエンティティを指定したいとします。
その場合以下の1コマンドで実現可能です。

execute as @e unless entity @s[tag=!A,tag=!B] run say AorB

Aでもなく、かつBでもないという状況のみを除外することで、AとBの和集合を取ることができます。
要するに「ド・モルガンの法則」ってやつです。

A or B = not ((not A) and (not B))

数学の法則をそのままコマンドにも転用しているというだけなのでそこまで難しいことはありません。覚えておくと便利なので是非。

連番の付与


特定のエンティティに対して、一体一体それぞれ異なる値をスコアボードに入れることができます。要するにID的なものを割り当てられます。
様々な方法が存在しますが、紹介するのはそのうちの新executeを利用したもののみにさせていただきます。

scoreboard players set @a id 0

execute as @a[scores={id=0}] at @a[scores={id=0}] run scoreboard players add @s id 1

「処理の分岐」を利用することで、全プレイヤーに1, 2, 3, 4…と値をそれぞれに割り当てることができます。
まず一人目の実行者がat @a[scores={id=0}]を実行しますよね。ここでも分岐が起きるので、一人目の実行者のスコアボードにはプレイヤーの人数分の値が加算されることになります。ワールドにプレイヤーが4人いれば4、3人いれば3ですね。
二人目はどうなるのかというと、同じくat @a[scores={id=0}]で分岐しますが、先程の処理で一人目の実行者はスコア"id"が0ではなくなっています。
つまり、二人目の実行者のスコアに代入されるのはその一人目の分を除いた、プレイヤーの人数-1の値です。
三人目も同様にプレイヤーの人数-2、四人目もプレイヤーの人数-3が加算され、最終的には1, 2, 3, 4…と連番が付与されます。

正射影(近似)


execute幾何学と呼ばれる分野の基礎の一つです。
説明が難しいので実用的な例で紹介します。

execute
    as A
    at B positioned ~ ~100000 ~
    facing entity @s feet positioned ^ ^ ^100000
    run tp ~ ~ ~

上記のコマンドを実行すると、AのY座標がBのY座標と同じになります。

Bの真上に10万m進み、そこからAに向かって10万m進んでいるだけです。
真上方向に伸びる線と、そこからAの方向に伸びる線はどちらも長さが大きすぎるため、もはや平行と見なせます。

Bから10万上に上がり、Aに向かって10万進んだ図


そのため、10万上がって10万下がるだけでこれが実現できるのです。
また10万という数字が特殊なわけではありません。20万でも30万でもokなのですが、あまり大きな値にしてしまうととある仕様によりうまく動かなくなってしまいます。
詳しくは説明しませんが、浮動小数点数の有効数字の限界のせいとだけ言っておきます。

直線について対称な移動(近似)


こちらもexecute幾何学です。
今回はZ軸について対称な位置に移動させます。

execute as A at @s
    positioned ~ ~ ~100000
    facing 0.0 0.0 0.0 positioned ^ ^ ^200000
    positioned ~ ~ ~100000
    run tp ~ ~ ~

上記のコマンドで実現できます。
先程の正射影と同様に近似を使用しており、Z軸に平行な方向に10万進み、そこから原点へその倍の20万進み、再び先程と同じ方向に10万進むとZ軸についてAを反転させることができます。
原点に向かう長さ20万の線分がもはやZ軸に平行であるため、以上の操作で実現できます。

点について対称な移動(近似)


先程の正射影・軸についての対象移動の合わせ技のようなものです。
結論から言えば以下で実現できます。
点Aの位置から点Oについて対称な位置にBを移動させます。

execute as A

  at O positioned ~ ~100000 ~
  facing entity @s feet positioned ^ ^ ^100000

  positioned ~100000 ~ ~ facing entity @s feet
  rotated ~180 ~ facing ^ ^ ^-1 positioned ^ ^ ^100000

  positioned ~ ~100000 ~ facing entity O feet
  positioned ^ ^ ^200000 positioned ~ ~100000 ~

  run tp B ~ ~ ~

線分AOの垂線の足Hをとり、AHの2:1の外分点をさらにY軸について対称な位置に移動させます。
こちらに関しては説明も面倒なので気が向いたときに詳しい説明を書いておきます。()

スコアの値が一致するエンティティの取得


「処理の分岐」を利用する技術です。
先にコマンドをお見せします。

execute
    as @a at @e[type=armor_stand]
    if score @s object = @e[c=1] object
    run say @e[c=1] は私とスコアが一致しています

各プレイヤーが、自身とスコアボード「object」の値が一致している防具立てを全員チャットに出力します。
as @aで分岐が発生するので、全プレイヤーがそれぞれの防具立ての場所で一回ずつ処理を行います。
atの時点で実行座標は防具立ての位置に完全一致しているので、のちの@e[c=1]、つまり最も近くのエンティティを指すセレクターは常にその防具立てを示します。

execute as
┣━━A
┃ ┣at 1 if score @s(=A) object = @e[c=1](=1) ... run
┃ ┣at 2 if score @s(=A) object = @e[c=1](=2) ... run
┃ ┣at 3 if score @s(=A) object = @e[c=1](=3) ... run 
┃ ┣at 4 if score @s(=A) object = @e[c=1](=4) ... run
┣━━B
┃ ┣at 1 if score @s(=B) object = @e[c=1](=1) ... run
┃ ┣at 2 if score @s(=B) object = @e[c=1](=2) ... run
┃ ┣at 3 if score @s(=B) object = @e[c=1](=3) ... run 
┃ ┣at 4 if score @s(=B) object = @e[c=1](=4) ... run
┃
.
.
.

また、ifをunlessに置き換えて値が等しくないことの検知を行ったり、=を<や>に置き換えて値が自身のものよりも大きい・小さいエンティティを取得することが可能です。

スコアが一致するエンティティのうち最も近いものを取得


セレクター引数「c=」はマイナスの値が指定されると処理順を逆にするという仕様があります。
これを利用すると近い順ではなく遠い順でエンティティを処理できるので、最後の処理で最も近いものが処理されることになります。
tpはいわば「座標の上書き」であるため、最後の結果だけが残ることにより最寄りにテレポートできます。

execute
    at @e[type=armor_stand,c=-100000]
    if score @s object = @e[c=1] object
    run tp ~ ~ ~

c=に巨大な値を渡しているのは、選択するエンティティの上限を制限するというc=本来の役割を使わないようにして処理順を逆にする効能だけを取り出すためです。

球体の描画


・tag=axisのボートを二つ(0, 90向きと180, -90向き)用意
・球の中心となる位置にcenterのタグを持つエンティティを配置

execute as @e[tag=center]

    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @e[tag=axis]
    at @s

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^4
    rotated as @r[type=boat,tag=axis] positioned ^4 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^4

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^2
    rotated as @r[type=boat,tag=axis] positioned ^2 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^2

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^1
    rotated as @r[type=boat,tag=axis] positioned ^1 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^1

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^0.5
    rotated as @r[type=boat,tag=axis] positioned ^0.5 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^0.5

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^0.25
    rotated as @r[type=boat,tag=axis] positioned ^0.25 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^0.25

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^0.125
    rotated as @r[type=boat,tag=axis] positioned ^0.125 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^0.125

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^0.0625
    rotated as @r[type=boat,tag=axis] positioned ^0.0625 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^0.0625

    rotated as @r[type=boat,tag=axis] rotated ~ 0 positioned ^ ^ ^0.03125
    rotated as @r[type=boat,tag=axis] positioned ^0.03125 ^ ^
    rotated as @r[type=boat,tag=axis] positioned ^ ^ ^0.03125

    facing entity @s feet
    positioned as @s[r=8]

# ここの最後の数字が半径
    positioned ^ ^ ^5

    run particle minecraft:basic_flame_particle

モンテカルロ法と呼ばれる方法による球面描画です。
今回は16^3の立方体の中にランダムな点をいくつかとり、そのうち直径16の球の範囲内の点のみに絞りこみます。
その各点から中心への向きを求め、中心に移動し、描画したい球の半径の分だけその向きに進んだ位置にパーティクルを出しています。
無駄に長いコマンドですが、やっていることは本当にそれだけです。

上記の方法の応用。
2エンティティの距離を直径とする球体。

線分の描画


こちらは球よりも簡単です。
・tag=axisのボートを二つ(任意の向きとその逆向き)用意
・線分の中点にtag=centerのエンティティを配置

execute as @e[tag=center] at @s

    rotated as @e[tag=axis] positioned ^ ^ ^8
    rotated as @e[tag=axis] positioned ^ ^ ^4
    rotated as @e[tag=axis] positioned ^ ^ ^2
    rotated as @e[tag=axis] positioned ^ ^ ^1
    rotated as @e[tag=axis] positioned ^ ^ ^0.5
    rotated as @e[tag=axis] positioned ^ ^ ^0.25

    run particle minecraft:basic_flame_particle

tag=centerから任意方向に伸びる長さ16の線分です。
プレイヤーの目の位置から正面に出す場合であれば、ボートをプレイヤーの向きとその逆方向にそれぞれ向けて、プレイヤーの視線の向きに8m進んだ位置から同じことをすればできますね。

rotatedによる分岐の図解

仕組みとしては上の画像の通りです。見やすくするために敢えて矢印の始点がずらされていますが、実際にはこれが一直線上で行われています。
16, 8, 4, 2, 1…と進む距離を1/2(2倍でもok)しながら分岐していくことによって、等間隔に実行座標を配置します。

極大半径式当たり判定


セレクター引数r=に巨大な値を渡すことによって、エンティティが左右どちらにいるか、前後どちらにいるか、上下どちらにいるかなどを判定できます。
実用的な例としては、以下がアップデート前のdx・dy・dzセレクター引数、つまりヒットボックスではなくエンティティの足先を参照するdxyz=を再現し、一般化したものです。

解説には前方検知を使用します。

execute as @a at @s
    positioned ^ ^ ^10000
    at @e[type=!player,r=10000]
    run particle minecraft:mobflame_single

プレイヤー以外のエンティティがプレイヤーの前方に居た場合、そのエンティティの位置に炎のパーティクルを出します。

まずプレイヤーの位置から、プレイヤーの視線の方向に向かって1万進みます。そこから半径1万の球を出すと、プレイヤーの前方にいるエンティティはその球体内のエンティティです。
半径1万ということは、球の端から見ればそれは大きすぎてもはや面です。
プレイヤーの位置から、視線の方向に垂直な面を出し、その向こう側のエンティティを取得する。
つまりこういうことを行っているのです。

さいごに


もっと色々な応用技術を載せたかったのですが、意外に思い浮かばなかったのでここまでにしておきます。
何か思いつき次第順次追加していきます。

前回の新execute解説よりも難しい言葉を使わざるを得ませんでしたが、新executeに対する理解を少しでも深めていただいていれば幸いです。
では。

この記事が気に入ったらサポートをしてみませんか?