I’m working on a placement system which snaps object positions relative to a part’s surface. The problem is that it only works correctly with a grid size of 1 stud on any part which has a size of at least 2 studs on all axis, but it’s not snapping properly with some differently sized parts and grid sizes. The object can go inside parts or float above parts. I’ve added some images below so you can see this behavior.
Grid size: 1 stud; Correct behavior
Grid size: 5 studs; Object goes inside part
Grid size: 5 studs; Object floats above part
Snap position calculation code:
function PlacementUtils.snapToGrid(position)
-- Position is position gotten by ray at mouse location, grid size is 1 stud
return Vector3.new(
math.floor(position.X / PlacementUtils.gridSize + 0.5) * PlacementUtils.gridSize,
math.floor(position.Y / PlacementUtils.gridSize + 0.5) * PlacementUtils.gridSize,
math.floor(position.Z / PlacementUtils.gridSize + 0.5) * PlacementUtils.gridSize
)
end
Main code:
-- Casts a ray every frame, targetPart is the part the ray hit, previewObject is the object to position
local objectSpace = Placement.targetPart.CFrame:PointToObjectSpace(raycastResult.Position)
local snapped = PlacementUtils.snapToGrid(objectSpace)
local worldSpace = Placement.targetPart.CFrame:PointToWorldSpace(snapped)
-- Makes the part face properly and rotates the part; rotation step is math.pi / 4
local lookCFrame = CFrame.lookAt(worldSpace, worldSpace + raycastResult.Normal) * CFrame.new(0, 0, -Placement.previewObject.Hitbox.Size.Z * 0.5)
local finalCFrame = lookCFrame * CFrame.Angles(0, 0, Placement.rotation)
Placement.placeCFrame = finalCFrame
Placement.previewObject:PivotTo(finalCFrame)
I read a lot of topics and resources on how to create a system like this to solve my problem but nothing has worked so far. I’m pretty sure this is some mistake I made with my calculations. Can anyone help me figure out what is causing this behavior or fix it?
Your problem is that you use grid for all 3 axises, while you need only 2. Which ones? If it’s sides of X, then you should snap only Y and Z, if it’s Y sides, then snap X and Z, and Z sides should snap X and Y only.
Why everything work ok for first image? because smaller grid = less bugs visible.
If I only include 2, one axis is left un-snapped, I want to include all 3 axis so it snaps on all sides of a part. I also want to have any grid size work. Any ideas as to how I could do this?
Get mouse position relative to part’s CFrame
So you will get Vector3, resembling where mouse look on part.
Get most matching axis from part’s size and mouse absolute relative position * 2.
So, if you will hover over Part with size (10, 20, 30), in most cases it will give you at least 1 axis matching, like (0.1234124, -9.13412, 15), so you can say that now mouse hovering on side related to Z-axis
I say MOST cases, because floating point. It won’t be exactly 15 like in all cases, but 14.999999999999992175239 usually.
You want to place smth to it, and for this you point your mouse on it:
Now, we should check, what side of this part is closest to mouse dot.
local Part = --That part
local Mouse = --mouse instance
--some code related to RunService or Mouse.move
local MouseRelative = (Part.CFrame:Inverse() * CFrame.new(Mouse.Hit.Position)).Position
local SideWeights = Vector3.new(math.abs(MouseRelative.X) * 2 / Part.Size.X, math.abs(MouseRelative.Y) * 2 / Part.Size.Y, math.abs(MouseRelative.Z) * 2 / Part.Size.Z)
--So we got Vector3 with values, representing how close to certain axis
--fases our mouse. If you need get certain side and not axis,
--remove math.abs()
if SideWeights.X > SideWeights.Y and SideWeights.X > SideWeights.Z then
--Mouse pointing on X-axis side
elseif SideWeights.Y > SideWeights.X and SideWeights.Y > SideWeights.Z then
--Mouse pointing on Y-axis side
else
--Mouse pointing on Z-axis side
end
My problem is fixed, I had to change up the mouse part because I use the newer API. I cannot express my gratitude to you for helping me solve this problem and explaining how to code it. Thank you.