13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Minecraft CommandAdvent Calendar 2023

Day 8

executeテクニック集【execute幾何学など】

Last updated at Posted at 2023-12-07

はじめに

executeのサブコマンドを組み合わせたテクニックの中で、コマンドの軽量化や単純化などに役に立つ可能性があるものを紹介します。
この記事は、executeサブコマンド機能の大体の理解と、高校程度の数学の知識を前提としています。

コマンド例の前提

コマンド例の内にある括弧()で囲われた部分は適宜置き換えてください。
コマンド例の実行環境では、objというスコアオブジェクティブが定義されており、obj #数字というダミースコアホルダにはその数字が入っているものとします。

基礎技術編

これらの基礎的な技術を組み合わせて様々なことができます。
実用的な応用が知りたい方は応用テクニック編まで飛ばしてください。

幾何学関連

幾何学的な座標操作を利用するテクニックです。

縦角度反転

(重要度☆☆☆)
実行方向の縦角度(x_rotation)を反転します。

縦角度のY軸対称反転
execute (角度反転前のコマンド) rotated ~180 ~ (角度反転後のコマンド)
縦角度のXZ平面対称反転
execute (角度反転前のコマンド) rotated ~180 ~ facing ^ ^ ^-1 (角度反転後のコマンド)

横角度反転

(重要度☆☆)
実行方向の横角度(y_rotation)を反転します。
横角度反転では、後述する絶対座標を活用しています。
絶対座標の原点位置に移動する都合上、反転前の位置の情報は消えてしまいます。

横角度のXY平面対称反転
execute (角度反転前のコマンド) positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 positioned ~ ~ 0.0 positioned ^ ^ ^0.5 facing 0.0 0.0 0.0 (角度反転後のコマンド)
横角度のYZ平面対称反転
execute (角度反転前のコマンド) positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 positioned 0.0 ~ ~ positioned ^ ^ ^0.5 facing 0.0 0.0 0.0 (角度反転後のコマンド)

絶対座標の活用

(重要度☆)
絶対座標での原点(X=0,Y=0,Z=0)付近を利用することでできる様になることがいくつかあります。
一つは、positioned as (セレクタ)ではXYZ軸全ての座標が変わってしまうが、絶対座標での位置に対してはpositioned ~ ~ 0.0の様に特定の軸のみ合わせることができること。
もう一つは、facing 0.0 0.0 0.0の様にエンティティを使わずにfacingができること。
注意点として、絶対座標指定では0 0 0の様に整数で書いてしまうと0.5 0.0 0.5と解釈されてしまう仕様があるため、0.0 0.0 0.0の様に小数点以下まで書く必要があります。

ベクトルの成分分解

(重要度☆☆☆)
ある方向のベクトルから(X軸,Y軸,Z軸,水平)のいずれかの成分の分だけ移動します。
ベクトルの半分の距離進み、角度を反転し、またベクトルの半分の距離を進みます。

Y軸成分
execute (元となる位置と方向でのコマンド) positioned ^ ^ ^(ベクトルの長さの半分) rotated ~180 ~ positioned ^ ^ ^(ベクトルの長さの半分) (ベクトルの成分分移動した位置でのコマンド)
水平成分
execute (元となる位置と方向でのコマンド) positioned ^ ^ ^(ベクトルの長さの半分) rotated ~180 ~ facing ^ ^ ^-1 positioned ^ ^ ^(ベクトルの長さの半分) (ベクトルの成分分移動した位置でのコマンド)

X軸成分とY軸成分の場合では、横角度反転を利用する都合上、元の位置と方向のエンティティを実行者とする必要があります。

X軸成分
execute as (元となる位置と向きのエンティティのセレクタ) at @s positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 positioned 0.0 ~ ~ positioned ^ ^ ^0.5 facing 0.0 0.0 0.0 positioned as @s positioned ^ ^ ^-(ベクトルの長さの半分) rotated as @s positioned ^ ^ ^(ベクトルの長さの半分) (ベクトルの成分分移動した位置でのコマンド)
Z軸成分
execute as (元となる位置と向きのエンティティのセレクタ) at @s positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 positioned ~ ~ 0.0 positioned ^ ^ ^0.5 facing 0.0 0.0 0.0 positioned as @s positioned ^ ^ ^-(ベクトルの長さの半分) rotated as @s positioned ^ ^ ^(ベクトルの長さの半分) (ベクトルの成分分移動した位置でのコマンド)

