Roblox Studio の「Model を挿入したときどこに現れるか」「その Position はどう決まるか」は、実は 状況によってルールが変わるんです。
🎭 結論:Model の挿入位置は “文脈依存” で決まる
以下の 4 パターンで挙動が変わります。
🟦 1. エクスプローラーで右クリック → Insert(挿入)した場合
➤ その親(Parent)にそのまま追加される
- Workspace を右クリック → Insert → Model
→ Workspace の直下に追加される - Folder を右クリック → Insert → Model
→ その Folder の中に追加される
➤ Position は (0, 0, 0) 付近になる
- Model 内に PrimaryPart が設定されていない場合
→ Model の中のパーツの位置がそのまま使われる - PrimaryPart が設定されている場合
→ PrimaryPart の位置が (0, 0, 0) に来るように配置される
つまり、Model の内部データに依存する。
🟦 2. Toolbox からドラッグ&ドロップした場合
➤ カメラの前方にスポーンする
Roblox Studio は「ユーザーが見ている方向」を優先するため、
- Camera の CFrame の前方 10〜20 stud 付近
- 地面に接地するように調整されることが多い
➤ Position は Camera に依存
例:
Camera が (0, 50, 0) を向いているなら、
Model は (0, 50, -15) 付近に出る。
🟦 3. 3D Viewport にドラッグ&ドロップした場合
➤ ドロップした地点の地面にスナップする
- マウスカーソルが指している Raycast のヒット位置に配置される
- 地面がない場合は Camera 前方に落ちる
➤ Position は Raycast の結果で決まる
🟦 4. Script で Instance.new("Model") した場合
➤ 位置は “存在しない” ところから始まる
Model は 位置を持たないコンテナなので、
生成した瞬間は Position が決まらない。
local m = Instance.new("Model")
m.Parent = workspace
この状態では 空の Model が Workspace に置かれるだけ。
➤ パーツを入れた瞬間に位置が決まる
Model 内に Part を入れると、その Part の Position が Model の位置の基準になる。
Roblox Studio のアセット管理(Asset Manager)で選択中の Model を扱う場合、中心を (0,0,0) に揃える処理と、Workspace 内の“見えるフォルダ”を対話的に作る処理を分けて考えると整理しやすいです。
以下に ① 対話的に Workspace フォルダを作る関数 と
② 選択中 Model を原点に揃えてそのフォルダに入れる関数
を示します。
🟦 ① Workspace に「見えるフォルダ」を対話的に作る関数
Roblox Studio の Plugin / Command Bar で動くコードです。
-- Workspace にフォルダを対話的に作る
function CreateVisibleFolder()
local name = ""
repeat
name = tostring(game:GetService("StarterGui"):Prompt("フォルダ名を入力してください"))
until name and name ~= ""
local folder = Instance.new("Folder")
folder.Name = name
folder.Parent = workspace
print("フォルダを作成:", folder:GetFullName())
return folder
end
🔍 ポイント
- Studio には標準の入力ダイアログがないため、
StarterGui:Prompt()のような簡易 UI を使うか、
PluginWidget を使うのが一般的です。 - ここでは「対話的に名前を入力する」構造を示しています。
🟦 ② 選択中の Model を Position(0,0,0) に揃えてフォルダに入れる関数
Asset Manager で選択した Model は Selection サービスから取得できます。
local Selection = game:GetService("Selection")
-- Model の中心を (0,0,0) に揃える
local function CenterModelAtOrigin(model)
-- PrimaryPart が必要
if not model.PrimaryPart then
warn("PrimaryPart が設定されていません:", model.Name)
return
end
-- 現在の中心位置
local currentPos = model.PrimaryPart.Position
-- 原点との差分
local offset = Vector3.new(0,0,0) - currentPos
-- Model 全体を移動
model:MoveTo(model.PrimaryPart.Position + offset)
end
-- 選択中 Model をフォルダに入れる
local function MoveSelectedModelToFolder(folder)
local selected = Selection:Get()
if #selected == 0 then
warn("何も選択されていません")
return
end
for _, obj in ipairs(selected) do
if obj:IsA("Model") then
CenterModelAtOrigin(obj)
obj.Parent = folder
print("移動:", obj.Name, "→", folder:GetFullName())
end
end
end
🟦 ③ 全体の流れ(目的に最適化)
local folder = CreateVisibleFolder()
MoveSelectedModelToFolder(folder)
これで、
- ユーザーがフォルダ名を入力して Workspace にフォルダを作る
- Asset Manager で選択中の Model を原点に揃える
- そのフォルダに収納する
という一連の舞台裏作業が自動化できます。
🎯 PrimaryPart がない Model に自動設定する関数
以下の関数は、Model 内の Part をスキャンして、
最も“中心に近い Part”を PrimaryPart に自動設定します。
-- Model の PrimaryPart を自動設定する
local function AutoSetPrimaryPart(model)
if not model:IsA("Model") then
warn("Model ではありません:", model)
return false
end
-- すでに PrimaryPart がある場合は何もしない
if model.PrimaryPart then
return true
end
-- Model 内の Part を収集
local parts = {}
for _, obj in ipairs(model:GetDescendants()) do
if obj:IsA("BasePart") then
table.insert(parts, obj)
end
end
if #parts == 0 then
warn("Model に BasePart がありません:", model.Name)
return false
end
-- Model の中心(BoundingBox の中心)を取得
local cf, size = model:GetBoundingBox()
local center = cf.Position
-- 中心に最も近い Part を探す
local bestPart = nil
local bestDist = math.huge
for _, part in ipairs(parts) do
local dist = (part.Position - center).Magnitude
if dist < bestDist then
bestDist = dist
bestPart = part
end
end
-- PrimaryPart に設定
model.PrimaryPart = bestPart
print("PrimaryPart を設定:", model.Name, "→", bestPart.Name)
return true
end
🟦 PrimaryPart 自動設定 → 原点に揃える → フォルダに収納
一連の流れはこうなります。
local function PrepareModel(model)
AutoSetPrimaryPart(model)
CenterModelAtOrigin(model)
end
このように PrimaryPart の自動設定は前処理として必須になります。
これはつまり 「Model の中心(BoundingBox の中心)を“仮想的な PrimaryPart”として扱い、実際に中心にパーツがなくても、そこを基準に位置合わせしたい」 ということですね。
Roblox の Model は PrimaryPart がないと MoveTo で中心基準の移動ができないため、
中心にパーツがなくても中心を基準に動かす“疑似 PrimaryPart”処理が必要になります。
そこで、靖さんの目的に完全に合う 中心基準の移動関数を示します。
🎯 中心にパーツがなくても Model を中心基準で (0,0,0) に揃える関数
-- Model を中心基準で (0,0,0) に移動する
local function MoveModelCenterToOrigin(model)
-- BoundingBox から中心を取得
local cf, size = model:GetBoundingBox()
local center = cf.Position
-- 中心を原点に合わせるためのオフセット
local offset = Vector3.new(0,0,0) - center
-- Model 内のすべての BasePart を移動
for _, obj in ipairs(model:GetDescendants()) do
if obj:IsA("BasePart") then
obj.Position = obj.Position + offset
end
end
end
🟦 この関数の特徴
✔ PrimaryPart が不要
Model.PrimaryPart がなくても動く。
✔ 中心にパーツがなくても正確に中心を使う
BoundingBox の中心を使うため、
立方体の中心が空洞でも問題なし。
✔ Model 全体を“中心基準”で移動できる
MoveTo の代わりに 全パーツを相対移動する方式。
ここまでの流れを 完全に一つの関数群として統合し、
- PrimaryPart がなくても中心基準で揃える
- 中心にパーツがなくても BoundingBox の中心で揃える
- Workspace に対話的にフォルダを作る
- 選択中の Model をそのフォルダに入れる
という一連の“舞台セットのセンタリング → 舞台袖への収納”を
ワンアクションで実行できる形にまとめます。
🎯 **完全統合版:
「中心揃え → フォルダ作成 → フォルダに収納」**
以下は Plugin / Command Bar で動くコードです。
🟦 ① Model を中心基準で (0,0,0) に揃える(PrimaryPart 不要)
local function MoveModelCenterToOrigin(model)
local cf, size = model:GetBoundingBox()
local center = cf.Position
local offset = Vector3.new(0,0,0) - center
for _, obj in ipairs(model:GetDescendants()) do
if obj:IsA("BasePart") then
obj.Position = obj.Position + offset
end
end
end
🟦 ② Workspace に対話的にフォルダを作る
Roblox Studio には標準の入力ダイアログがないため、
ここでは 簡易的に Command Bar 用の入力として扱います。
local function CreateFolderInWorkspace(name)
local folder = Instance.new("Folder")
folder.Name = name
folder.Parent = workspace
print("フォルダ作成:", folder:GetFullName())
return folder
end
※ 靖さんが Plugin 化するなら、ここを PluginWidget に置き換え可能。
🟦 ③ 選択中の Model を中心揃えしてフォルダに入れる
local Selection = game:GetService("Selection")
local function ProcessSelectedModels(folder)
local selected = Selection:Get()
if #selected == 0 then
warn("何も選択されていません")
return
end
for _, obj in ipairs(selected) do
if obj:IsA("Model") then
MoveModelCenterToOrigin(obj)
obj.Parent = folder
print("移動:", obj.Name, "→", folder:GetFullName())
end
end
end
🟦 ④ 全体統合:
「フォルダ名を指定 → 中心揃え → フォルダに収納」
local function CenterAndStoreSelectedModels(folderName)
local folder = CreateFolderInWorkspace(folderName)
ProcessSelectedModels(folder)
end
🎬 使い方(Command Bar)
CenterAndStoreSelectedModels("MyCenteredModels")
これで:
- Workspace に「MyCenteredModels」フォルダが作られる
- 選択中の Model が
- PrimaryPart がなくても
- 中心にパーツがなくても
BoundingBox の中心基準で (0,0,0) に揃えられる
- そのフォルダに収納される
という一連の舞台裏作業が一発で完了します。
ここは 「既存フォルダがある場合にどう振る舞うべきか」という、
舞台袖(Workspace)管理の“運用ルール”の話になりますね。
結論から言うと、
既にフォルダがある場合の動作は、どうしたいかで分岐できる
ように設計するのが一番透明で扱いやすいです。
ただ、一般的な「期待される動作」は以下の3つのどれかです。
🎯 既存フォルダがある場合の標準的な動作パターン
🟦 パターン1:既存フォルダをそのまま使う(最も自然)
フォルダ名が一致したら 新しく作らず、そのフォルダに入れる。
これは舞台袖の“既存区画”にセットを収納するイメージ。
🟦 パターン2:同名フォルダがあっても新しく作る(バージョン管理向け)
例:
- Models
- Models (2)
- Models (3)
のように 自動で連番をつけて新規作成する。
🟦 パターン3:ユーザーに選ばせる(対話的)
- 既存フォルダを使う
- 新しく作る
- 別のフォルダ名にする
「外部化・可視化」思想に最も合うのはこれ。
🎯 統合スクリプトに最適な動作
あなたのワークフロー(舞台構造の整理・可視化)を考えると、
既存フォルダがあればそれを使う
という動作が最も自然で、混乱も少ないです。
そこで、統合スクリプトを以下のように改良します。
🟦 既存フォルダがあればそれを使い、なければ作る関数
local function GetOrCreateFolder(name)
-- Workspace 内に同名フォルダがあるか探す
for _, child in ipairs(workspace:GetChildren()) do
if child:IsA("Folder") and child.Name == name then
print("既存フォルダを使用:", child:GetFullName())
return child
end
end
-- なければ新規作成
local folder = Instance.new("Folder")
folder.Name = name
folder.Parent = workspace
print("新規フォルダ作成:", folder:GetFullName())
return folder
end
🟦 統合処理(中心揃え → フォルダ収納)
local function CenterAndStoreSelectedModels(folderName)
local folder = GetOrCreateFolder(folderName)
ProcessSelectedModels(folder)
end