新駒奈日誌

Minecraftの建築に関する情報をよろずに書いていく。

【Minecraft】 Axiom 補足 : Script Brush で自分だけのブラシを作る

Minecraftのワールド編集MOD「Axiom」のScript Brush (スクリプトブラシ) について詳しく解説します。

注意

  • 本記事は、Axiom Ver.3.0.0 (Minecraft 1.20.X) 現在の状況について記載しています。AxiomのMODについては、こちらの記事を参照してください。 komnazsk.hatenablog.jp
  • 本記事はプログラミングに関する内容を扱います。少しでもプログラミングに触ったことがある人なら、よく読めばきっと理解できると思います。逆に、全くプログラミングに触れたことがない人だとかなり難しい内容かもしれません。

Script Brush の概要

AxiomのScript Brushは、通常のブラシでは実現できない自分だけのブラシを作ることができる機能です。

Script Brushは、「Lua」と呼ばれるPythonに似たプログラミング言語を使用します。Axiom以外のMinecraftのMODでは、「ComputerCraft」のプログラミング言語としても採用されています。

AxiomではScript Brush以外にも、ブラシをかける条件を指定する「マスク」に対してもLua言語でプログラミングすることができます(マスクは本記事の対象外です)。

Script Brushのサンプル

Script Brushで実現可能なブラシは多種多様です。以下は、サンプルに入っているPresetsのブラシの一覧です。まずは実際にさわってどんな事ができるか確かめてみると良いでしょう。

Presets 説明
Clear Foliage 指定範囲を除草します。
Grass 指定範囲に草を生やします。草の種類はユーザが指定できます。
Flowers マウスを右クリックするたびに、ランダムで花を咲かせるブラシです。
Heart マウスを右クリックすると、赤い巨大なハートの立体を生成します。
Kelp 指定範囲の水中に昆布を生やすブラシです。
Leafy Bushes マウスを右クリックすると、葉ブロックを一定範囲に設置します。
Terrain: Desert 指定範囲の地面を砂漠バイオームの地層に置き換えます。
Terrain: Plains 指定範囲の地面を草原バイオームの地層に置き換えます。
Vines 指定範囲の壁にツタを生やします。

チュートリアル

言語仕様とか細かい点を先に説明してもいいですが、習うより慣れたほうが習得が早い人もいるかと思いますので、いくつか先にチュートリアルを試してみましょう。

ブロックの置き換え

最も単純な例として、「石 (stone) をセットするブラシ」を作成します。以下を入力して、右ドラッグでブラシをかけてみてください。選択した範囲に石が設置されたと思います。

return blocks.stone
  1. returnで、石 (blocks.stone) に置き換える

このように、基本的には指定した座標に対して、returnからはじまる文でブロックを指定することで、ある座標を置き換えるブラシを作ることができます。

※return以外の方法としては、setBlock(x,y,z)で特定の座標にブロックをセットすることもできます。

作ったブラシを保存する場合は、Presetsの「+」ボタンを押して、名前をつけて保存します。

条件分岐

もう少し手を加えてみましょう。以下は、「もしオークの葉 (oak_leaves) だったら、白樺の葉 (birch_leaves) に置き換える」というブラシの例です。以下を入力して、オークの木にブラシをかけてみてください。

if getBlock(x,y,z) == blocks.oak_leaves then
    return blocks.birch_leaves
end
  1. Luaでは、if 条件 thenendで囲むことで条件文を表します。
  2. getBlock(x,y,z)は、座標x, y, z (この値は、初期値として与えられます) のブロックを返します。
  3. 2で取得したブロックがオークの葉 (blocks.oak_leaves) と一致(==)していたら、return blocks.birch_leaves を実行して白樺の葉に置き換えます。

入力値を与える

ユーザがGUIでブロックまたは数値を入力するように組むこともできます。次は「空気以外のすべてのブロックを、ユーザが入力したブロックに置き換えるブラシ」例を示します。

input_block = $blockState(Input,stone)$

if getBlock(x,y,z) ~= blocks.air then
    return input_block
end
  1. $blockState(Input,stone)で、下図のように「Input」とタイトルの付いたブロックを入力するためのUIが表示されます。ここでユーザはブロックを選択することができます。その入力値 (デフォルトではstone) をinput_block変数にセットしています。
  2. 先程の例と異なり、ここでは空気ブロック (blocks.air) と一致しなかったら (~=) 入力したinput_blockで置き換えます。

実際ブラシをかけてみると、地面だけでなく水や植物まで置き換わったことがわかります。

草ブロックを置き換えないためには、getBlock(x,y,z) ~= blocks.air のところをisSolid(getBlock(x,y,z))に置き換えてみてください。isSolid(block)は、指定したブロックが固体ブロック(水や植物を除く)であるかどうかを判定します。

ライブラリを使ってみる

Axiomでは、ブロックを操作・取得する関数や、数学関数が用意されています。以下は、「固形ブロックに対して、ランダムに2つの羊毛ブロックに置き換えるブラシ」の例を示します。

value = $int(Value,5,0,10)$
random_value = math.random(0, 10)

if isSolid(getBlock(x,y,z)) then
    if value >= random_value then
        return blocks.green_wool
    else
        return blocks.lime_wool
    end
