この記事について
- Cloud Wolf氏のこの動画を参考にしているのでこれを先に見てもらったほうが理解しやすいと思います。
(というかほぼおんなじ内容なのでこの記事要らないかもしれん...) - この記事は筆者が初めて書いた記事です。思考のままに書きなぐっているので、どこか変なところや読みづらいところがあっても温かい目で見てください。
この記事の内容はMinecraft 1.21.10で検証しています。
- この記事の対象
データパック 高等初級者~中級者
まぁ、考え方だけでも知っていただければ。
はじめに
みなさんは「エンティティどうしやプレイヤーとエンティティをリンクして(紐づけて)なんやかんやしたいな~」と思ったことがあります(強引)。
銃やミサイル、同伴ペット、個人用テレポートポータルなんかで必要そうですよね。
プレイヤーがワールドに一人しかいなかったり、ワールド内にリンクしたいエンティティ対だけが存在するときには実装は簡単でしょうが、それ以外やマルチ対応したいときなんかはそうもいきません。
これを実装する方法としてStorage、Tags、アイテムデータ、赤石愛氏のOh! My Dat!を用いた個別ストレージなどなど色々なやり方があると思いますが、今回はScoreboardを用いた方法を紹介していきます。
-
注意点
この記事で紹介するのは、スコアボードという「エンティティに対して一意に決まる数値」を用いる都合上、
A-B, A-C, A-D というような一個体が複数リンクをもつようなことができません。
シチュエーション設定
エンティティどうしをリンクすることで "何が起こるか・何ができるか" がわかりやすくなるように、簡単なシチュエーションを設定しておきます。
「プレイヤーのためにデコイを召喚してその人の視点の先に追従させたい!」 とでもしておきましょう。
そこ、どういうシチュだそれ?とか言わない!
そしてこんなfunction構成になったとしましょう。
data/
└situ/
└function/
│
├linker/
│ ├link.mcfunction
│ └load.mcfunction
│
├summon_decoy.mcfunction
├kill_decoy.mcfunction
└tp_decoy.mcfunction
結論(唐突)
シチュのfunction
decoy_amst, notlinked というタグをもたせたアマスタを召喚します。
summon armor_stand ~ ~ ~ {Marker:true,Tags:[decoy_amst,notlinked]}
execute at @s run function situ:linker/link
これだけです。短いですね。
データパック内の一か所でしか使わないようなら、別ファイルにせずとも起動元のfunction内にそのまま書き込んでもいいでしょう。
scoreboard players add #linkidoperator linkid 1
scoreboard players operation @n[tag=decoy_amst,tag=notlinked,distance=..1] linkid = #linkidoperator linkid
scoreboard players operation @s linkid = #linkidoperator linkid
execute as @e[tag=decoy_amst,distance=..1] at @s if score @s linkid = @p linkid run tag @s remove notlinked
先にload functionかどこかでlinkid スコアボードオブジェクトを作成しておいてください。
- #linkidoperatorというダミープレイヤーのlinkidスコアをリンクされていないリンク対象と同等のエンティティから探した対象者のlinkidスコアに代入
- 同ダミープレイヤーから実行者(リンクのもう一方)に代入
これでプレイヤーとダミーを紐づけできましたね。
あとは煮るなり焼くなりしていただければいいんですが。どうやって使うかも決めたシチュエーションに沿って見ていきましょう。
リンクされた者たちの使い方
シチュエーションに沿って、「デコイをデコイ召喚者の視点先に追従させる」を見てみましょう。
execute at @a as @e[tag=decoy_amst] if score @s linkid = @p linkid run tp ^ ^ ^2
これを毎tick実行させればいいわけですね。
kill_decoy.mcfunction
#実行者のデコイを消す
execute as @e[tag=decoy_amst,distance=..10] if score @s linkid = @p linkid run kill @s
何が起こってるの? 2nd
私もそう思います。自分で書いといて毎回ちょっと混乱する。
ちゃんと動きましたが、ちょっと複雑なので解説おば
-
execute at @a as @e[tag=decoy_amst]の部分
- このfunction事態の実行位置はすべてのプレイヤー
- 実行者は各デコイ
-> デコイが順に各プレイヤーの位置で以下の検証を行う
-
if score @s linkid= @p linkidの部分
実行者(デコイ)のlinkidが実行位置に一番近い一人のプレイヤーのものと同じかどうか(を各プレイヤーごとの位置で順に確かめる)
---[trueということは]--> デコイが自分と同じlinkidをもつプレイヤー(の位置と視点)をみつけたということ。 -
run tp ^ ^ ^2
実行者(デコイ)を実行位置(プレイヤーの視点)の2ブロック先にテレポートする。
もう一つ使用例として、筆者が現在開発中のデータパック内でどう使われているかをちょっとお見せします。深い解説はしないので雰囲気を感じてください(雑)。
「自分が設置したポータルに飛ぶぞ!」っていう文脈です。
#error send
execute at @e[tag=jmp_son.portal] unless score @n[tag=jmp_son.portal] jmp_son.linkid = @s jmp_son.linkid run tell tp failed *there's no your portal
#jump
execute at @e[tag=jmp_son.portal] if score @n[tag=jmp_son.portal] jmp_son.linkid = @s jmp_son.linkid run function jmp_son:item/sword_of_night/sneak/portal_jump
問題点と回避法、注意点について
「ちょっとまて」「大丈夫かこれ」と思った方もいるかもしれません。
そうですね。お見せした方法では「まだそのlinkidがつかわれていないか」を確認していませんし、#linkidoperatorの値をずっと増やし続けているので どこかでオーバーフロー してしまいます。
おそらく大丈夫だという話
お見せしたやり方だと一番最初の#linkidoperatorのlinkid値は1から始まるのでがオーバーフローして負の値を経由し、さらに1にもどってこない限りリンクされたエンティティ対たちはそれぞれ固有のlinkidをもつわけですが、
Minecraftのスコアボードの値は 符号つき32bit整数 なので、取りうる値は
-2^31 ~ (2^31)-1即ち−2,147,483,648 ~ 2,147,483,647です。
余程のことがない限り「40億回もリンクを実行しても一番最初にリンクしたエンティティ対がリンクされた状態で残っている」なんてことはないでしょう。
回避法
それでもなんかあったら嫌だというときには、単純に「そのlinkidが現在使われているかを確認 -> 使われていたら#linkidoperatorの値をもう一回増やす -> 繰り返し」とすればよいのですが、それだと約40億対しかリンクできません。
なのでlinkidの値を使い切ったらlinkid2の値を1増やして設定する。などすればもっとたくさんできるでしょう。
注意点
上でも書いたように、今回紹介した方法では一個体が複数リンクをもつことができません。
また、今回は扱いませんでしたが、複数体に同じlinkidを持たせることで A-B-C-D のようなリンクを作ることもできます。是非ご活用ください。
おわり
以上、スコアボードを用いたエンティティどうしのリンク(紐づけ)についてでした。
今回はあくまでスコアボードを用いた方法なので、これが最適な方法というわけでもないでしょうし、ましてやこれしか方法がないわけではありません。
仕組みや考え方だけでいえばとても簡単なのでみなさんもここから発展させたり、全然別な方法を編み出したりしてみてください。
一番はじめにもお伝えした通り、これが初めての記事な上、超適当に書いています。(表記ブレがすごいとことかね)
どこかおかしいところがあったり今回の内容のより良い方法などあったら是非コメント等をお願いします。
ではまた。