はじめに
こんにちは、qiitaのアドベントカレンダーにマイクラコマンドもあると知り参加してみました。この記事を読むひとがコマンドすごい人か、初めての人かわからないですが、初めての人にも分かりやすくを目標にして書いてみようと思います。とはいえgiveコマンドの意味から...みたいなのは無理なので分からない人はしっかり調べながら見てもらえばわかりやすくなると思われます。今回は自分が過去に作った動物が襲ってくる世界の作り方、マルチ対応の作り方を書きます。一貫性がないですが初心者向けと考えたときに自分が困っていたことを書くのがいいのではと考えたのでマルチ対応も追加しました。しかし、完全自己流で他の人がどうやってるのか知らないので負荷に問題があったり、バグが発生するなどあるかもしれないので参考程度に見てください。よろしくお願いします・ω・
データパックの作り方
データパックを作る手順は
- どんなものにするか考える。
- 実装方法を考える。
- 書いてみる。
- デバッグ
- しばらく遊んでバグを探す。
みたいな感じだと思います。デバッグはこまめに入れないと後で大変かもしれないです。
ここでは実装方法のみ書いていきます。
実装方法を考える
動物がプレイヤーを追いかけるようにするにはどうすればよいでしょうか。
本来ならばプレイヤーを追いかけるようにアルゴリズムを組んだりする必要があるのですがマイクラの仕様により動物にゾンビを乗せるとプレイヤーを追いかけるようになります。ゾンビはお昼だと燃えてしまうので頭に装備をかぶせてあげましょう。頭に装備できればなんでもよいです。ボタンでもいいです。またゾンビは透明にするので頭に装備するテクスチャは消さなくてはいけません。消しても問題ないアイテムを装備させましょう。
##シャープの部分はentity_typesで検索
execute as @e[type=#animal:animals] at @s unless entity @s[tag=added] run tag @s add temp
execute as @e[tag=temp] at @s run summon zombie ~ ~ ~ {Attributes:[{Name:generic.max_health,Base:100}],Silent:1b,Tags:["inv"],ArmorItems:[{},{},{},{id:"minecraft:music_disc_11",count:1}]}
execute as @e[tag=zombie] run effect give @s invisible infinite
ride @e[tag=zombie,limit=1] mount @e[tag=temp,limit=1]
tag @e[tag=temp] add added
tag @e[tag=temp] remove temp
モンスターと一緒に動物もわかせる
これだけでは動物が襲ってくることがあまりないのでもう少し動物を沸かせましょう。モンスターがわいたときに動物も一緒にわかせます。モンスターに対して動物を召喚するコマンドを実行します。動物を召喚し終わったらsummonedタグをつけましょう。
execute as @e if entity @s[type=#animal:monsters,tag=!summoned,tag=!inv] at @s if score @s aaaaa.pos1_copy matches -10..10 if entity @a[distance=..100] run function animal:summon/summon
それだけでも大体完成ですが、細かいところも考えます。
上のゾンビに攻撃が当たったら
上のゾンビを無敵にしていないのはこの対策のためです。ゾンビに攻撃が吸われると永遠に動物が倒せなくなってしまいます。ゾンビのhpが減ったら始めのhpから減算して受けたダメージを計算しましょう。ダメージが高い場合はそのまま死んでしまうかもしれないのでゾンビの体力は100くらいとっておきます。ゾンビが攻撃を受けた判定はadvancementsでとっています。最後のdamageのファイルで下のモブからダメージを減算しています。
execute as @e[tag=inv] at @s store result score @s aaaaa.zombie_health run data get entity @s Health
execute as @e[tag=inv] at @s if score @s aaaaa.zombie_health matches ..99 if entity @a[tag=hurt_inv] run function animal:summon/inv
↓
scoreboard players operation $100 aaaaa.damage -= @s aaaaa.zombie_health
scoreboard players operation @s aaaaa.damage = $100 aaaaa.damage
scoreboard players set $100 aaaaa.damage 100
data modify entity @s Health set value 100
execute as @e[tag=inv] at @s if score @s aaaaa.zombie_health matches ..99 run data modify entity @s Health set value 100
execute as @e[tag=inv] at @s if score @s aaaaa.damage matches 1.. run function animal:summon/damage
モブがいっぱいで重くなる
夜になるとモンスターに加えて動物とその上にゾンビと、敵が大量に沸いて重くなります。遠くのモンスターや洞窟のモンスターには動物を沸かせないようにしましょう。モンスターと一緒に動物を沸かせる、のところのコマンドでpos1_copyが-10..10のときだけ沸くようになっています。大体ですがこれで洞窟に動物はあまり沸きません。
#洞窟で出ないように
execute as @e[type=#animal:monsters] at @s if entity @a[distance=..100] store result score @s aaaaa.pos1 run data get entity @s Pos[1] 1
execute as @a at @s store result score @s aaaaa.pos1 run data get entity @s Pos[1] 1
execute as @e[type=#animal:monsters] at @s if entity @a[distance=..100] run scoreboard players operation @s aaaaa.pos1_copy = @s aaaaa.pos1
execute as @e[type=#animal:monsters] at @s if entity @a[distance=..100] run scoreboard players operation @s aaaaa.pos1_copy -= @a[sort=nearest,limit=1] aaaaa.pos1
遠くのモブも消そう
遠くにいるモブも消してしまいましょう。動物が50ブロック以上離れている場合は近くのモンスターにrespawnタグをつけて動物は消してしまいます。15マス以内に入ったときリスポーンさせましょう。近くのモンスターにという条件は誤作動を起こす可能性もありますがそこまで厳密にやらなくても良いと思うのでこのままいきましょう。
execute as @e[tag=animal] at @s unless entity @a[distance=..50] run scoreboard players add @s aaaaa.afk 1
execute as @e[scores={aaaaa.afk=1..}] at @s if entity @a[distance=..15] run scoreboard players set @s aaaaa.afk 0
execute as @e[scores={aaaaa.afk=600..}] at @s run function animal:summon/afk
出来上がり!
他にもいろいろ改良を加える必要がありますが、個性的な動物たちを追加して、出来上がりです。是非自分だけの怖い動物を作ってみてください!!↓下のデータパックものぞいてみてください
https://minecraft-mcworld.com/38319/
マルチ対応のやりかた
マルチ対応とはなんでしょうか。例えば銃を作るとして、マーカーを再起関数で前に飛ばします、普通に前に飛ばす分にはいいのですが問題はダメージを与える部分にあります。マーカーの半径0.5ブロックくらいのエンティティにダメージを与えるとなるとマーカーを出した瞬間に実行者に当たってしまいます。では出してから数tickまでは当たり判定をつけないようにする、となると近距離の敵に対して当たらなくなってしまいます。こういうマルチ対応が必要だなってなったときにやっているのがプレイヤーごとに個別の番号をもたせることです。今回はスコアボードを用いてやってみます。(UUIDをとってストレージを比較することでも可能。)
まずは番号を持たせる。
特に難しいことはないです。tagを持っていない人に番号を振ります。番号を振ったときに次の人の番号は+1になるようにして被らないようにしましょう。
tag @s add Kei.added
scoreboard players add $max Kei.number 1
scoreboard players operation @s Kei.number = $max Kei.number
当たった時の処理
アマスタを出したときにプレイヤーと同じ数字をアマスタに振ります。当たり判定をするときにアマスタと、近くにいるプレイヤーの番号をチェックします。番号が違ったらダメージを与える感じです。
当たったのを分かりやすくするために、アマスタと番号が同じの人に音をならせます。
#出すときにスコアをつける
scoreboard players operation @e[tag=rifle,sort=nearest,limit=1,tag=temp] weapon_number = @s Kei.number
#同じか確認
execute if entity @s[type=player] unless score @s hit_player = @s Kei.number run function rifle:hit_player
#音を鳴らす
execute as @a if score @s Kei.number = @e[tag=hit_player,limit=1] hit_player at @s run playsound entity.arrow.hit_player master @s ~ ~ ~ 1 1
もう少し複雑なことも
番号をつけるのは結構便利でもっと複雑なこともできます。例えばRPGを作る際に、モブを倒した処理をするときです。経験値とお金をプレイヤーに直接与えたい、というときにマクロとストレージを組み合わせることで可能になります。
おまけ:回転体を作ってみた
数3の体積を解いていてふとマイクラで回転体を作ってみたくなったのでやってみました。
数3は苦手でして難しい図形はできませんでした。ごめんなさい。
簡単な問題を引っ張ってきました。
y=-x^2+4x....の頂点を変えまして=-x^2+6xとy=xで囲まれた図形です。
今回はy軸の周りに回転させます。
こんな感じの図形になるようです。
$execute as @e[tag=0] at @s run tp @s $(x) $(y) 29
execute as @e[tag=0] at @s run summon marker ~ ~ ~ {NoGravity:1b,Tags:["func"]}
execute as @e[tag=0] at @s if score $0 x matches 0..6 run tag @e[tag=func,limit=1,sort=nearest] add in
scoreboard players add $0 x 1
execute if score $check x matches 1 if score $0 x matches ..10 run function number:func/particle_ with storage kei
execute if score $check x matches 2 if score $0 x matches ..10 run function number:func/x2 with storage kei
別のファイルで式の演算を行って、1行目のx,yに入るような感じです。$マークはマクロというものです。
これを実行するとこんな感じになります。
大体同じですね。次はこれを回転させなければならないのですがこれがよく分からなかったです。
どうすれば回る?
回転のさせ方がよく分からなかったので少し箇条書きで上げてみました。
- 回転させる点とx軸の距離をとって軌跡みたいな感じにする。
- x軸のマーカーを回して視線先にtpさせる。
- 円運動をまねして作ってみる。
2つ目が簡単かと思ったのですがx軸と関数との距離をそれぞれの点ずつ出さなければいけないので大変です。そこで円運動について調べてみました。
円運動は中心方向に加速度(向きが変わるだけ)があって接線方向に速度があるようです。
これを再現すれば円運動ができるのではないか、ということでやってみました。
関数のmarkerをx軸の方向に向かせて、接線方向にtpします。
execute as @e[type=marker,tag=func] at @s facing entity @e[tag=1,limit=1,sort=nearest] feet run tp ^ ^ ^
execute as @e[tag=func] at @s run tp @s ^0.1 ^ ^
すると、こんな感じになってしまいました。始まりが木のブロックでmarker(画像ではアマスタ)が外側にずれてしまっています。実際の円運動では運動すると同時に加速度が決定しているのでこのようなことは起きないはずですが、このコマンドでは一度加速度を決めた後に0.1ブロック動いてしまうので誤差が生じるのだと思われます。これを改善するには移動する量を小さくするしかないです。やってみると0.001にするとほぼ誤差がなくなりました。(数周試した限りでは確認できなかった)ということで半径6マスを回ってもらうには30分くらいかかりますので待ちましょう^^
30分後出来上がったのがこちらになります。
ふつくしい....苦労したかいがありましたね。ここまでやって何をするでもないというのも悲しいので最後に体積を求めておきます。と思っていたのですが早々に消してしまいまして、また30分待つのは気が滅入るので文明の利器に頼りましょう。(何のために作ったのか。)
上記とのことです。
約654だそうです。一マス一ブロックになっているので654ブロック入る?ということですかね。
204ブロックくらいですね。
これくらいの量のようです。
これの三分の一くらいのようです。
入りそうじゃないでしょうか。うん入りますね。ということで大体近似ということにして今回は終わりたいと思います。ありがとうございました。('ω')