end
  1. $int(Value,5,0,10)$で、デフォルト5、最小値0、最大値10の値をユーザが入力するUIを表示して入力させます。変数valueに代入します。
  2. math.random(0, 10)で、0以上10以下の値をランダムで出力します。出力値はrandom_valueに入力します。
  3. isSolid(getBlock(x,y,z))関数で、x,y,z座標で取得したブロックが固形ブロックかどうかを識別します。
  4. 「ブロックが固形 (空気、植物、水は含まない) である」条件を満たした場合のみ、さら以下を実行します。
    1. valuerandom_value以上>=であれば、緑の羊毛 (blocks.green_wool) に置き換えます。
    2. そうでなければ (else)、黄緑の羊毛 (blocks.lime_wool) に置き換えます。

右クリックで1回適用されるブラシ

こはちょっと難しくなります。右クリックで1回だけ適用されるブラシの例です。以下は「右クリックした箇所を中心に、半径10ブロックの円形に花畑を咲かせるブラシ」です。

$once$

size = 10
for distX = -size,size,1 do
    for distZ = -size,size,1 do
        if distX^2 + distZ^2 < size^2 then
            setBlock(x + distX, y + 1, z + distZ, blocks.cornflower)
        end
    end
end
  1. $onceを最初に指定することで、「右クリックで指定した箇所に1回だけ適用されるブラシ」になります。
  2. 円の半径は変数size = 10で指定しています。
  3. x座標は -size \leqq x \leqq size の範囲で花畑を咲かせます。z座標も同様です。ここでは、それぞれfor文による繰り返し文を使用します。Luaでは、繰り返し文はfor 変数=(開始値),(終了値),(増減値) doendで囲みます。
  4. 「円形の内側である」条件は、高校で習う円の方程式  x^{2} + y^{2} \lt r^{2} に従います。 プログラム的には、if distX^2 + distZ^2 < size^2 thenの部分 (XZの平面なのでYの部分はZになる) です。
  5. 円の内側であれば、setBlock(x,y,z)で指定した座標に青い花 (cornflower) をセットします。 円の計算はあくまで原点0からの相対座標だったので、x座標とy座標はそれぞれx + distXz + distZで絶対座標に変換してからセットしています。Y座標は右クリックした座標の1ブロック上y + 1としています。

色々とできるようになると・・・

色々と知識を深めると、より複雑なブラシを作れるようになります。

ここで、筆者が作った「陸地に農地を生やすブラシ」を作った例を下記に公開します。みなさんも、作ったブラシをコミュニティ等で公開すると楽しくなるのではと思います。

農地を生やすブラシ: https://onedrive.live.com/?authkey=%21ANzDqFLxwH00lSY&cid=FD7C12F059C23EEC&id=FD7C12F059C23EEC%2110900&parId=FD7C12F059C23EEC%2110899&o=OneUp


仕様

参考情報

AxiomのScript Brushの仕様については、下記に記載されています(英語)。

https://axiomdocs.moulberry.com/tools/painting/scriptbrush.html

Lua言語の仕様は、まともな日本語の参考資料が少ないです。以下は参考になるかもしれないですが、一部適用できない部分がある (例: print文はAxiomでは出力されません) ため、Script Brushのサンプルやチュートリアルを少し変えてみて覚えたほうが早いと思います。

qiita.com

Script BrushではLuaの数学関数(math)ライブラリを使用することができます。数学関数ライブラリの仕様は、以下が参考になります。

inzkyk.xyz

Axiomの変数

AxiomのScript Brushでは、以下の変数は最初から利用することができます。

  • x, y, z: ブラシを掛けた地点のXYZ座標を表します。
  • blocks.(ブロック名): ブロックを表します。ブロック名は、Axiomでブロックを選択するときに表示される minecraft:stone の stone の部分に該当する名前です。

AxiomのAPI

AxiomのScript Brushでは、以下の関数が利用できます。

関数 説明
getBlock(x, y, z) 指定座標のブロックの種類を取得します。
getBlockState(x, y, z) 指定座標のブロックの種類とblockstateを取得します。後述するwithBlockPropertyで取得した値や、ユーザ入力値$blockStateで取得した値と比較できます。
getHighestBlockYAt(x,z) 空気ブロック以外で最も高い位置にあるブロックのY座標を取得します。
getSimplexNoise(x,y,z,seed) シンプレックスノイズ関数に基づき、0~1の範囲内で値を取得します。
getVoroniEdgeNoise(x,y,z,seed) ボロノイ図関数に基づき、0~1の範囲内で値を取得します。
isSolid(block) 指定したブロックが固体ブロックかどうか調べ、trueまたはfalseの値を返します。水や空気、植物ブロックは対象外です。
isBlockTagged(block,"tag") 指定したブロックが指定したタグを持っているかどうか調べます。
withBlockProperty(block,"property=value") 指定ブロックの指定プロパティ(blockstate)の値を取得します。
getBlockProperty(block,"property") 指定ブロックの指定プロパティの値を取得します。
setBlock(x,y,z,block) 指定した座標に指定したブロックをセットします。

シンプレックスノイズとボロノイ図については、AxiomのNoise Painterを使ってみるとどんな振る舞いになるかイメージが掴めると思います。

komnazsk.hatenablog.jp

Axiomテンプレート変数

AxiomのScript Brushでは、ユーザが入力値を設定するためのテンプレート変数が用意されています。

テンプレート変数 説明
$once$ クリックごとにスクリプトを1回だけ実行します。
$blockState(title,block)$ GUIを使用してユーザがブロックを入力できるようにします。
$int(title,default,min,max)$ スライダーGUIを表示して、ユーザが整数値を入力できるようにします。
$float(title,default,min,max)$ スライダーGUIを表示して、ユーザが小数値を入力できるようにします。
$boolean(title,default(true/false))$ トグルGUIを表示して、ユーザがオン(true)/オフ(false)を設定できるようにします。