0コメント

【Minecraft】統合版とJava版でコマンドを使う上でのメリット・デメリット

サーバーを開発するうえで、最近統合版のサーバーをJava版でも動作するように変換していくという作業を実際に行っている中で、前々から少し気にしてはいたがやはりJava版だからこそ実装できるという物があったためそれについて考えてみる。

今回は「時計をゼロ地点(0 10 0)近辺で投げると自宅(-70 250 -110)にテレポートし、自宅で時計を投げるとゼロ地点にテレポートする」というコマンドについてexecuteコマンドから考えてみる。

早速だが、実行例を挙げる。(※サーバーで使用したコマンドのため上記条件以外の引数があることにはご了承願いたい)
※Java版も旧バージョンでは現行の統合版と同じ仕様のため旧バージョンを利用している場合は注意

統合版(bedrock edition)
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ replaceitem entity @s slot.weapon.mainhand 0 clock
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ execute @s[x=0,y=5,z=0,r=20] ~ ~ ~ tp @s -70 250 -110
(conditional)kill @e[name="時計"]
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ execute @s[x=0,y=5,z=0,rm=21] ~ ~ ~ tp @s 0 10 0
kill @e[name="時計"]

Java版(Java edition)
execute as @a[gamemode=creativeat @s if entity @e[type=item,nbt={Item:{id:"minecraft:clock"}}
,distance=..5
run item replace entity @s weapon.mainhand with minecraft:clock
execute as @a[gamemode=creative,x=0,y=5,z=0,distance=..20at @s if entity @e[type=item,nbt={Item:{id:"minecraft:clock"}}] run tag @s add tpFromLobby
execute as @a[tag=tpFromLobbyrun tp @s -70 250 -110
execute as @a[gamemode=creative,x=0,y=5,z=0,distance=21..,tag=!tpFromLobbyat @s if entity @e[type=item,nbt={Item:{id:"minecraft:clock"}}] run tp @s 0 10 0
kill @e[type=item,nbt={Item:{id:"minecraft:clock"}}]
tag @a remove tpFromLobby

とりあえず、統合版のパターンはfunctionではなくコマンドブロックで実装しているため、conditional部分を省く形に書き換えてみる。
統合版 - 比較修正版
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ replaceitem entity @s slot.weapon.mainhand 0 clock
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ execute @s[x=0,y=5,z=0,r=20] ~ ~ ~ tag @s add tpFromLobby
execute @e[tag=tpFromLobby] ~ ~ ~ tp @s -70 250 -110
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ execute @s[x=0,y=5,z=0,rm=21,tag=!tpFromLobby] ~ ~ ~ tp @s 0 10 0
kill @e[name="時計"]
tag @a remove tpFromLobby
書き直した。検証してないけどおそらくこれで動く。
これを見てもらえれば一目瞭然だろう。

まずNBT指定。統合版ではnbt引数が利用できないので、name引数で無理やり時計を指定しているがもちろん言語が違うとこれは動作しない。
Java版ではアイテムIDまで指定できているのでより正確に指定が出来ている。

また、統合版では「必ず実行者と実行者からの相対座標の指定」が必要になってくる。一方で、Java版であればこれらはすべてサブコマンドの一つとして実装されたため、「無くても動作する」という具合になった。
ただその代わり
「統合版ではexecuteコマンドには実行者と相対座標の入力を必須(プラスでdetectのしても可能)」
という仕様が
「Java版ではexecuteコマンドにはサブコマンドを用意する代わり、全部自分で指定しろよ!」
という仕組みに変わったため、ひょっとしたら初心者にはとっつきにくいものになってしまった可能性はある。

初心者にありがちなミスが、execute @a ~ ~ ~ summon lightning_boltというコマンドをJava版で書くときexecute as @a run summon lightning_boltと書きがちなことだ。
この場合lightning_boltは座標無指定という事で0 0 0に召喚されてしまう。
実行者をasで指定しても、実行点も自身で指定しないといけないためこれでは統合版と同じ指定はしないということだ。(そのため場所指定にat @sを付け加える必要がある)

ただし、Java版でもexecuteコマンドを統合版と同じ動作をさせたいというのであれば
サブコマンドで「execute as 実行者 at @s positioned 相対座標 run コマンド本文」と入力すれば互換性を持たせることは出来る。が、もちろんサブコマンドで出来る幅が増えたため確実に別途新規に記述したほうがもっと短縮できる。

例:
execute @e[name="時計"] ~ ~ ~ execute @p[m=c] ~ ~ ~ execute @s[x=0,y=5,z=0,r=20] ~ ~ ~ tag @s add tpFromLobby
直変換後(統合版→Java版):
execute as @e[name="時計"] at @s as @p[gamemode=creative] at @s as @s[x=0,y=5,z=0,distance=..20] run tag @s add tpFromLobby
人力変換後:
execute as @e[name="時計"] at @s as @p if entity @s[gamemode=creative,x=0,y=5,z=0,distance=..20] run tag @s add tpFromLobby
もしくは
execute positioned 0 5 0 if entity @p[gamemode=creative,distance=..20] if entity @e[name="時計"] run tag @s add tpFromLobby
等。例が悪かったので短縮とまではいかなかったが記述方法は割と多く、それでいて詳細に調整する事が出来る。
Java版にデメリットなんてなさそうだが、こうしたサブコマンド化したことで初心者がとっつきにくくなった可能性があるのと、記述がごっちゃになってしまう可能性というのもある。

execute as @e[type=!player,limit=1,sort=random] if entity @e[type=zombie] at @e[type=zombie] run summon lightning_bolt ~ ~ ~
execute if entity @e[type=zombie] as @e[type=!player,limit=1,sort=random] at @e[type=zombie] run summon lightning_bolt ~ ~ ~
こういうif/unless系なら順序逆でも同じ動作をするし、記述になれてないと逆に見にくくなる可能性も。
ちなみに同じサブコマンドを複数回使う事も出来る.

それに、サブコマンドが追加されてとても見やすくなったとて、プログラミング言語に勝る記述のしやすさは手に入れられていない
Hello worldと5回出力するなら、プログラミング言語であればwhile文を使えば三行か二行程度で済むが、functionなら結局五回同じ文章を記述しなければならないし(※次回実行時に引き継ぐ場合はscoreboardを変数として使えば3行程度で実装出来ないことは無い)
個人的には中途半端な感じが否めないと思っている

それでもstoreサブコマンドやif/unlessで割と使いやすくなったのは事実だけれども。


あと、executeコマンドから関係ない部分に飛ぶとしたら,Java版ではscoreboardコマンドでスコアボードを作成する際に何かしらに値を連動させる(例:経験値レベル、敵を攻撃した回数)事が出来るようにはなったが何故か統合版で出来ていた乱数生成がJava版では利用できない。(scoreboard players random)
ちなみに私はエンティティに別々のスコアを所持させsort=randomで一体指定してそのエンティティが所持するスコアを乱数として代入する方法を使って対策はしたものの、実装が面倒臭い。

ちなみにscoreboardはint型のアドレス領域が確保されてるがオーバーフローして符号が逆転する問題はどちらにしても現時点では改善されていない。まあそもそも、十億単位の巨大な値を使う機会がナイとは思うが。


原点回帰すると、executeコマンド以外での差ってアップデートを積み重ねてPE版の時代と比較すれば統合版も割とまともな形になってきているため一般人からすればあんまし関係ない気がするが、コマンド的には割と仕様が異なる。

例えば、NBTをJava版では扱う事が出来る、統合版では入力が緩い(gamemodeコマンドにおいて、Java版ではcreativeと入力する必要があるところを統合版では1,c,creativeと入力する事が出来る、functionで名前空間指定が不要な事など)
案外仕様の異なる部分は多い。特にexecuteコマンドの仕様変更がコマンドで開発している人たちによってはかなり痛恨だと思う


とにかく、まとめてみるとこんな感じ(※個人の偏見も含む)
 Java版統合版
executeコマンドの使いやすさ
executeコマンドの汎用性
乱数生成
予測変換
条件指定実行(if)
NBTの利用(引数、値取得、変動)


個人的な見解としてはこんな感じ。
というかマジでJava版のサブコマンドについては幅が広すぎて難しい。まだ使いこなせてない部分も多いし

まあ詳しくはこちらを参照してもらって。

Java版ではNBTを扱えるといったが、例えば
execute as @a store result score @s healthScore run data get entity @s Health 1
で自身のhealthScoreスコアボードに自身の体力を代入したり
execute as @e[type=zombie] store result entity @s Attributes[0].Base double run scoreboard players get @s attackDamage
でゾンビは自身のattackDamage値を自身の攻撃力値に代入したり

マジでやりたい放題。

ただしプレイヤーのNBTは取得することは出来るが書き換える事が出来ないため、注意。(※1.16現時点)

以上


ps.読者メッセージが届いていました。ありがとうございます。
恐らくこの記事の事だと思うんですけど、コメントで指摘してもらえればありがたいです。

32767の中から乱数を生成し、10000以内の乱数であれば
乱数 = $RANDOM % 10000
の場合、30000から32767の部分が余分に乱数の確率に入ってしまうので0~2767が出る確率が若干上がると思います。

こっちの記事の乱数生成の場合は、
10000であればアーマースタンドを10000体召喚する必要があるので重くなるとは思いますが、同じ値のアーマースタンドが二体存在するとかそういう訳じゃない限りは全て確率は均等だと思います。

確率計算は得意ではないので何かしらミスがあれば申し訳ない
0

この記事へのコメント