I’m having trouble with an item placement rendering-preview mechanism — the sides of the item trying to be placed are overlapping with other parts. As of right now, the item shouldn’t be able to be placed directly against a vertical surface (wall, terrain, etc.) but it should not render the item through a part regardless.
The issue looks like this in live preview render in live play:
And the goal is to make it look like the green part on the right (see illustration below)
The source code right now is as follows
-- this function below is binded to render step in another script, this code below is where the bug symptom is at
function RenderPlacement(placementObject: Model)
Mouse.TargetFilter = placementObject -- set mouse target filter to the placement object so that it doesn't collide with itself
self:SetPlacementColor(placementObject, self.BLUEPRINT_COLOR, Enum.Material.ForceField)
if Mouse.Target then
local rootPosition = Character.HumanoidRootPart.Position
local difference = rootPosition - Mouse.Hit.Position
local distance = math.clamp(difference.Magnitude, self.MIN_DISTANCE, self.MAX_DISTANCE)
local newMouseHit = -(difference.Unit * distance) + rootPosition
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = { placementObject, Character, Mouse }
-- cast our ray and get our normal, which is a unit vector
local ray = Ray.new(placementObject.PrimaryPart.Position, Vector3.new(0, -1, 0) * 100)
local result = workspace:Raycast(ray.Origin, ray.Direction, raycastParams)
if result then
local normal = result.Normal
-- get the perpendicular vector and the angle between the two
local cross = Vector3.new(0, 1, 0):Cross(normal)
local theta = math.acos(Vector3.new(0, 1, 0):Dot(normal))
-- make sure our axis is valid
local axis = Vector3.new(1, 0, 0)
if cross.Magnitude > 0 then
axis = cross.Unit
end
-- get the model's size and cframe
local size, orientation = placementObject:GetBoundingBox()
if distance <= self.MIN_DISTANCE then
self:SetPlacementColor(placementObject, self.ERROR_COLOR, Enum.Material.ForceField)
placementObject:PivotTo(
CFrame.new(newMouseHit + normal * orientation.Y / 2)
* CFrame.new(0, 0, 0)
* CFrame.Angles(0, math.rad(Character:WaitForChild("HumanoidRootPart").Orientation.Y), 0)
* CFrame.Angles(0, math.rad(self.placementInfo.rotateValue), 0)
)
self.placementInfo.cFrame = placementObject:GetPivot()
return
end
if distance >= self.MAX_DISTANCE then
-- Don't rotate the model to terrain surface
self:SetPlacementColor(placementObject, self.ERROR_COLOR, Enum.Material.ForceField)
self.placementInfo.canPlace = false
normal = Vector3.new(0, 1, 0)
placementObject:PivotTo(
CFrame.new(newMouseHit + normal * orientation.Y / 2)
* CFrame.new(0, 0, 0)
* CFrame.Angles(0, math.rad(Character.HumanoidRootPart.Orientation.Y), 0)
* CFrame.Angles(0, math.rad(self.placementInfo.rotateValue), 0)
)
self.placementInfo.cFrame = placementObject:GetPivot()
return
end
-- CFrame the model
self.placementInfo.canPlace = true
self.placementInfo.cFrame = placementObject:GetPivot()
placementObject:PivotTo(
CFrame.new(newMouseHit + normal * orientation.Y / 2)
* CFrame.fromAxisAngle(axis, theta):ToWorldSpace(
CFrame.new(0, 0, 0)
* CFrame.Angles(0, math.rad(Character:WaitForChild("HumanoidRootPart").Orientation.Y), 0)
* CFrame.Angles(0, math.rad(self.placementInfo.rotateValue), 0)
)
)
-- check for collision with other objects, if there is a collision then prevent item from being placed
local hitbox = placementObject:FindFirstChild("Hitbox")
local overlapParams = OverlapParams.new()
overlapParams.FilterType = Enum.RaycastFilterType.Exclude
overlapParams.FilterDescendantsInstances = { hitbox, placementObject }
local overlappingParts = workspace:GetPartBoundsInBox(hitbox.CFrame, hitbox.Size, overlapParams)
if #overlappingParts > 0 then
for _, overlappingPart: BasePart in pairs(overlappingParts) do
if overlappingPart then
self:SetPlacementColor(placementObject, self.ERROR_COLOR, Enum.Material.ForceField)
self.placementInfo.canPlace = false
end
end
end
end
else
-- Mouse does not have a target, set the model to the mouse position in relation to the max distance allowed
self:SetPlacementColor(placementObject, self.ERROR_COLOR, Enum.Material.ForceField)
self.placementInfo.canPlace = false
local rootPosition = Character.HumanoidRootPart.Position
local difference = rootPosition - Mouse.Hit.Position
local distance = math.clamp(difference.Magnitude, self.MIN_DISTANCE, self.MAX_DISTANCE)
local newMouseHit = -(difference.Unit * distance) + rootPosition
placementObject:PivotTo(
CFrame.new(newMouseHit + Vector3.new(0, 1, 0) * placementObject:GetExtentsSize().Y / 2)
* CFrame.new(0, 0, 0)
* CFrame.Angles(0, math.rad(Character:WaitForChild("HumanoidRootPart").Orientation.Y), 0)
* CFrame.Angles(0, math.rad(self.placementInfo.rotateValue), 0)
)
self.placementInfo.cFrame = placementObject:GetPivot()
return
end
end