I have a working placement system that has a grid which isn’t fully functional
Basically I take model.PrimaryPart.Position to check if it’s inside the plot, however this takes the model’s very center and you are still able to place even if the model is out of bounds and this is what happens:
I think you should explain more about the approach you are using for this placement system.
Raycasting?
In those pictures, its based on the wall or based on the floor?
You tried using an offset substracting the half of the size of the part you are placing or the size of the wall?
Ok so this is currently what I’ve tried and works pretty well except for positive Z axis ?
(PlotBounds.Negative and PlotBounds.Positive are Vector3Values)
local modelSize = currentModel:GetExtentsSize()
local bottomLeftCorner = currentModel.PrimaryPart.Position - Vector3.new(modelSize.X / 2, 0, modelSize.Z / 2)
local bottomRightCorner = currentModel.PrimaryPart.Position - Vector3.new(-modelSize.X / 2, 0, modelSize.Z / 2)
local topLeftCorner = currentModel.PrimaryPart.Position + Vector3.new(modelSize.X / 2, 0, -modelSize.Z / 2)
local topRightCorner = currentModel.PrimaryPart.Position + Vector3.new(-modelSize.X / 2, 0, -modelSize.Z / 2)
if bottomLeftCorner.X < PlotBounds.Negative.Value.X or
bottomLeftCorner.Z < PlotBounds.Negative.Value.Z or
bottomRightCorner.X > PlotBounds.Positive.Value.X or
bottomRightCorner.Z > PlotBounds.Positive.Value.Z or
topLeftCorner.X < PlotBounds.Negative.Value.X or
topLeftCorner.Z > PlotBounds.Positive.Value.Z or
topRightCorner.X > PlotBounds.Positive.Value.X or
topRightCorner.Z > PlotBounds.Positive.Value.Z then
canPlace = false
else
canPlace = true
end
Read my post please. It’s based on the model.PrimaryPart.Position which is the center of the model and it’s checked if it’s within the bounds of the plot with Vector3
Yes, especially if you want it to work no matter how the parts are rotated (although still at 90 degree intervals):
local DOT_TO_DIAGONAL = Vector3.xAxis:Dot((Vector3.xAxis + Vector3.yAxis + Vector3.zAxis).Unit)
--[[
Given a part and a world face (or a direction in world space, represented by a NormalId), return the face
of the part that most closely corresponds to that world face.
]]
function getPartFace(part: BasePart, worldFace: Enum.NormalId): Enum.NormalId
local worldNormal = Vector3.FromNormalId(worldFace)
for _, partFace in ipairs(Enum.NormalId:GetEnumItems()) do
local partNormal = part.CFrame:VectorToWorldSpace( Vector3.FromNormalId(partFace) )
local isClosest = partNormal:Dot(worldNormal) >= DOT_TO_DIAGONAL
if isClosest then
return partFace
end
end
error() --Unreachable
end
--[[
Given a part and one of it's faces, return the center of the face of that part.
]]
function getPartFaceCenter(part: BasePart, face: Enum.NormalId): Vector3
local objectTranslation = Vector3.fromNormalId(face) * part.Size * 0.5
return part.CFrame * objectTranslation
end
--[[
Checks if the inner part is fully contained by the outer part.
Works for parts that are axis-aligned or rotated at right angles to the axes.
]]
function isPartFullyInPart(outer: Part, inner: Part): boolean
local outerCF, innerCF = outer.CFrame, inner.CFrame
for _, worldFace in ipairs(Enum.NormalId:GetEnumItems()) do
local outerFace = getPartFace(outer, worldFace)
local innerFace = getPartFace(inner, worldFace)
local faceAxis = Vector3.FromNormalId(outerFace)
local outerDisplacementOnAxis = ((getPartFaceCenter(outer, outerFace) - outerCF.Position) * faceAxis):Dot(faceAxis)
local innerDisplacementOnAxis = ((getPartFaceCenter(inner, innerFace) - outerCF.Position) * faceAxis):Dot(faceAxis)
local outsideOnAxis = math.abs(innerDisplacementOnAxis) > math.abs(outerDisplacementOnAxis)
local sameDirection = math.sign(outerDisplacementOnAxis) == math.sign(innerDisplacementOnAxis)
if sameDirection and outsideOnAxis then
return false
end
end
return true
end
It essentially does the same as your math, but since the parts might be rotated it can’t know if it should compare (X to X) or (X to Y) or (X to Z) and whatever other combinations are possible. So it uses some vector math instead to figure out which face on each part corresponds to each of the directions. And it uses GetEnumItems to loop over all 6 directions.