縦角度->横角度 変換

(重要度☆)
実行方向の縦角度に応じて横角度を変化させます。

縦角度(-90~90)から横角度(-90~90)への変換
execute (元となる向きでのコマンド) rotated 0 ~ positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 rotated ~180 ~ positioned ^ ^ ^1 rotated ~-90 ~ positioned ^ ^-1 ^ rotated ~180 ~ positioned ^ ^1 ^ facing 0.0 0.0 0.0 (変換後の角度でのコマンド)

角度の半分化

(重要度☆)
実行方向の縦角度または横角度を半分にします。
以下の例ではx_rotation=0,y_rotation=0の方向(~ ~ ~1 の方向)を基準に半角化しています。
positioned ~ ~ ~-1の部分に、基準方向と逆方向の単位ベクトルを指定することで、任意の方向を基準とすることができます。
注意点として、基準方向と丁度逆になる方向では正しい角度になりません。

縦角度半角化
execute (元となる方向でのコマンド) positioned 0.0 0.0 0.0 positioned ^ ^ ^-1 rotated ~ 0 positioned ^ ^ ^-1 facing 0.0 0.0 0.0 (半角化後の角度でのコマンド)
横角度半角化
execute (元となる方向でのコマンド) positioned 0.0 0.0 0.0 rotated ~ 0 positioned ^ ^ ^-1 positioned ~ ~ ~-1 facing 0.0 0.0 0.0 (半角化後の角度でのコマンド)

三角式なす角判定

(重要度☆☆☆)
ある方向Aと方向Bのなす角が一定の閾値以下であればコマンドを実行する仕組みです。
三角形の性質を利用しているため、三角式と個人的に呼んでいます。
以下は、エンティティAの向きとエンティティBの向きのなす角が60°以下であれば実行する例です。
ある二等辺三角形の二つの等辺の方向がそれぞれエンティティA,Bの向きと等しく、二辺のなす角が60°であれば、その三角形は正三角形であり残りの一辺の長さも等しくなる、という性質を利用しています。

三角式なす角判定
execute at (エンティティAのセレクタ) positioned ^ ^ ^1 rotated as (エンティティBのセレクタ) positioned ^ ^ ^-1 if entity @s[distance=..1] (条件を満たした場合に実行するコマンド)

近似関連

以下のページに解説を任せます。

一応個人的に好きなものを一つ紹介します。

多点実行関連

execute実行分岐

(重要度☆☆☆)
executeサブコマンドのうちas at positioned as rotated as facing entityなどには、セレクタの対象となるエンティティが複数ある場合に、コマンドの実行を分岐させ、以降のコマンドの実行数を増やす効果があります。

on passengersの実行分岐

(重要度☆☆)
on passengersは実行者にライドしているエンティティを実行者に変更するサブコマンドですが、ライドしているエンティティが複数ある場合は、エンティティ毎にexecuteの実行を分岐させる効果があります。
その際に、実行者として呼び出されるエンティティの順番は、NBTのPassengersタグのリスト内での順番と同じです。

多点実行

(重要度☆)
向きの違うエンティティによって実行を分岐させると、実行位置を変えることができます。

以下は、二つのエンティティを使い、線状に8点パーティクルを表示する例です。

8点線実行
# エンティティ召喚
summon minecraft:armor_stand ~ ~ ~ {Rotation:[0f,0f],Tags:["line"]}
summon minecraft:armor_stand ~ ~ ~ {Rotation:[180f,0f],Tags:["line"]}

# 線状に8点パーティクル表示
execute rotated as @e[tag=line] positioned ^ ^ ^2 rotated as @e[tag=line] positioned ^ ^ ^1 rotated as @e[tag=line] positioned ^ ^ ^0.5 run particle minecraft:composter ^ ^ ^ 0 0 0 0 0 force

