見出し画像

新dxdydzとヒットボックス検知についての備忘録的な


Minecraft Preview 1.19.70.21にて、
セレクター引数「dx」「dy」「dz」の仕様が変更されたため、その変更点のまとめとヒットボックス検知への活用の仕方について書きます。

新dxdydzの変更点


※語彙力不足なのでバグ報告の文を直訳していることがあります

  • エンティティがテストされる前に位置がフロアされなくなった

  • 小数点以下の指定に対応した

  • 足先ではなく、そのエンティティのヒットボックスが範囲に入っているかでテストするようになった

  • 0が指定された際、xyz軸それぞれ+方向に1.0の範囲が選択されるようになった(※1)

要するにJEのものと殆ど同じになった。

(※1)語彙力不足のため画像で補足

ブロックの角( 0.0, 0.0, 0.0 )からdx(dy,dz)=0を指定したときの選択される範囲

扱う上での注意点


例として次のコマンドを実行する。

execute positioned 1 -60 5 if entity @a[dx=0] run say

上のコマンドでは、座標の表示が( 1, -60, 5 )であるときに常に出力されるとは限らない。
execute positionedでの整数指定にはブロック中心補正が発生するため、この構文では( 1.5, -60.0, 5.5 )から( 2.5, -59.0, 6.5 )までにプレイヤーがいるときに出力される。(※2)
つまり、正しく指定するには

execute positioned 1.0 -60 5.0 if entity @a[dx=0] run say
 
execute if entity @a[x=1,y=-60,z=5,dx=0] run say

のどちらかのようにしなければならない。
基準座標はdxdydzによる指定範囲の角になることを忘れずに。

(※2)語彙力不足のため画像で補足

※赤い炎の範囲=選択したい範囲

さらにこれだけではなく、新しいdxdydzは「ヒットボックス」を参照することにも気を付けなければならない。
実は「正しく指定するには…」とか言っていた上の2つの文では、y座標が-61になっていても、xzが少し違っていても、ヒットボックスさえ立方体の中に入れば出力されてしまう。
これを防ぐには、r=やrm=を用いて極大円を使った擬似的な立方体を作るか、検知したいエンティティのヒットボックスに合わせて範囲を縮める必要がある。

1×1×1よりも小さい範囲を選択する方法


dxdydzは、0を指定しても1×1×1の範囲が選択されてしまう。負数を指定すると、今度は-(マイナス)方向に範囲が伸びてしまう。
そこで、異なる2点から同時に範囲を選択し、重なった範囲を指定することで1よりも小さい範囲を選択することができる。


下手くそな図

実行座標がヒットボックスに触れていることの検知


上の図のように、2つの範囲が重なった範囲を指定することで1よりも小さい範囲を指定できる。この重なった部分を0.01×0.01×0.01などにするともはや点と見なすことができるため、実行座標となるエンティティ(=エンティティのヒットボックスの底面の中心)がヒットボックスに触れているかどうかもこれで検知できる。

# EntityID=全エンティティに割り当てられた固有スコア
 
... as @e[dx=0] unless score @s EntityID = @e[c=1] EntityID if entity @s[x=~-0.99,y=~-0.99,z=~-0.99,dx=0] ...

実行座標となるエンティティにヒットボックスが触れているエンティティは上のコマンドで実行者として取り出すことができる。
ただし、一度実行者を検知したいエンティティ側に渡す必要があるため1コマンドで全てを完結させることができないこともある。
「実行座標となるエンティティ」というのにも注意が必要で、これは実行座標にヒットボックスが触れているエンティティを取り出すものではない。もし1000ブロック上で管理している飛び道具から1000ブロック下でヒットボックス検知をしたいのであれば、スコア比較の部分を飛び道具を放ったプレイヤーの固有スコアと比較する文に書き換えて、セレクター探索座標(または実行座標)を1000ブロック下に下げて処理を行うなどの工夫が必要となる。
また単に"実行座標"にヒットボックスが重なっているエンティティを出力したいだけであればスコア比較の文はもちろん要らなくなる。

