【JEコマンド講習会】第15回、だるまさんが転んだシステムを考える【マインクラフト】

スポンサーリンク

ようこそぷっこ村へ、ししゃもだよ。

(・∀・)ノ

やってきました、第15回コマンド講習会。今回はアマスタを使った、だるまさんが転んだシステムを考えてみたいと思います。

向き合ってないとアマスタが追いかけてくる

今回は↑のように、向き合ってないとアマスタがジワジワと追いかけてくるシステムを考えてみたいと思います。仕様ですが

  • 最寄りのプレイヤーが正面60°以内にいると追わない
  • (累積)5秒間向き合っているとランダムな位置にワープ
  • 向き合って止まるのは最寄りのプレイヤーの時だけ
  • 追いつかれるとワープさせられる
  • アマスタの見た目はアニマ姉さん

みたいな感じです。アニマ姉さんはきっとヤンデレの視野狭窄なのでしょう。

アニマ姉さんを知らない……、だと!?ここで恐怖のどん底に落ちろ

今回の最大の問題は、向き合っているか?はコマンドが存在しないため、何とか自力で作らないといけないってことです。そろそろコマブロの限界に近付きつつありますね。正直上手く解説する自信がないよ!さぁ、でっきるっかな!?

仰、俯角、水平角について

まずはマイクラの仰俯角、水平角について知らないと向き合ってるかもクソもありませんね。お勉強していきましょう。まず大前提なのですが、

イクラの方位角(水平方向でどちらを向いているか)は、バージョンでかなり違います。この記事を書いてる現在、1.18.2で検証しており、今後大きく変わる可能性もあります。

個人的見解ですが、前に比べて安定した値になってるので、もう変わらないんじゃね?と思ってます。

図のようになっています。

方位角について

方位と値の関連についてです。

  • 南……0.0
  • 北……180.0 or -180.0 ピッタリ北の時は180.0になるっぽい
  • 東……‐90.0
  • 西……90.0

北は-179.999999999……みたいになりますが、どうせ整数部分しか使わないので細かいことはいいです。後は、その間の値をとります。良きように。

仰角、俯角について

上、下向きと値の関連についてです。

  • 真上……‐90.0
  • 真下……90.0

こちらはバージョンによって変わらないです。上が-90って直感的に分かりにくいよなーっていっつも思う。また、今回は考慮しませんー。

一歩踏み込んだスコアボード講座 operation を使いこなそう

今回初めて出てくるscoaboard コマンドの operation について。operationは2つのスコアボードの値同士を計算できます。和差積商、剰余など基本的な計算は網羅してくれてます

基本的な使い方
/scoreboard players operation <操作される名前> <操作されるボード> <操作> <操作する名前> <操作するボード>

操作のところでどんな計算をするか指定します。

例……Count というスコアボードがあるとして
/scoreboard players operation Aさん Count += Bさん Count

上記で、AさんのCountに、BさんのCountの値が足された値が入ります。Aさんが1、Bさんが2だった場合、AさんのCountは3になります。

  • +=……足す
  • -=……引く
  • *=……かける
  • /=……割る、整数で割れるところまで。7÷4=1
  • %=……割った余りを入れる。7÷4=3
  • =……同じ値を入れる
  • <……Aさん>Bさんの場合、値を入れる
  • >……Aさん<Bさんの場合、値を入れる
  • ><……値の入れ替え

operation があると何が幸せなのかっていうと、不確定な結果同士を計算できるってことです。これは便利だぞ!

使うコマンド

gamerule sendCommandFeedback false
scoreboard objectives add Rotation dummy

execute unless entity @e[tag=Ghost] run summon armor_stand ~ ~ ~ {NoGravity:1b,Invisible:1b,Tags:[Ghost],ArmorItems:[{},{},{},{id:"minecraft:stick",Count:1b,tag:{CustomModelData:480}}]}

こちらは事前準備用

execute as @e[tag=Ghost] store result score @s Rotation run data get entity @s Rotation[0]
execute at @e[tag=Ghost] store result score #NP_Rotation Rotation run data get entity @p Rotation[0]
scoreboard players operation #NP_Rotation Rotation -= @e[tag=Ghost] Rotation
execute if score #NP_Rotation Rotation matches ..-1 run scoreboard players add #NP_Rotation Rotation 360
execute unless score #NP_Rotation Rotation matches 150..210 as @e[tag=Ghost] at @s facing entity @p feet run tp ^ ^ ^0.1
execute if score #NP_Rotation Rotation matches 150..210 run scoreboard players add #Spread_Timer Rotation 1
execute if score #Spread_Timer Rotation matches 100.. as @e[tag=Ghost] at @p run spreadplayers ~ ~ 30 40 false @s
execute if score #Spread_Timer Rotation matches 100.. run scoreboard players set #Spread_Timer Rotation 0
execute if score #NP_Rotation Rotation matches 150..210 run tp ~ ~ ~
execute at @e[tag=Ghost] as @p[distance=..0.1] run tp ~ ~100 ~

今回はコマンド自体は少な目です。1つのタワーにまとめて、常時ループさせることにします。

解説するまでもなさそうだけど一応。紫のリピートにして、あとはチェーンです。もちろん動力の有無はお好みで。