以下は、二つのエンティティを使い、立方体の頂点状にパーティクルを表示する例です。

立方体頂点実行
# エンティティ召喚
summon minecraft:armor_stand ~ ~ ~ {Rotation:[0f,90f],Tags:["cube"]}
summon minecraft:armor_stand ~ ~ ~ {Rotation:[180f,-90f],Tags:["cube"]}

# 立方体の頂点状にパーティクル表示
execute rotated as @e[tag=cube] positioned ^ ^ ^1 rotated as @e[tag=cube] rotated ~ 0 positioned ^ ^ ^1 rotated as @e[tag=cube] run particle minecraft:composter ^1 ^ ^ 0 0 0 0 0 force

その他にも、様々な図形を表示できるそうです。

store・スコア関連

変更前の実行者に書き込む

(重要度☆☆☆)
@eセレクタは比較的負荷が重く、なるべく使用を減らす必要があります。
@sセレクタであれば負荷が軽く、また違うエンティティを選択してしまう恐れもないために便利であるものの、一度に実行者で居られるエンティティは一体のみです。
しかし、一度store success entity @sで代入先を設定してしまえば、asで実行者を変更した後でも代入対象は変わることはないため、変更前の実行者にコマンドの成否を代入することができます。

scoreboard playersの戻り値

(重要度☆☆)
scoreboard playersコマンドでのset,add,remove,operationなどの演算系のモードでは戻り値(store resultで取れる値)は代入したスコアとなります。
代入先の対象が複数ある場合には、代入した値の合計が戻り値となります。

そのため、以下の様なコマンドで複数のセレクタ対象が持つスコアの合計を得ることができます。

戻り値合計
execute store result score #sum obj run scoreboard players add (対象のセレクタ) obj 0

store代入のタイミング

(重要度☆)
storeによる代入はコマンドが実行された直後に行われます。
executeが実行分岐していてコマンドが複数回実行される場合は、各実行の直後に代入が行われます。
複数回storeを書いた場合は書いた順番に代入が行われます。

セレクタの評価タイミング

(重要度☆)
executeサブコマンドの評価は全てrun以降のコマンドの実行の前に行われます。
そのため、run以降のコマンドやstoreなどによってエンティティの状態やスコアを変更したとしても、executeサブコマンド内でのセレクタの結果が変わることはありません。
しかし、run以降のコマンド内でのセレクタはコマンドの実行の度に評価されます。

未定義動作関連

今後のアップデートで挙動が変更される可能性が無いとは言えないので、一応注意して使ってください。

オーバーフロー

(重要度☆☆☆)
スコアはint型という形式で管理されており、表せる値の範囲の最大値は2147483647、最小値は-2147483647です。
最大値を超えて加算しようとすると最小値+超過した分の値の値となり、最小値を超えて減算しようとすると最大値-超過した分の値となります。
ざっくり、最大値と最小値が繋がっており、数直線がループしていると考えれば良いです。

桁落ち式align

(重要度☆)
浮動小数点数の桁落ちという性質を利用してalignの様なことをする技術です。
通常のalignサブコマンドと比べ、任意の2の乗数の単位でalignができる、実行方向によって動的にalignする軸を変更することができる、などの利点があります。
(2^n)ブロック単位でalignしたい場合には、alignしたい軸方向に2^(52+n)+30000000移動した後-(2^(52+n)+30000000)移動します。
詳しい原理については「浮動小数点数 桁落ち」などで調べてください。
以下はX,Z軸方向に16ブロック単位でalign(チャンクの端にalign)する例です。

X,Z軸に16ブロック単位でalign
execute (元となる位置でのコマンド) positioned ~-8 ~ ~-8 positioned ~72057594067927936 ~ ~72057594067927936 positioned ~-72057594067927936 ~ ~-72057594067927936 (align後の位置のコマンド)

その他

anchored eyes式スニーク状態検知/伏せ・泳ぎ状態検知

