いいね、ここで一回「ローダー」をちゃんと OS レイヤーとして定義しよう。


全体像

入力:

  • insect_parts
    • パーツごとの OBJ から作られた Model(MeshPart 群)
  • 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」を一気通貫でつなぐ全体図