placeコマンドを使ってみよう

# はじめに
この記事は**Minecraft Command Advent Calendar 2023**21日目の記事です。
私は普段あまりコマンドを使ってなくて、どちらかというとカスタムディメンジョンばかり弄っています。
そんな私でも書けることはないか？
そういえば私は頻繁に使うけどコマンド勢placeコマンドあまり使わないじゃん？
って思ってしたためてみました。

今回はplaceコマンドの基本的な使い方と「こんな使い方できるよ」ってのを紹介していきたいと思います。


…序にfeatureの作り方もおまけで書いてるからそっちも見てってくれよな！(｀・ω・´)

# placeコマンドとは
1.19で、/placefeatureに置き換わる形で実装されたコマンドです。
/placefeatureはその名の通り、木や鉱石、山積みのブロック、アメジストジオードといったfeatureのみを呼び出します。
/placeはこれを更に拡張し、ストラクチャーやストラクチャーのパーツとなる、ストラクチャーテンプレート（ストラクチャーブロックで保存したり呼び出したりするアレ）を呼び出したり、ジグソーを呼び出し、続けて処理をする、といったことが可能となっています。

尚、このコマンドはカスタムディメンジョンの要素をコマンド化したようなものです。それらについては、今後別に記事をあげるつもりです(qiitaではないかもしれませんが)。


# 使い方
## 構文
placeするものに応じて4種類の構文があります。
### featureをplace
`/place feature <feature> [<pos>]`
#### 引数の意味
feature : 呼び出したいfeatureを`worldgen/configured_feature`から指定します。
pos : 省略可能で、指定した座標に呼び出します。省略した場合は、今いる場所に呼び出します。
### ストラクチャーをplace
`/place structure <structure> [<pos>]`
#### 引数の意味
structure : 呼び出したいストラクチャーを`worldgen/strucure`から指定します。
pos : 省略可能で、指定した座標に呼び出します。省略した場合は、今いる場所に呼び出します。
### ストラクチャーのパーツをplace
`/place template <template> [<pos>] [<rotation>] [<mirror>] [<integrity>] [<seed>]`
#### 引数の意味
template : 呼び出したいストラクチャーテンプレートを`structures`から指定します。
pos : 省略可能で、指定した座標に呼び出します。省略した場合は、今いる場所に呼び出します。
rotation, mirror, integrity : ストラクチャーブロックのように、呼び出すテンプレートの向きを回転させたり、反転させたり、所々欠損させて呼び出すことができます。
seed : 知らん。
### ジグソーをplaceし、連鎖的にストラクチャーを生成する
`/place jigsaw <pool> <target> <max_depth> [<position>]`
#### 引数の意味
pool : ジグソーが参照するテンプレートプールを`worldgen/template_pool`から指定します。この「テンプレートプール」というのは、呼び出したいテンプレートの候補リストのようなものです。
target : 接続先のジグソーの名前を指定します。
max_depth : ジグソーブロックはテンプレートプールからテンプレートを呼び出し、それを連鎖的に行います。その連鎖の最大数を指定します。
position : 呼び出したい座標を指定します。指定しなかった場合は、今いる場所に呼び出します。

## 基本的な使い方
### place feature
鉱石や木やアメジストジオード等のfeatureを呼び出します。木とかを生成させることができるので、マップとか作るときにちょっとだけ楽ができるのではないでしょうか。

### place structure
これは廃坑などの構造物を呼び出します。

### place jigsaw
place structureと若干似ていますが、こちらはジグソーブロック単位で呼び出せます。これにより、構造物内でどこから生成するかを指定できます(place structureは必ず角から生成されます)。

# こんな使い方もできるよ　~ 応用テクニック
ここまで、「対応したものを呼び出す」という、基本的な使い方を話しました。
featureやストラクチャーを呼び出せるってだけでも大きいのですが、
折角だから「コマンド技」に応用してみませんか？

というわけで、ちょっとした応用テクを紹介していきます。
ただ、万能ではありません。「こういうこともできるよ」って感じで見てください。

## place feature
さて、このfeature。自作したものを呼び出すこともできます。
featureの作り方についてはこちらをどうぞ

https://qiita.com/noir_kokumin/items/e9cadb13b388dd7cc044

### setblockでは置けないブロックを置く
このplace feature、ものによってはブロック更新を起こさずにブロックを置くことができます。
それが、type:"minecraft:replace_single_block"です。
例えば、道ブロックをブロックの真下に置くとします。
通常はsetblockコマンドでも、即座に土に戻ってしまいますが、このfeatureを使えば、土の道のままにすることができます。