(重要度☆☆☆)
anchored eyesによる目線高さへの実行位置の移動は、スニーク状態や伏せ・泳ぎ状態での目線高さの変更も反映されます。
そのため、それを利用してスニーク状態や伏せ・泳ぎ状態を検知することができます。

スニーク状態検知
execute as @a at @s anchored eyes positioned ^ ^ ^ positioned ~ ~-1.27 ~ if entity @s[distance=..0.0001] (スニーク状態時に実行するコマンド)
伏せ・泳ぎ状態検知
execute as @a at @s anchored eyes positioned ^ ^ ^ positioned ~ ~-0.4 ~ if entity @s[distance=..0.0001] (伏せ・泳ぎ状態時に実行するコマンド)
立ち状態検知
execute as @a at @s anchored eyes positioned ^ ^ ^ positioned ~ ~-1.62 ~ if entity @s[distance=..0.0001] (立ち状態時に実行するコマンド)

横角度align

(重要度☆☆)
横角度を東西南北のうち最も近い方角にalignします。

横角度align
execute (元となる方向でのコマンド) positioned 0.0 0.0 0.0 rotated ~45 0 positioned ^ ^ ^-0.5 align xz facing -0.5 0.0 -0.5 rotated ~-45 0 (横角度align後のコマンド)

高さのスコア化

(重要度☆☆)
if blocksを利用すれば、エンティティのNBTのPos[1]を取らなくても高さをスコアに代入することができます。
if blocksの比較元範囲と比較先範囲を同じにすることで必ず成功させつつ、比較範囲の大きさを高さとして取ります。
注意点として、結果は整数値になること、基準となる高さ以下でも正の値になること、ブロック設置範囲外では使えないこと、などがあります。

高さのスコア化
execute (元となる位置でのコマンド) (代入先へのstore result) if blocks ~ (基準となる高さ) ~ ~ ~-1 ~ ~ (基準となる高さ) ~ all

応用テクニック編

基礎技術を組み合わせた応用テクニックの例を紹介します。

スコア連番付与