1つ目のくくり、事前準備

gamerule sendCommandFeedback false
scoreboard objectives add Rotation dummy

execute unless entity @e[tag=Ghost] run summon armor_stand ~ ~ ~ {NoGravity:1b,Invisible:1b,Tags:[Ghost],ArmorItems:[{},{},{},{id:"minecraft:stick",Count:1b,tag:{CustomModelData:480}}]}

今回はスコアボード1個だけ使います。

  • 1行目、コマンド実行ログがチャット欄に流れないようにします。
  • 2行目、ダミーのスコアボードを作ります。1個で足ります。
  • 3行目、アマスタの召喚ですね。頭に棒を付けてますが、これはししゃものリソパにあるアニマ姉さんに見立てるためです。

2つ目のくくり、メインループ

execute as @e[tag=Ghost] store result score @s Rotation run data get entity @s Rotation[0]
execute at @e[tag=Ghost] store result score #NP_Rotation Rotation run data get entity @p Rotation[0]
scoreboard players operation #NP_Rotation Rotation -= @e[tag=Ghost] Rotation
execute if score #NP_Rotation Rotation matches ..-1 run scoreboard players add #NP_Rotation Rotation 360
execute unless score #NP_Rotation Rotation matches 150..210 as @e[tag=Ghost] at @s facing entity @p feet run tp ^ ^ ^0.1
execute if score #NP_Rotation Rotation matches 150..210 run scoreboard players add #Spread_Timer Rotation 1
execute if score #Spread_Timer Rotation matches 100.. as @e[tag=Ghost] at @p run spreadplayers ~ ~ 30 40 false @s
execute if score #Spread_Timer Rotation matches 100.. run scoreboard players set #Spread_Timer Rotation 0
execute if score #NP_Rotation Rotation matches 150..210 as @e[tag=Ghost] at @s facing entity @p feet run tp ~ ~ ~
execute at @e[tag=Ghost] as @p[distance=..0.1] run tp ~ ~100 ~

では、メインのループ解説していきます。向き合っているか?の値を求めるのが1〜4行目で、値をもとに判定、処理を行うのが5〜9行目となってます。

  • 1行目、まずアニマ姉さんの向きを取得します。向きはNBTのRotationに配列で格納されており、[水平角,仰俯角]となってます。水平角を取得したい場合は Rotation[0] とします。
  • 2行目、こちらは最寄りのプレイヤーの水平角を取得してます。前にやりましたが、スコアボードはエンティティが存在していなくてもよいです。エンティティとして存在しない場合は#を頭につけると良いので(きまりではない)、#NP_Rotation としています。
  • 3行目、出ました、operation。向き合っているか?の計算をします。最寄りのプレイヤーの向きからアニマ姉さんの向きを引きます。
  • 4行目、3行目の結果が負の場合、360を足します。理由は後述。この結果180前後であれば、大体向き合ってます。
  • 5行目、向き合っている状態の定義を左右30度ずつ、計60度とし、計算結果が150〜210までの間でない、つまり向き合っていない場合、最寄りのプレイヤーに向かってtpします。
  • 6行目、ここ以降向き合っている場合です。#Spread_Timer の Rotation に1加算します。ランダムワープ用のカウンターとして Rotation を使いまわします。
  • 7行目、#Spread_Timer の Rotation が100以上になったらランダムワープさせます。spreadplayers は任意の位置を中心に、任意の範囲内のランダムな位置にワープさせるコマンドです。
  • 8行目、#Spread_Timer の Rotation が100以上になったら 0 に戻します。
  • 9行目、向き合ってる場合、その場で最寄りのプレイヤーの方向に向きを変えます。こうしておかないと、向き合った状態でプレイヤーが向きを変えずにアニマ姉さんの背後に回り込んでも、向いている方向自体は変わらないので、動かなくなります。
  • 10行目、プレイヤーの0.1マス以内に来ると、プレイヤーをtpさせます。テスト時、いちいち死ぬ位置に飛ばされるとしんどいので100マス上に飛ばしてます。奈落に突き落としたほうが楽しいよね!
ややこしい向きと値の関係について

メインループの4行目で、負の場合360を足してるのは何でか?答えから言うと、マイクラのコマンドには or がないからです。ないよね?

今回の計算結果で、値のとりうる範囲なんですが、‐360~360までになります。この範囲のうち、今回の定義だと、‐210~‐150、150~210が向き合っている範囲です。このどちらかであればいいのですが、さっきも言った通り or がないので、いちいち2通りの判定をしないといけません。

後の判定を減らすために、値をそろえてるんですねー。一見必要なさそうな処理ですが、ちゃんと貢献してくれてます。

まとめ

ちょっとした数学(算数か?)ができれば大丈夫かなと思います。まぁ一番のネックはそこなんだけども……。もし例えば、近づいてくるときに歩くようなポーズをとらせたい時は、スコアボードをもう一個用意してタイマーにするといいですね。

プレディケートには見ているかって条件があるので、データパックで上手く使うことでより簡単に、かつマルチでも問題なく作ることができます。(1.19ではなくなったっぽい?)

てな感じで、今回はここまでお疲れ様でしたー。お帰りの際はお気を付けて~(・∀・)ノシ

コメント