こういう書き方になります。
```{
    "type": "minecraft:replace_single_block",
    "config": {
        "targets": [
            {
                "target": {
                    "predicate_type": "minecraft:block_match",
                    "block": "minecraft:dirt"
                },
                "state": {
                    "Name": "minecraft:dirt_path"
                }
            }
        ]
    }
}
```
### ドアをパーティクルなしで開閉する
コマンドでドアを開けたい。
そんな感じのことを思うことがあるかもしれません。
しかしここでsetblockやfillを使うとどうしてもパーティクルが発生してしまいます。

place featureを使えばこれを回避できます。
`facing=north,hinge=right,powered=false`を例にするとこういうものを用意すればいいのです。
```{
    "type": "minecraft:block_column",
    "config": {
        "direction": "up",
        "allowed_placement": { //柱を構成するブロックを置く条件
            "type": "minecraft:matching_blocks",
            "blocks": "minecraft:oak_door"
        },
        "prioritize_tip": false,
        "layers": [
            {
                "height": 1,
                "provider": {
                    "type": "minecraft:simple_state_provider",
                    "state": {
                        "Name": "minecraft:oak_door",
                        "Properties": {
                            "open": "true",
                            "powered": "false",
                            "hinge": "right",
                            "half": "lower",
                            "facing": "north"
                        }
                    }
                }
            },
            {
                "height": 1,
                "provider": {
                    "type": "minecraft:simple_state_provider",
                    "state": {
                        "Name": "minecraft:oak_door",
                        "Properties": {
                            "facing": "north",
                            "half": "upper",
                            "hinge": "right",
                            "open": "true",
                            "powered": "false"
                        }
                    }
                }
            }
        ]
    }
}
```
`block_column`とは、柱状にブロックを設置するfeatureです。ドアって縦2ブロックじゃん？
このファイルでも下からlower、upperと積み上げています。
これをドアのある座標で使うだけ。

### ブロックの設置可否をチェック
こちらをご覧ください。
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">トーチアローの面検知の仕方を変えてみました。<br>松明が置ければ置いてくれます。 <a href="https://t.co/oD4TDQIhdB">pic.twitter.com/oD4TDQIhdB</a></p>&mdash; 黒民 (@noir_kokumin) <a href="https://twitter.com/noir_kokumin/status/1561253712785420291?ref_src=twsrc%5Etfw">August 21, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

結構自然に置いてるでしょ？
これ、実はplace featureを使っています。
こんな感じの。
```
{
    "type": "minecraft:simple_random_selector",
    "config": {
        "features": [
            {
                "feature": {
                    "type": "minecraft:simple_block",
                    "config": {
                        "to_place": {
                            "type": "minecraft:simple_state_provider",
                            "state": {
                                "Name": "minecraft:wall_torch",
                                "Properties": {
                                    "facing": "east"
                                }
                            }
                        }
                    }
                },
                "placement": [
                    {
                        "type": "minecraft:block_predicate_filter",
                        "predicate": {
                            "type": "minecraft:would_survive",
                            "state": {
                                "Name": "minecraft:wall_torch",
                                "Properties": {"facing":"east"}
                            }
                        }
                    }
                ]
            }
        ]
    }
}
```
simple_random_selectorは複数のplaced_featureから1つ選択するものですが、placed_featureを読み込めることを利用して敢えて選択肢を1つだけにしています。
placementにある`would_survive`はそのブロックが自壊したりしないかどうかを条件としています。ツイートにある「松明が置ければ」っていうのはこういうことです。
~~面検知じゃねえなこれ~~
<font color="#aaaaaa">まあ面検知はネザー要塞でやってるから…</font>

### 指定した範囲にランダムにブロックを置く。
まず、こちらをご覧ください。
<iframe width="460" height="817" src="https://www.youtube.com/embed/9KMbfoBN7oU" title="アメジスト、生やしました。#Shorts" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
これ、右クリック検知とか効果音とかパーティクルは除いて、アメジスト出てくる所は1コマンドかつ1ファイルで実行しているんですよ。
この場合はこんな書き方をしています。

```
{
    "type": "minecraft:random_patch",
    "config": {
        "tries": 10,
        "xz_spread": 4,
        "y_spread": 0,
        "feature": {
            "feature": {
                "type": "minecraft:simple_block",
                "config": {
                    "to_place": {
                        "type": "minecraft:simple_state_provider",
                        "state": {
                            "Name": "minecraft:small_amethyst_bud",
                            "Properties": {
                                "facing": "up",
                                "waterlogged": "false"
                            }
                        }
                    }
                }
            },
            "placement": [
                {
                    "type": "minecraft:block_predicate_filter",
                    "predicate": {
                        "type": "minecraft:matching_block_tag",
                        "offset": [
                            0,
                            -1,
                            0
                        ],
                        "tag": "amefer:amethysts"
                    }
                }
            ]
        }
    }
}
```
`xz_spread`で設置したい場所の範囲を決めています。triesを10にすることで、その範囲の中の10ブロックがランダムにピックされているわけです。
また、設置条件(placement)で直下のブロックがアメジスト系ブロックであることを条件としています。