(関連技術:scoreboard playersの戻り値
複数のエンティティにセレクタの順番で0からの連番のスコアを振ります。

スコア連番付与
scoreboard players set #number obj -1
execute as (対象のセレクタ) store result score @s obj run scoreboard players add #number obj 1

逆順の連番はこうです。

スコア逆連番付与
execute store result score #number obj if entity (対象のセレクタ)
execute as (対象のセレクタ) store result score @s obj run scoreboard players remove #number obj 1

n番目からm番目limit

(関連技術:スコア連番付与
通常のlimit=n引数では、見つかるのが早い順番にn体のエンティティが選択されます。
連番付与を利用すれば、n番目からm番目までに見つかったエンティティといった選択や、最後からn番目からm番目までに見つかったエンティティといった選択ができます。

limit n~m
scoreboard players set #number obj -1
execute as (対象のセレクタ) store result score @s obj run scoreboard players remove #number obj 1

# n番目からm番目に見つかったエンティティに実行
execute as @e[scores={obj=(n..m)}] (エンティティに実行するコマンド)
limit n~m 逆順
execute store result score #number obj if entity (対象のセレクタ)
execute as (対象のセレクタ) store result score @s obj run scoreboard players remove #number obj 1

# 最後からn番目からm番目に見つかったエンティティに実行
execute as @e[scores={obj=(n..m)}] (エンティティに実行するコマンド)

n番目のPassengersエンティティに実行

(関連技術:on passengersの実行分岐の戻り値スコア連番付与
on passengersでスコア連番を付与し、nのスコアを持ったエンティティに実行します。

n番目のPassengersエンティティに実行
scoreboard players set (ライド元のセレクタ) obj 0
execute as (ライド元のセレクタ) on passengers store result score @s obj on vehicle run scoreboard players add @s obj 1
execute as @s[scores={obj=(「n番目」のn)}] (エンティティに実行するコマンド)

2つごとのエンティティに実行

(関連技術:scoreboard playersの戻り値
セレクタで選ばれる順番で奇数/偶数番目のエンティティにだけコマンドを実行します。
-1を掛けることで1と-1が交互に切り替わることを利用しています。

奇数/偶数番目に実行
scoreboard players set #number obj 1
execute as (対象のセレクタ) store result score @s obj run scoreboard players operation #number obj *= #-1 obj

# 奇数番目に実行
execute as @e[scores={obj=-1}] (奇数番目に実行したいコマンド)

# 偶数番目に実行
execute as @e[scores={obj=1}] (偶数番目に実行したいコマンド)

セレクタをランダムにすると、1/2の抽選ができます。

nつごとのエンティティに実行

(関連技術:scoreboard playersの戻り値オーバーフロー
-1を掛けるの代わりにオーバーフローを利用すると2以外の周期でも実行できます。

3つごとに実行
scoreboard players set #number obj 0
execute as (対象のセレクタ) store result score @s obj run scoreboard players add hoge obj 1431655765
execute store success score hoge obj as @e[scores={obj=0..1431655765}] at @s (3つごとに実行するコマンド)
4つごとに実行
scoreboard players set #number obj 0
execute as (対象のセレクタ) store result score @s obj run scoreboard players add hoge obj 1073741824
execute store success score hoge obj as @e[scores={obj=1073741824}] at @s (4つごとに実行するコマンド)

総数/n体のエンティティに実行

(関連技術:scoreboard playersの戻り値セレクタの評価タイミング
対象のエンティティから対象のエンティティの総数/n体選んでコマンドを実行します。
nつごとのエンティティに実行に似ていますが、こちらはセレクタで見つかった順にエンティティの総数/n体が選ばれるという部分が違い、また未定義動作であるオーバーフローが必要ありません。

総数/n体に実行
execute as (対象のセレクタ) store success score @s obj run tag @e[(対象のセレクタの引数),tag=!chosen,limit=(「総数/n」のn)] add chosen
execute as @e[scores={obj=1..}] (総数/n体に実行するコマンド)
tag (対象のセレクタ) remove chosen

視線検知

(関連技術:三角式なす角判定変更前の実行者に書き込む
実行者があるエンティティを見ているかを大雑把に判定することができます。
@s[distance=..0.1]の部分の距離を大きくすると閾値の角度は大きくなり、逆は小さくなります。
あくまで「実行者の向き」と「実行者から対象までのfacingの方向」のなす角が閾値以下かを判定しているだけなので大雑把な判定です。

見る側でコマンド実行する視線検知
execute as (見る側のセレクタ) at @s anchored eyes facing entity (見られる側のセレクタ) eyes anchored feet positioned ^ ^ ^1 rotated as @s positioned ^ ^ ^-1 if entity @s[distance=..0.1] (見ている場合に見る側を実行者として実行するコマンド)

見られている側に対してコマンドを実行したい場合は、storeを利用し以下の様にします。

見られる側でコマンド実行する視線検知
execute as (見られる側のセレクタ) store success score @s obj at @s anchored eyes positioned ^ ^ ^ anchored feet as (見る側のセレクタ) facing entity @s eyes positioned as @s positioned ^ ^ ^1 rotated as @s positioned ^ ^ ^1 if entity @s[distance=..0.1]
execute as @e[scores={obj=1}] (見ている場合に見られる側を実行者として実行するコマンド)

前方判定

(関連技術:三角式なす角判定
エンティティAの位置を通りエンティティAの向きに直交する面に対して、エンティティBの位置がその面の前方にあるかを判定することができます。
なす角が直角の場合は、二等辺三角形ではなく、各辺の長さが3:4:5の直角三角形を利用すると、距離を整数で綺麗に書けます。
これを6面分行うことで回転可能な直方体範囲での判定も可能になります。

前方判定
execute as (面を定義するエンティティのセレクタ) at @s facing entity (位置を判定するエンティティのセレクタ) feet positioned ^ ^ ^3 rotated as @s positioned ^ ^ ^-4 if entity @s[distance=..5] (前方にあった場合に実行するコマンド)

最後に

ご質問やご指摘などあれば、このページのコメント欄や https://twitter.com/komaramune までご連絡ください。

13
4
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
13
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?