scheduleコマンド、実行者変わっちゃうじゃん!!
scheduleコマンドで指定したfunctionはエンティティを対象に実行させることができません.
私はこれに結構悩まされてきました.そこで今回は赤石愛さんのOhMyDatとマクロを用いてエンティティごとに時間を指定してfunctionを実行する方法を紹介します.この方法では複数のfunctionを指定できます.要はOhMyDatでエンティティ毎にアドレスとタイマーを持たせてタイマーが0になったらアドレス先を実行するだけです.はい、本当にこれだけです.
※この記事はMinecraft Command Advent Calendar 2023への参加記事です.
細かい仕様とか何もわからないので、あまり期待しないで下さい.
前提
赤石愛さん製作のデータパックOhMyDatが必要です.
また、以下のようにスコアボードを作成しておいてください.
scoreboard objectives add . dummy
作ってみる
OhMyDatを使えばエンティティごとに色々なデータを持たせることができます.
今回は実行予定のfunctionのアドレスと実行までの時間(tick)を持たせます.
以下が実行予定functionと時間を設定するマクロです.
##schedule(l:アドレス,t:ティック) *このマクロが実行されたエンティティ中心に実行
##使用例 /function peru.lib:schedule {l:"peru.lib:ex",t:600}
function #oh_my_dat:please
$data modify storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list prepend value {l:"$(l)",t:$(t)}
tag @s add peru.lib.schedule
アドレスと時間をリスト(sc_list)に入れていますね.このリストに実行される予定のファンクションたちを入れていくのです.
最後の行のtag付けは常時実行されるエンティティを制限するものです.リスト(sc_list)に実行予定のfunctionが入ってなければこのタグは外されます.
以下は常時実行するfunctionです.
predicateによりタグ(peru.lib.schedule)を持ったエンティティを識別してfor文を実行します.※peru.lib:forのこと
execute as @e if predicate peru.lib:tag/schedule at @s run function peru.lib:for {i:16,l:"peru.lib:hide/sche_for",args:"{}"}
急にfor文なんて言葉を出しましたが、これは指定した回数コマンドを繰り返すマクロです.この1文でファンクションperu.lib:hide/sche_forを16回繰り返します.本題とはそれるのでこちらの説明は割愛します.後でコードは載せますが繰り返すfunction内で引数iを使えるように作っており、iは今何回目のループなのかを示す数字です.
16という数字は実行予定のfunctionをいくつ持てるかを設定する数字です.
function #oh_my_dat:please
$execute store result storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list[$(i)].t int 0.9999999999999999 run data get storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list[$(i)].t
function peru.lib:hide/sche_run with storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list[{t:0}]
ストレージの値も一定の条件下で加減算できます.
0.99...という値をかけることで値が1減ります.2行目ではsc_listのi番目の要素のtを1減らしています.3行目ではtが0の要素を引数としてマクロを実行します.
つまり、tの値を減らしていって0になったら実行予定のfunctionを実行します.
あとは実行して、実行した要素を消します.
1行目と5行目でそれぞれ行っています.
$function $(l)
function #oh_my_dat:please
execute store success score #x . run data get storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list[0].t
execute if score #x . matches 0 run tag @s remove peru.lib.schedule
data remove storage oh_my_dat: _[-4][-4][-4][-4][-4][-4][-4][-4].peru_lib.sc_list[{t:0}]
2~4行目により実行予定のfunctionをそのエンティティが持っていなければタグが外されます.リスト0番目の要素のtのゲットに失敗する、つまりリストに何も入ってなければ #x . の値が0になります.2行目でもう一度OhMyDatを読み込んでいますが、これをする理由は、実行関数内で読み込む個別ストレージが変わる可能性があるからです。つまり、実行させる予定のファンクションの中でもOhMyDatを使ってしまっている可能性があるということです。こうなればちゃんと動かなくなってしまいます。ちなみに最近のアプデ情報ではダミープレーヤーの名前は#で始めると軽くなるらしいです.$は書き換えないと...
使用例
— ぺる (@peruzimoone) December 16, 2023
エンティティごとに時間指定してfunction実行
— ぺる (@peruzimoone) December 16, 2023
[OhMyDat](https://t.co/kgXAa8P5oZ)
を使わせて頂いてます。 pic.twitter.com/O7MrIkvMpL
余談ですが動画のポーションはクリエのプレーヤーも死にます.
(ダメージと回復を29どっちも29lvlにするとできる)
for文を再現
##for(l:functionのリンク,args:引数,i:回数)
##使用例 /function peru.lib:for {i:7,l:"peru.lib:ex",args:"{}"}
scoreboard players set #i . 0
$data modify storage peru.lib: for set value $(args)
data modify storage peru.lib: for append value {i:0}
$function peru.lib:hide/for. {l:"$(l)",i:$(i)}
##for.(l:functionのリンク,i:回数)
$execute if score #i . matches $(i).. run return 0
execute store result storage peru.lib: for.i int 1 run scoreboard players get #i .
$function $(l) with storage peru.lib: for
scoreboard players add #i . 1
$function peru.lib:hide/for. {l:"$(l)",i:$(i)}
引数も読み込めるので便利です.ただ、#iという名前は被りそうなので避けるべきですかね.一時的な値ってわけではないですし.
あとがき
自分の使ってるライブラリからそのまま引っ張ってきたのでフォルダ構造が説明向きではなかったです.私がデータパックを作る時はファイルが多すぎて頭パンクしそうになってるのでフォルダをhideという名で別に分けているのですが皆さんどうしてるんですかね...?それもマクロのお陰でだいぶスッキリしましたけどね.
この方法以外にもっと簡単なやり方があれば教えて頂きたいです.つよつよコマンダーの方々お願いします...間違いなどあれば教えてください.
何はともあれ、初めてのQiitaの記事をこのような場で公開でき嬉しく思います.