### 条件分岐しながらブロックを置換する
まずこちらをご覧ください。
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr">条件分岐でブロック置換 <a href="https://t.co/bQ9rMYLcpe">pic.twitter.com/bQ9rMYLcpe</a></p>&mdash; 黒民 (@noir_kokumin) <a href="https://twitter.com/noir_kokumin/status/1731635088616742928?ref_src=twsrc%5Etfw">December 4, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


はい、見ての通り1コマンドかつ1ファイルでブロックに応じて違うブロックに置換していますね。
これの正体は、type:diskのfeature。
川や沼の底に円状に粘土が見つかることがありますよね？アレです。

実はこのfeature、条件分岐が可能なんです。この動画の場合は次のような書き方をしています。

```{
    "type": "minecraft:disk",
    "config": {
        "state_provider": { // どの条件にも、マッチしなかった場合
            "fallback": {
                "type": "minecraft:simple_state_provider",
                "state": {
                    "Name": "minecraft:dirt"
                }
            },
            "rules": [ //どのブロックをどのブロックに変化させるか。
                {
                    "if_true": {
                        "type": "minecraft:matching_block_tag", //指定した位置がこのブロックタグにあるものになっているか
                        "offset": [
                            0,
                            0,
                            0
                        ],
                        "tag": "minecraft:base_stone_overworld" //石等、鉱石に置き換わるやつ
                    },
                    "then": {
                        "type": "minecraft:simple_state_provider",
                        "state": {
                            "Name": "minecraft:blackstone"
                        }
                    }
                },
                {
                    "if_true": {
                        "type": "minecraft:matching_blocks", //指定した位置がこのブロックか
                        "blocks": "minecraft:grass_block"
                    },
                    "then": {
                        "type": "minecraft:simple_state_provider",
                        "state": {
                            "Name": "minecraft:crimson_nylium"
                        }
                    }
                },
                {
                    "if_true": {
                        "type": "minecraft:matching_blocks",
                        "blocks": [
                            "minecraft:sand",
                            "minecraft:red_sand"
                        ]
                    },
                    "then": {
                        "type": "minecraft:simple_state_provider",
                        "state": {
                            "Name": "minecraft:soul_sand"
                        }
                    }
                },
                {
                    "if_true": {
                        "type": "minecraft:matching_block_tag",
                        "tag": "minecraft:gold_ores"
                    },
                    "then": {
                        "type": "minecraft:simple_state_provider",
                        "state": {
                            "Name": "minecraft:gilded_blackstone"
                        }
                    }
                }
            ]
        },
        "target": { // 変化させたいブロック
            "type": "minecraft:matching_blocks", // ここに一致するもののみ変化。常時変化させたい場合は"true"を使おう。
            "blocks": [
                "minecraft:stone",
                "minecraft:deepslate",
                "minecraft:grass_block",
                "minecraft:sand",
                "minecraft:red_sand",
                "minecraft:gold_ore",
                "minecraft:deepslate_gold_ore",
                "minecraft:nether_gold_ore"
            ]
        },
        "radius": 0, //diskのサイズで、この数値*2+1が横方向のサイズです。この場合は、1ブロックだけ換わります。
        "half_height": 0 //実は上下方向に伸ばすことも可能です。この数値*2+1が上下方向のサイズです。
    }
}
```
上の`state_provider`に`rules`という配列があるのですが、よく見るとその中に`if_true`とか`then`という気になる表記がありますね。
お察しの通り、このtypeの場合、条件がif-then構文になっています。
matching_blocksやmatching_block_tagはpredicateのlocation_checkのblock.blocksやtagと大体同じです。
ここに何もマッチしなかった場合はfallbackが適用されます。

targetでは変化させるブロックを指定しています。
radius、half_heightはそれぞれ横方向、上下方向に延長されるのですが、今回は指定した場所だけ置き換えたいので0にしています。縦2ブロック分だけ変化させたい、とか周りも変化させたいとかあればその分指定します。

# おわりに
如何でしたか？
まあ、ちょっと駆け足でうまく説明できなかったかもしれません。
でもなんだかちょっとだけ……「可能性」を感じませんか？
これを機にcustom featureやstructureに触れてもらえたら幸いです。

<font color="#eeeeee">多分そのうちいつも弄っているカスタムディメンジョンデータパックどこかに配布するので遊んでもらえたら嬉しいな</font>