ここからは 「16×16 の Part グリッドを並べて、GUI で選んだ地形パレット色に応じて対話的に色を変える」 ところまでを、Roblox でそのまま動かせる形でまとめるね。

まだ 256×256 への変換は後で
まずは インタラクティブな 16×16 ペイント UI を完成させる。


🎨 目標

  1. 16×16 の Part を Workspace に並べる
  2. GUI で地形マテリアル(Grass, Sand, Rock…)を選ぶ
  3. クリックした Part の色がパレットに応じて変わる
  4. 内部バッファ(16×16)にも値を保存する
  5. 後でこのバッファを 256×256 に拡大して Terrain に変換する

🟦 1. まず 16×16 の Part を並べるスクリプト

ServerScriptService に置く。

local folder = Instance.new("Folder")
folder.Name = "PaintGrid"
folder.Parent = workspace

local SIZE = 16
local PART_SIZE = 4

for y = 1, SIZE do
    for x = 1, SIZE do
        local p = Instance.new("Part")
        p.Size = Vector3.new(PART_SIZE, 1, PART_SIZE)
        p.Anchored = true
        p.Position = Vector3.new(x * PART_SIZE, 0, y * PART_SIZE)
        p.Name = x .. "_" .. y
        p.Parent = folder
    end
end

🟦 2. GUI でパレットを選ぶ

例として TextButton を並べた簡単なパレット

StarterGui に ScreenGui → Frame → TextButton を複数置く。

例:

  • Grass
  • Sand
  • Rock
  • Mud
  • Snow

LocalScript を Frame に入れる:

local palette = {
    Grass = Color3.fromRGB(80, 200, 80),
    Sand  = Color3.fromRGB(230, 220, 170),
    Rock  = Color3.fromRGB(120, 120, 120),
    Mud   = Color3.fromRGB(90, 60, 40),
    Snow  = Color3.fromRGB(240, 240, 255)
}

local selected = "Grass"

for _,btn in pairs(script.Parent:GetChildren()) do
    if btn:IsA("TextButton") then
        btn.MouseButton1Click:Connect(function()
            selected = btn.Name
        end)
    end
end

-- 選択中のパレットを他スクリプトが読めるように
_G.SelectedPalette = function()
    return selected, palette[selected]
end

🟦 3. Part をクリックして色を変える

PaintGrid の Part にクリックイベントを付ける。

StarterPlayerScripts に LocalScript:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()

local buffer16 = {}  -- 16x16 の内部バッファ
for y = 1, 16 do
    buffer16[y] = {}
    for x = 1, 16 do
        buffer16[y][x] = "Grass"  -- 初期値
    end
end

mouse.Button1Down:Connect(function()
    local target = mouse.Target
    if not target then return end
    if not target.Parent or target.Parent.Name ~= "PaintGrid" then return end

    local name = target.Name  -- "3_5" のような形式
    local x, y = name:match("(%d+)_(%d+)")
    x = tonumber(x)
    y = tonumber(y)

    local matName, color = _G.SelectedPalette()

    -- Part の色を変える
    target.Color = color

    -- 内部バッファに保存
    buffer16[y][x] = matName
end)

🟦 4. これで「対話的な 16×16 ペイント」が完成

  • GUI でパレットを選ぶ
  • Part をクリックすると色が変わる
  • 内部バッファに地形マテリアル名が保存される

このバッファが 後で 256×256 に拡大して Terrain に変換する元データ になる。


🟦 5. 次のステップ(必要なら続けて作る)

ここから先は靖さんの OS 設計に合わせて、次の段階を作れる:

✔ 16×16 → 256×256 のガウス平滑アップサンプル

(前に書いた関数を統合)

✔ 256×256 を Terrain:SetMaterialHeightmap に変換

(MaterialMap と HeightMap を PNG 生成 or 数値配列で Terrain に書き込み)

✔ GUI に「筆圧」「ブラシサイズ」「にじみ」などを追加

(あなたの“半紙のにじみ”OS に合わせる)


次はどこを作りたい?

  • バイリニア拡大
  • ガウス平滑高速化(separable)
  • Terrain への書き込み
  • ブラシUIの強化

どれでもすぐ続けられるよ。