マイクラでパズドラもどきを作った話

この記事は [Minecraft Command Advent Calendar 2024](https://qiita.com/advent-calendar/2024/mccmd) シリーズ 2 22日目の記事です。

初めまして、狛寝もふたです。

[超魅せるコマンド選手権2024-食べ物](https://mcfascinate.net/%e8%b6%85%e9%ad%85%e3%81%9b%e3%82%8b%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89%e9%81%b8%e6%89%8b%e6%a8%a92024-%e9%a3%9f%e3%81%b9%e7%89%a9/)において、こちらの作品を投稿しました。[^1]

https://x.com/MCFascinatComd/status/1781271271541747873

この記事では、上記の作品に出てくるパズドラもどき(マッチ3ゲーム)に使った技術を紹介します。大慌てで作ったので、必ずしも最善の方法ではないことをご承知おきください。

**Minecraft Java Edition 1.20.5 Pre-Release 1** にて作成しています。

## 1. 盤面の召喚、ドロップのランダム配置

操作検知用のinteractionをドロップの数だけごり押しで並べて盤面にしています。ドロップ自体はitem displayです。

ランダム配置は、loot spawnでitemを召喚し、NBT[^2]をitem displayにコピーすることで行っています。

## 2. プレイヤーの操作 - 「2つ」の判定

interactionが2つクリックされたら、それぞれに重なるdisplayを入れ替える処理です。

```mcfunction:click_check.mcfunction
# 2つクリックされたことの検知

execute at @e[(略),nbt={interaction:{}},limit=1] if entity @e[distance=0.01..,(略),nbt={interaction:{}},limit=1] run function cmn_food:exchange
```

クリックされた記録を持つinteractionが、複数の座標に存在するかを確認することで、2つ(以上)のinteractionがクリックされたことを判定しています。

```mcfunction:exchange.mcfunction
# displayの位置入れ替え

execute as @e[type=minecraft:item_display,(略)] at @s if entity @e[distance=..0.01,type=minecraft:interaction,(略),nbt={interaction:{}}] run tp @s @e[type=minecraft:interaction,(略),nbt={interaction:{}},sort=furthest,limit=1]
(以下略)
```

displayから見て、クリックされたinteractionのうち最も遠いものに向けてtpします。

## 3. 3つそろったことの判定

あるドロップから見て、上下or左右の両方が、自分と同じアイテムを表示していれば3つ揃っている、と判定できます。

```mcfunction:combo_check.mcfunction
#全ドロップで実行する
#両側を見て、自分と同じitemだったらタグsameを付け、same付きが自分+2体いたら処理用タグ(combo)をつける

#左右チェック
tag @s add same
$execute positioned ~$(grid_size) ~ ~ run tag @e[type=minecraft:item_display,(略),distance=..0.01,nbt={item:$(food_item)}] add same
$execute positioned ~-$(grid_size) ~ ~ run tag @e[type=minecraft:item_display,(略),distance=..0.01,nbt={item:$(food_item)}] add same

#数える
execute store result storage cmn_food:candy int_match int 1 if entity @e[type=minecraft:item_display,(略),tag=same]
execute if data storage cmn_food:candy {int_match:3} run tag @e[type=minecraft:item_display,(略),tag=same] add combo

tag @e remove same

#上下も同様
```

即座にkillせずタグの付与にとどめているのは、4つ以上並んでいる場合に対応するためです。

ただ、マクロ入りfunctionをドロップの個数分実行するのは重そうな気がします。もっと良い書き方がありそうです。

## 4. ドロップの落下 - 「真上」の検出

盤面の上の見えない位置にteleport durationを設定したドロップを用意しておき、各マスへtpすることで落下の見た目を作っています。

それぞれのマス(interaction)にtpするドロップは、以下のような条件で探しています。

interactionから見て、

- x,zが同じ
- yが同じか大きい(上にある)
- 最も近い

```mcfunction:search.mcfunction
# このfunctionは下から順に全マス(interaction)から実行する

execute positioned ~ ~100 ~ as @e[(略),distance=..100.01] if predicate cmn_food:same_xz run tag @s add just_above

tp @e[(略),tag=just_above,sort=nearest,limit=1] ~ ~ ~

tag @e remove just_above
```

```json:same_xz.json
{
  "condition": "minecraft:entity_properties",
    "entity": "this",
    "predicate": {
      "distance": {
        "horizontal": {
          "max": 0.01
            }
        }
    }
}
```

x,zはpredicateで水平距離を指定することで判定しています。

:::note warn
このように相対座標を判定するpredicateは、```execute if predicate ...```で適用する必要があります。

セレクターの引数として```@e[predicate=...]```のように書くと、**相対座標の基点は"this"が指すエンティティの座標になります**。このpredicateであれば常に成功します。
:::

y座標に関しては、positionedで上に移動し、移動距離より近いもの、という形で判定しています。

[^1]:投票いただいた方、ありがとうございます！
[^2]:このバージョンでは、アイテムのデータはcomponentではなくNBTで管理されていました。