SubstituteGeometry used to work fine, but recently (weeks?) it fails to do anything and doesn’t update the original object’s mesh (visually, at least). This is in Studio and I’ve reproduced it via the command bar and a plugin.
When using it I’ll see the Size property of the object update, but not the mesh. Consequently, the Size can become misaligned with the appearance.
Here’s a simple test case that will run in the command bar. I would expect this to result in a sphere being visible but it results in the original cuboid remaining visible. Notably, the triangle count property doesn’t update either.
This creates a Cuboid (unioned with a smaller cuboid inside) and does the same with a Sphere. Then replaces the Cuboid’s geometry with the Sphere union’s. The same result happens with {} passed to UnionAsync - the small cuboid isn’t important, I just wanted to eliminate the risk that a single-part union was a problem.
local Selection = game:GetService("Selection")
local ChangeHistoryService = game:GetService("ChangeHistoryService")
local GeometryService = game:GetService("Geometry")
ChangeHistoryService:SetWaypoint("Before Test SubstituteGeometry")
-- Create main part (cuboid/box) - this will be substituted
local mainPart = Instance.new("Part")
mainPart.Size = Vector3.new(4, 4, 4)
mainPart.Anchored = true
mainPart.CFrame = CFrame.new(0, 10, 0)
mainPart.Name = "TestMainPart_Cuboid"
mainPart.Parent = workspace
-- Create small cuboid to union with main part (to make it a proper union operation)
local smallCuboid1 = Instance.new("Part")
smallCuboid1.Size = Vector3.new(0.5, 0.5, 0.5)
smallCuboid1.Anchored = true
smallCuboid1.CFrame = CFrame.new(0, 10, 0) -- Same position, will be inside
smallCuboid1.Name = "TestSmallCuboid1"
smallCuboid1.Parent = workspace
-- Create sphere part to union with
local spherePart = Instance.new("Part")
spherePart.Shape = Enum.PartType.Ball
spherePart.Size = Vector3.new(4, 4, 4)
spherePart.Anchored = true
spherePart.CFrame = CFrame.new(0, 10, 0)
spherePart.Name = "TestSpherePart"
spherePart.Parent = workspace
-- Create small cuboid to union with sphere part
local smallCuboid2 = Instance.new("Part")
smallCuboid2.Size = Vector3.new(0.5, 0.5, 0.5)
smallCuboid2.Anchored = true
smallCuboid2.CFrame = CFrame.new(0, 10, 0) -- Same position, will be inside
smallCuboid2.Name = "TestSmallCuboid2"
smallCuboid2.Parent = workspace
warn("[Test SubstituteGeometry] Creating main PartOperation (cuboid union)...")
-- Create main union (cuboid/box) using part's :UnionAsync() method with small cuboid
local mainPartOp = mainPart:UnionAsync({smallCuboid1})
mainPartOp.Name = "TestMainPartOp_Cuboid"
mainPartOp.Anchored = true
mainPartOp.CFrame = CFrame.new(0, 10, 0)
mainPartOp.Parent = workspace
mainPart:Destroy() -- Clean up original parts
smallCuboid1:Destroy()
warn("[Test SubstituteGeometry] Creating substitute PartOperation (sphere union)...")
-- Create substitute union (sphere) using part's :UnionAsync() method with small cuboid
local substitutePart = spherePart:UnionAsync({smallCuboid2})
substitutePart.Name = "TestSubstitutePart_Sphere"
substitutePart.Anchored = true
substitutePart.CFrame = mainPartOp.CFrame
substitutePart.Parent = workspace
spherePart:Destroy() -- Clean up original parts
smallCuboid2:Destroy()
warn("[Test SubstituteGeometry] Attempting SubstituteGeometry (cuboid -> sphere)...")
local ok, err = pcall(function()
mainPartOp:SubstituteGeometry(substitutePart)
end)
if ok then
warn("[Test SubstituteGeometry] ✓ SubstituteGeometry call succeeded!")
warn("[Test SubstituteGeometry] The selected object should now be a SPHERE instead of a CUBOID")
warn("[Test SubstituteGeometry] Check visually - if it's still a cube, SubstituteGeometry didn't work")
-- Clean up the substitute part
substitutePart:Destroy()
Selection:Set({mainPartOp})
else
warn(string.format("[Test SubstituteGeometry] ✗ SubstituteGeometry failed: %s", tostring(err)))
-- Clean up both
mainPartOp:Destroy()
substitutePart:Destroy()
end
ChangeHistoryService:SetWaypoint("After Test SubstituteGeometry")
Expected behavior
I would expect that calling SubstituteGeometry would substitute the visual geometry of the union with the one that is passed to it.