いいね、ここで一回「ローダー」をちゃんと OS レイヤーとして定義しよう。
全体像
入力:
insect_parts- パーツごとの OBJ から作られた
Model(MeshPart 群)
- パーツごとの OBJ から作られた
motionPatch- JSON(または Lua テーブル)
rig.bones[]とpatches[]を持つ
出力:
Model(階層構造つき)- 各パーツ間に
Motor6D MotionPatchに基づく制御スクリプト
MotionPatch の想定構造(Lua テーブル)
local motionPatch = {
rig = {
root = "Thorax",
bones = {
{ id = "Head", parent = "Thorax", partName = "Head" },
{ id = "Abdomen", parent = "Thorax", partName = "Abdomen" },
{ id = "WingFrontL", parent = "Thorax", partName = "WingFrontL" },
{ id = "WingFrontR", parent = "Thorax", partName = "WingFrontR" },
},
},
patches = {
{
id = "wing_flap_L",
target = "WingFrontL",
type = "oscillate",
axis = Vector3.new(1, 0, 0),
rangeDeg = 45,
frequencyHz = 18,
},
{
id = "wing_flap_R",
target = "WingFrontR",
type = "oscillate",
axis = Vector3.new(1, 0, 0),
rangeDeg = 45,
frequencyHz = 18,
},
}
}
1. Motor6D を貼るローダー
local RunService = game:GetService("RunService")
local function createMotorBetween(parentPart: BasePart, childPart: BasePart, name: string): Motor6D
local motor = Instance.new("Motor6D")
motor.Name = name
motor.Part0 = parentPart
motor.Part1 = childPart
motor.C0 = parentPart.CFrame:ToObjectSpace(childPart.CFrame)
motor.C1 = CFrame.new()
motor.Parent = parentPart
return motor
end
local function applyRig(model: Model, motionPatch)
local bones = motionPatch.rig.bones
local boneMap = {}
for _, bone in ipairs(bones) do
local parentPart = model:FindFirstChild(bone.parent, true)
local childPart = model:FindFirstChild(bone.partName, true)
if parentPart and childPart then
local motor = createMotorBetween(parentPart, childPart, bone.id)
boneMap[bone.id] = {
motor = motor,
part = childPart,
}
else
warn("Bone mapping failed:", bone.id, bone.parent, bone.partName)
end
end
return boneMap
end
2. MotionPatch を Motor6D 制御にマッピング
ここではまず oscillate(羽ばたき) だけ実装する例。
local function applyPatches(model: Model, motionPatch, boneMap)
local patches = motionPatch.patches
local connections = {}
for _, patch in ipairs(patches) do
local boneEntry = boneMap[patch.target]
if not boneEntry then
warn("No bone for patch target:", patch.target)
continue
end
local motor = boneEntry.motor
local axis = patch.axis or Vector3.new(1, 0, 0)
local range = math.rad(patch.rangeDeg or 30)
local freq = patch.frequencyHz or 2
if patch.type == "oscillate" then
local startTime = os.clock()
local conn
conn = RunService.Heartbeat:Connect(function()
if not motor.Parent then
conn:Disconnect()
return
end
local t = os.clock() - startTime
local angle = math.sin(t * math.pi * 2 * freq) * range
local rot = CFrame.fromAxisAngle(axis, angle)
motor.Transform = rot
end)
table.insert(connections, conn)
else
warn("Unsupported patch type:", patch.type)
end
end
return connections
end
3. ローダーのエントリポイント
local function loadInsectWithMotion(model: Model, motionPatch)
-- 1. Motor6D リグ構築
local boneMap = applyRig(model, motionPatch)
-- 2. MotionPatch を Motor6D 制御に変換
local connections = applyPatches(model, motionPatch, boneMap)
return {
model = model,
bones = boneMap,
connections = connections,
}
end
return loadInsectWithMotion
ここまででできること
- 生成側からは OBJ(パーツごと)+ MotionPatch JSON を渡すだけ
- Roblox 側では:
- OBJ → MeshPart 群 → Model
- MotionPatch → Motor6D+羽ばたき制御
- 後から:
type = "walk_cycle",type = "antenna_wiggle"などを追加していける
次、どこを詰めたい?
patch.typeのバリエーション設計(walk / wiggle / sway)- MotionPatch JSON → Lua テーブルへの読み込みレイヤー
- 「昆虫 IR → OBJ+MotionPatch」を一気通貫でつなぐ全体図