上の構文の解説としては、
まずas @e[dx=0]で、実行座標からxyz軸それぞれ+方向に1ブロック分の長さの辺を持つ立方体を出す。
それに触れているエンティティを実行者として、後に固有スコアが一致しないことを確かめて実行座標となるエンティティ自身(基準)を除外する。
そしてx,y,z軸それぞれ0.99ブロックマイナスの方向に下がり、そこからさらに+方向に1ブロック分辺が伸びた立方体を出す。
その立方体にも重なっていた場合、実行座標から0.01^3の立方体(≒点)にエンティティのヒットボックスが重なっているということなので、「ヒットボックスが実行座標となるエンティティに重なっている」と言うことができる。

ヒットボックス検知技術の応用例


※twitterの宣伝みたいになって申し訳(土下座)

Minecraft Preview 1.19.60にてスニーク時のプレイヤーのヒットボックスが小さくなるように変更がなされたため、1.19.70アップデートによりスニークを1コマンドで検知できるようになった。その構文がこちら。

execute as @a at @s if entity @s[y=~1.4,dx=0] unless entity @s[y=~1.5,dx=0] run say

ただし、泳ぎ中・エリトラ飛行中はスニークしてもヒットボックスがそれ以上縮まないため検知できない。

同時に可能となった泳ぎ・エリトラ飛行の検知。構文はこちら。

# エリトラ飛行検知
execute as @a at @s unless entity @s[y=~1,dx=0] unless block ~ ~ ~ water run say
 
# 泳ぎ検知
execute as @a at @s unless entity @s[y=~1,dx=0] if block ~ ~ ~ water run say

エリトラ飛行と泳ぎはif|unless blockで区別しているため"完全に正確"ではない。

1tick以内に視線先のヒットボックスを検知するコマンド。ただし距離に応じてコマンドを増やす必要があったりと使い勝手はよくない。
構文は長いので画像で…

他にもスライムやマグマキューブのサイズ、子供モブの判定など様々な場所で活用できる。

再帰処理での使用


アドオン機能の1つ「function」では再帰処理を使用することができるため、1tick以内に視線の先を効率的に調べることができる。

ファイル構成:

# ※tick.jsonTick.mcfunctionを常時実行する
# ※Tick.mcfunctionは全プレイヤーからBoot.mcfunctionを実行する
 
functions
 ┣Hitbox
 ┃ ┣Boot.mcfunction
 ┃ ┣Loop.mcfunction
 ┃ ┣Hit.mcfunction
 ┃ ┗Tick.mcfunction
 ┗tick.json

Boot.mcfunctionの中身

# 自身を除外するためのタグ
tag @s add Temporary
 
# 再帰起動
execute positioned ~ ~1.65 ~ run function Hitbox/Loop
 
# リセット
tag @s remove Temporary
 
tag @s remove Temporary_2

Loop.mcfunctionの中身

# Hitboxに重なっているとき実行
execute as @e[dx=0,tag=!Temporary] if entity @s[dx=0,x=~-0.99,y=~-0.99,z=~-0.99] run function Hitbox/Hit
 
# 検知範囲は30mまで、実行座標が空気ブロックでかつHitboxに重なっていなければ再帰
execute if entity @s[r=30,y=~-1.65,tag=!Temporary_2] if block ^ ^ ^ air positioned ^ ^ ^0.1 run function Hitbox/Loop
 
# ブロックの判定にCheckforBlocks使ってないのはサボったから

Hit.mcfunctionの中身

# title表示(Temporary=発動者に付いているタグ)
title @a[tag=Temporary] title @s
 
# Hitboxに重なったのでtag付ける
tag @a[tag=Temporary] add Temporary_2
 
# tag=Temporaryと@sの2つを同時に使うとスコア比較とかもできるよ
# ex) execute if score @s Test = @r[tag=Temporary] Test run say 一致!!!

   Tick.mcfunctionの中身

# メインハンドにリンゴがあるプレイヤーを実行者、実行座標にrun function
execute as @a[hasitem={item=apple,location=slot.weapon.mainhand}] at @s run function Hitbox/Boot
 
# functionを何個にも分けるのは負荷軽減のためだよ


実行しても挙動としては視線先のエンティティをtitle表示するものと変わらない。
しかし遥かに簡単に距離設定ができ、常時実行するコマンド数も減らすことができる。

さいごに


以前のdxdydzを理解していないと難しい内容になってしまったかもしれません。
r=での足先にしか判定のない飛び道具とはこれでおさらばになることを期待してます。
あと何度も言うようですが(?)、これはまだPreview版の内容になります。誤解のないように…
ではまた。


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