カスタムフォントで汎用的なUIを作る

この記事は、[Minecraft Command Advent Calendar 2023](https://qiita.com/advent-calendar/2023/mccmd) 23日目の記事です。:santa:

## はじめに
マクロもフォントの`"space"`も無い時に作ったカスタムフォントのUIが時代遅れになってきたので、マクロを使って一から作り直してみたという話です。
この記事のデータパックは[yavu/CustomUI](https://github.com/yavu/CustomUI)に、リソースパックは[yavu/CustomUI_Resources](https://github.com/yavu/CustomUI_Resources)にあります。

![custom_bar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/1a2d5a83-83cd-1853-820d-d23982df6f27.gif)

:::note warn
`JE ver1.20.4` で開発しています
:::

## カスタムバー
経験値バーのような棒状のUIです。
今回は、引数を元に、現在値が反映されたバーの文字列の配列を返すファンクションを作ります。

### 引数
<font color=#76889A>`debug:paint_test.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/b3e7a7d7-e398-d27b-3574-e41effdf229a.png)
`満タン状態の文字列`、`空状態の文字列`、`総目盛り数`、`デリミタの文字数`、`現在値として使うスコアボード`、`最大値`、`増える向きの反転`、`結果の保存先NBTパス` の値をマクロで渡して、

<font color=#76889A>`custom_bar:renderer/.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/4ac307a0-3791-9944-922a-4d0284adebf0.png)
メインのファンクションで受け取ります


### 目盛りの位置を算出する
![目盛り位置 2023_12_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/33ada652-dc96-2306-10d9-3a42a3c333fd.png)
`現在値`と`最大値`、`総目盛り数`から、`目盛りの位置`を算出します。

<font color=#76889A>`custom_bar:renderer/.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/b214e6a1-29bc-6779-7b90-114012de1f1c.png)
`目盛りの位置 = 総目盛り数 × 現在値 ÷ 最大値`です。

### トリミング位置を算出する
`総目盛り数`と`目盛りの位置`で、`満タン状態の文字列`と`空状態の文字列`のトリミングに使う値を算出します。

<font color=#76889A>`custom_bar:renderer/.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/4569c7a8-2076-e1e3-7023-ee2cfeddbbde.png)
バーの伸びる方向で計算が変わるので、分岐します。

#### 左からの場合
<font color=#76889A>`custom_bar:renderer/calc/l2r.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/9c81d898-170d-4f97-a556-9011d01caf54.png)
`満タン状態の文字列`のトリミングは、開始が`0`、終了が`目盛りの位置`で、

<font color=#76889A>`custom_bar:renderer/calc/l2r.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/a3f2c701-faff-f69a-928c-0375a47eb9de.png)
`空状態の文字列`のトリミングは、開始が`目盛りの位置`、終了が`総目盛り数`です。

<font color=#76889A>`custom_bar:renderer/calc/l2r.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/bc7887f2-ab1a-17ef-6004-387f11184071.png)
そのままではデリミタの分だけトリミング位置がズレてしまうので、補正します。
算出した値を`デリミタの文字数 + 1` 倍して、そのままではデリミタ1つ分多いのでその分引いています。

#### 右からの場合
<font color=#76889A>`custom_bar:renderer/calc/r2l.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/482f0e40-1955-4396-2f6b-bf70c673d7f9.png)
`満タン状態の文字列`のトリミングは、開始が`総目盛り数 - 目盛りの位置`、終了が`総目盛り数`で、

<font color=#76889A>`custom_bar:renderer/calc/r2l.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/00379750-fa9d-4f89-02df-5fd92657b5dc.png)
`空状態の文字列`のトリミングは、開始が`0`、終了が`総目盛り数 - 目盛りの位置`です。

<font color=#76889A>`custom_bar:renderer/calc/r2l.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/f2ac18fc-d9da-f85d-9f75-0c83725845e0.png)
デリミタ分の補正をします。
この処理は左からの場合のものと同じです。

### トリミングする
<font color=#76889A>`custom_bar:renderer/.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/71b537dc-e131-7ce4-4e3c-1478568ecdd0.png)
トリミングも、バーの方向で分岐します。
マクロで渡すために、値を一度ストレージに保存しています。

#### 左からの場合
<font color=#76889A>`custom_bar:renderer/trim/l2r.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/06b176bf-fe67-5b2b-13ff-88a6533bbb70.png)
`結果の保存先NBTパス`の配列を初期化し、順に保存します。
#### 右からの場合
<font color=#76889A>`custom_bar:renderer/trim/r2l.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/3a19a330-3500-d93d-3461-5d3f518d46c2.png)
右からの場合は保存順が逆なだけです。

### プレイヤーに表示する
<font color=#76889A>`debug:paint_test.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/25cf2bab-1177-9795-1992-69d8094dd1fe.png)
`"interpret"`を`true`にすることで、配列内の文字列を繋げて表示することができます。
一番最初に`{"text": ""}`を書いているのは、titleコマンドは最初のセクションの書体や文字色、フォントなどをそれ以降のセクションが継承するので、それらの指定が無いセクションを始めに書くことでそれを防ぐことができるためです。
これでコマンド面は完成しました。

### カスタムフォント
このままでは`#.#.#.#.#.#.-.-.-.-.-.-`のように表示されてしまうので、リソースパックのフォントで見た目を変更します。

#### フォント作成
<font color=#76889A>`scale.png`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/855a118d-1a6e-1ed8-bbd0-89d3df557421.png)
このような`オンの目盛り`と`オフの目盛り`のテクスチャを作り、

<font color=#76889A>`minecraft:font/custom_bar/test.json`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/46ea1dfe-6b31-e7e3-902d-8cbbf2c538c3.png)
jsonファイルで指定しフォントにします。

#### 反映
<font color=#76889A>`debug:paint_test.mcfunction`</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/fa1f6f5b-3447-20e0-1f2d-102a6feb1155.png)
後はコマンド側でフォント指定すれば、

![custom_bar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/1a2d5a83-83cd-1853-820d-d23982df6f27.gif)
カスタムフォントのバー完成です。

### おまけ
![custom_health.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3606608/62cfc378-3cc4-a34f-8ae0-16ed66161044.gif)
引数で渡す文字列と指定するフォントを変えるとこんなこともできます。

## おわりに
今回はマクロを使って汎用的なUIを作ってみました。
円グラフのようなUIも作ってあるのですが、書く時間が足りませんでした...。
次はそれについて書こうと思います。