Block Placement System Issues

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    What I want to achieve is for the transparent block to slide on top of the surface of whatever part is underneath it.

  2. What is the issue? Include screenshots / videos if possible!
    The issue is that when I set the CanCollide Property of the transparent block to false, the block does not rebound back and forth, which is what I am going for, but the transparent block is partially inside of the part that its on top of. When I set the CanCollide Property of the transparent block to true however, the transparent block begins to rebound back and forth.

Here is a video of what happens when the CanCollide Property of the transparent block is set to false.

As you can see, the transparent block does not rebound back and forth, but it is partially within the part underneath it.

Here is another video but this time the CanCollide Property of the transparent block is set to true.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I have tried making a ray the simulates the built in mouse.Hit.p but haven’t had much progress with figuring out how to do it effectively.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

Here is the script

button = script.Parent
player = game.Players.LocalPlayer
mouse = player:GetMouse()
replicatedstorage = game:GetService("ReplicatedStorage")
userinputservice = game:GetService("UserInputService")
Run = game:GetService("RunService")
raymouseparams = RaycastParams.new()
raymouseparams.FilterType = Enum.RaycastFilterType.Whitelist
raymouseparams.FilterDescendantsInstances = {workspace}

raymouse = workspace:Raycast(mouse.UnitRay.Origin, mouse.Hit.p, raymouseparams)
--[[
endpointraymouse = raymouse.UnitRay.Origin + raymouse.UnitRay.Direction.Magnitude * raymouse.UnitRay.Direction.Unit
]]

local SpawnBlockEnabled = false
local hovering
button.MouseEnter:Connect(function()
	hovering = true
	button.BackgroundColor3 = Color3.fromRGB(184, 184, 184) 
end)
button.MouseLeave:Connect(function()
	hovering = false
	button.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
end)

button.MouseButton1Click:Connect(function()
	if SpawnBlockEnabled == false then
		SpawnBlockEnabled = true
	else if SpawnBlockEnabled == true then
			SpawnBlockEnabled = false
		end
	end
	if SpawnBlockEnabled == true then
		transparentblock = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
		transparentblock.Parent = game.Workspace
		transparentblock.Transparency = .065
		transparentblock.CanCollide = true

			Run.Heartbeat:Connect(function()
				if transparentblock then
					transparentblock.Position = mouse.Hit.p
					transparentblock.Orientation = Vector3.new(0, 0, 0)
				end
			end)
			
		local selectionbox = Instance.new("SelectionBox")
		selectionbox.Parent = transparentblock
		selectionbox.Adornee = transparentblock
	else if SpawnBlockEnabled == false then
			transparentblock:Destroy()
		end
	end
end)

userinputservice.InputBegan:Connect(function(input)
	if hovering == false then
	if SpawnBlockEnabled == true then
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			local cube = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
			cube.Parent = game.Workspace
			cube.Position = mouse.Hit.p
			cube.CanCollide = true
			print("cube spawned")
		end
	end
	end
end)

Maybe try increasing the Y by .5 of what ever the mouse Y is

button = script.Parent
player = game.Players.LocalPlayer
mouse = player:GetMouse()
replicatedstorage = game:GetService("ReplicatedStorage")
userinputservice = game:GetService("UserInputService")
Run = game:GetService("RunService")
raymouseparams = RaycastParams.new()
raymouseparams.FilterType = Enum.RaycastFilterType.Whitelist
raymouseparams.FilterDescendantsInstances = {workspace}

raymouse = workspace:Raycast(mouse.UnitRay.Origin, mouse.Hit.p, raymouseparams)
--[[
endpointraymouse = raymouse.UnitRay.Origin + raymouse.UnitRay.Direction.Magnitude * raymouse.UnitRay.Direction.Unit
]]

local SpawnBlockEnabled = false
local hovering
button.MouseEnter:Connect(function()
	hovering = true
	button.BackgroundColor3 = Color3.fromRGB(184, 184, 184) 
end)
button.MouseLeave:Connect(function()
	hovering = false
	button.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
end)

button.MouseButton1Click:Connect(function()
	if SpawnBlockEnabled == false then
		SpawnBlockEnabled = true
	else if SpawnBlockEnabled == true then
			SpawnBlockEnabled = false
		end
	end
	if SpawnBlockEnabled == true then
		transparentblock = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
		transparentblock.Parent = game.Workspace
		transparentblock.Transparency = .065
		transparentblock.CanCollide = true

			Run.Heartbeat:Connect(function()
				if transparentblock then
					local Height = Mouse.Hit.p.Y + .5
					transparentblock.Position = Vector3.new(mouse.Hit.p.X, Height, mouse.Hit.p.Z)
					transparentblock.Orientation = Vector3.new(0, 0, 0)
				end
			end)
			
		local selectionbox = Instance.new("SelectionBox")
		selectionbox.Parent = transparentblock
		selectionbox.Adornee = transparentblock
	else if SpawnBlockEnabled == false then
			transparentblock:Destroy()
		end
	end
end)

userinputservice.InputBegan:Connect(function(input)
	if hovering == false then
	if SpawnBlockEnabled == true then
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			local cube = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
			cube.Parent = game.Workspace
			cube.Position = mouse.Hit.p
			cube.CanCollide = true
			print("cube spawned")
		end
	end
	end
end)

I’m not very expericed with this but i think that should work. All i updated is

                 if transparentblock then
					transparentblock.Position = mouse.Hit.p
					transparentblock.Orientation = Vector3.new(0, 0, 0)
				end

To

				if transparentblock then
					local Height = Mouse.Hit.p.Y + .5
					transparentblock.Position = Vector3.new(mouse.Hit.p.X, Height, mouse.Hit.p.Z)
					transparentblock.Orientation = Vector3.new(0, 0, 0)
				end

Adding .5 to the Y Axis of the transparent block’s position adjusted the height in the Y axis, but if I place the transparent block horizontally beside a part, it is still partially inside of it. Is there an effective approach for detecting the surface of the part that the transparent block will be placed upon?

Now detecting the side of a part I would not know. Possibly try and check whether it is on the X or Z axis of the block and try to change it accordingly

You can get the surface normal of the object hit by the mouse using a raycast,

local block = --The block
local params = RaycastParams.new()
params.FilterDescendantsInstances = {block}

local rayStart = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.Position --The position of the camera the client is using
local rayDirection = mouse.UnitRay.Direction

local rayResult = game.Workspace:Raycast(rayStart, rayDirection)
if rayResult then
    local normal = rayResult.Normal
    local instance = rayResult.Instance
    local pos = rayResult.Position

    local setPos = pos + (normal * (block.Size/2))
    local setCFrame = CFrame.new(setPos.X, setPos.Y, setPos.Z) * CFrame.Angles(0, 0, 0) -- Add rotation if you want
    
    block.CFrame = setCFrame
end
1 Like

When I attempt to print the position of the rayResult, it gives me this error:

  23:38:54.314 - Players.blitzjakeem.PlayerGui.ScreenGui.BlockSpa.Test Script:57: attempt to index nil with 'Position'
23:38:54.315 - Stack Begin
23:38:54.317 - Script 'Players.blitzjakeem.PlayerGui.ScreenGui.BlockSpa.Test Script', Line 57
23:38:54.317 - Stack End

I also configured some of the code to increase the offset between the transparent block and the part it is being placed on by .5 of the X, Y, and Z components of the transparent block’s size.

Here is the script:

button = script.Parent
player = game.Players.LocalPlayer
mouse = player:GetMouse()
replicatedstorage = game:GetService("ReplicatedStorage")
userinputservice = game:GetService("UserInputService")
Run = game:GetService("RunService")
raymouseparams = RaycastParams.new()
raymouseparams.FilterType = Enum.RaycastFilterType.Whitelist
raymouseparams.FilterDescendantsInstances = {workspace}


raymouse = workspace:Raycast(mouse.UnitRay.Origin, mouse.Hit.p, raymouseparams)
--[[
endpointraymouse = raymouse.UnitRay.Origin + raymouse.UnitRay.Direction.Magnitude * raymouse.UnitRay.Direction.Unit
]]

local SpawnBlockEnabled = false
local hovering
button.MouseEnter:Connect(function()
	hovering = true
	button.BackgroundColor3 = Color3.fromRGB(184, 184, 184) 
end)
button.MouseLeave:Connect(function()
	hovering = false
	button.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
end)

button.MouseButton1Click:Connect(function()
	if SpawnBlockEnabled == false then
		SpawnBlockEnabled = true
	else if SpawnBlockEnabled == true then
			SpawnBlockEnabled = false
		end
	end
	if SpawnBlockEnabled == true then
		transparentblock = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
		transparentblock.Parent = game.Workspace
		
		local block = transparentblock
		local params = RaycastParams.new()
		params.FilterDescendantsInstances = {block}
		
		local rayStart = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.Position --The position of the camera the client is using
		local rayDirection = mouse.UnitRay.Direction

		local rayResult = game.Workspace:Raycast(rayStart, rayDirection)
		if rayResult then
			local normal = rayResult.Normal
			local instance = rayResult.Instance
			local pos = rayResult.Position
			local offsetX = pos.X + (block.Size.X * .5)

			setPos = Vector3.new((pos.X + (block.Size.X * .5)), (pos.Y + (block.Size.Y * .5)), (pos.Z + (block.Size.Z * .5)))
			setCFrame = CFrame.new(setPos.X, setPos.Y, setPos.Z) * CFrame.Angles(0, 0, 0) -- Add rotation if you want
			block.Position = Vector3.new(setCFrame.X, setCFrame.Y, setCFrame.Z)
		end
		print(rayResult.Position)
		transparentblock.Transparency = .065
		transparentblock.CanCollide = false

		Run.Heartbeat:Connect(function()
			if transparentblock then
				local veroffs = mouse.Hit.p.Y + .5
				transparentblock.Position = Vector3.new(setPos.X, setPos.Y, setPos.Z)
				transparentblock.Orientation = Vector3.new(0, 0, 0)
			end
		end)

		local selectionbox = Instance.new("SelectionBox")
		selectionbox.Parent = transparentblock
		selectionbox.Adornee = transparentblock
	else if SpawnBlockEnabled == false then
			transparentblock:Destroy()
		end
	end
end)

userinputservice.InputBegan:Connect(function(input)
	if hovering == false then
		if SpawnBlockEnabled == true then
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local cube = replicatedstorage.PartFolder["1x1x1"]:WaitForChild("1Cube"):Clone()
				cube.Parent = game.Workspace
				cube.Position = mouse.Hit.p
				cube.CanCollide = true
				print("cube spawned")
			end
		end
	end
end)

And this is the part that is giving me the error:

		
		local block = transparentblock
		local params = RaycastParams.new()
		params.FilterDescendantsInstances = {block}
		
		local rayStart = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.Position --The position of the camera the client is using
		local rayDirection = mouse.UnitRay.Direction

		local rayResult = game.Workspace:Raycast(rayStart, rayDirection)
		if rayResult then
			local normal = rayResult.Normal
			local instance = rayResult.Instance
			local pos = rayResult.Position
			local offsetX = pos.X + (block.Size.X * .5)

			setPos = Vector3.new((pos.X + (block.Size.X * .5)), (pos.Y + (block.Size.Y * .5)), (pos.Z + (block.Size.Z * .5)))
			setCFrame = CFrame.new(setPos.X, setPos.Y, setPos.Z) * CFrame.Angles(0, 0, 0) -- Add rotation if you want
			block.Position = Vector3.new(setCFrame.X, setCFrame.Y, setCFrame.Z)
		end
		print(rayResult.Position)
		transparentblock.Transparency = .065
		transparentblock.CanCollide = false

To recap, when I attempt to print the Position of the rayResult, it throws me this error:

  23:38:54.314 - Players.blitzjakeem.PlayerGui.ScreenGui.BlockSpa.Test Script:57: attempt to index nil with 'Position'
23:38:54.315 - Stack Begin
23:38:54.317 - Script 'Players.blitzjakeem.PlayerGui.ScreenGui.BlockSpa.Test Script', Line 57
23:38:54.317 - Stack End

do not print the position outside the for loop, since rayresult will be nil if it didn’t hit anything

I don’t understand why people is overcomplicating everything.
Just do
Height = Mouse.Hit.Position + Block.Size.Y/2

This will cause the block to look normal when placed on a surface, but it will clip through the object when placed on the side of a surface or the bottom of a surface.

They need to use the surface normal returned from the raycast to determine the updated position.

1 Like

He wants to be able to place it on the sides without it clipping through as well.

2 Likes

Ah sorry lol.
Didn’t knew!!..

You could probably get the part normal via raycasting, and then offset positioning from there.

Greetings again. So I have be looking for different ways to allow the “transparent block” to be placed on “On Top” of any face of a “part”. At the moment, the “transparent block” is partially clipping through “parts” when the “transparent block” is not being placed in the positive y-axis relative to the "part."

Here is a video of the results

Here is the Script

button = script.Parent
player = game.Players.LocalPlayer
mouse = player:GetMouse()
replicatedstorage = game:GetService("ReplicatedStorage")
partfolder = replicatedstorage:WaitForChild("PartFolder")
userinputservice = game:GetService("UserInputService")
Run = game:GetService("RunService")

local SpawnBlockEnabled = false
local hovering
button.MouseEnter:Connect(function()
	hovering = true
	button.BackgroundColor3 = Color3.fromRGB(184, 184, 184) 
end)
button.MouseLeave:Connect(function()
	hovering = false
	button.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
end)

button.MouseButton1Click:Connect(function()
--Debounces
	if SpawnBlockEnabled == false then
		SpawnBlockEnabled = true
	else if SpawnBlockEnabled == true then
			SpawnBlockEnabled = false
		end
	end
	
				--[[
					--Surface Detector
						function findSide(part)
							 cf = part.CFrame
							 cs = {side = nil,y = -2}
							if cf.lookVector.Y > cs.y then cs.side = "FrontSurface" cs.y = cf.lookVector.Y end
							if -cf.lookVector.Y > cs.y then cs.side = "BackSurface" cs.y = -cf.lookVector.Y end
							if cf.rightVector.Y > cs.y then cs.side = "RightSurface" cs.y = cf.rightVector.Y end
							if -cf.rightVector.Y > cs.y then cs.side = "LeftSurface" cs.y = -cf.rightVector.Y end
							if cf.upVector.Y > cs.y then cs.side = "TopSurface" cs.y = cf.upVector.Y end
							if -cf.upVector.Y > cs.y then cs.side = "BottomSurface" cs.y = -cf.upVector.Y end
							return cs.side
						end
						--findSide will return the part's side which is facing up, as a string
					]]
				
					--[[
							local function GetNormal(part, normalId)
								normal = part.CFrame * Vector3.FromNormalId(normalId) - part.Position
								return normal
							end
						]]
				
--Raycasts
	if SpawnBlockEnabled == true then
		transparentblock = replicatedstorage.PartFolder["2x2x2"]:WaitForChild("2Cube"):Clone()
		transparentblock.Parent = game.Workspace
		mouse.TargetFilter = transparentblock
		
		local function filter(number, increment)
			number = math.floor(number)
			number = number - (number%increment)
			return number
		end
		Increment = .5
		
		block = transparentblock
		params = RaycastParams.new()
		params.FilterDescendantsInstances = {}
		rayStart = game.Workspace.CurrentCamera.CFrame.Position
		rayDirection = mouse.Target.Position - rayStart
		rayResult = workspace:Raycast(rayStart, rayDirection.Unit * 100, params)

			--normal = rayResult.Normal
			--target = mouse.Target
			--int = rayResult.Instance
			
	if rayResult then	
	--block positioning
		Run.Heartbeat:Connect(function()
			if block then	
				block.CFrame = CFrame.new(filter(mouse.Hit.p.X,Increment), filter(mouse.Hit.p.Y + (block.Size.Y/2), Increment), filter(mouse.Hit.p.Z,Increment))				
				print(rayResult.Normal)								
			end
		end)
	end
	---
---
		block.Transparency = .065
		block.CanCollide = false
	--selection box
		local selectionbox = Instance.new("SelectionBox")
		selectionbox.Parent = block
		selectionbox.Adornee = block
	---
	else if SpawnBlockEnabled == false then
			block:Destroy()
		end
	end
end)

userinputservice.InputBegan:Connect(function(input)
	if hovering == false then
		if SpawnBlockEnabled == true then
			if input.UserInputType == Enum.UserInputType.MouseButton1 then
				local cube = replicatedstorage.PartFolder["2x2x2"]:WaitForChild("2Cube"):Clone()
				cube.Parent = game.Workspace.PlotBlocks
				cube.CFrame = block.CFrame
				cube.CanCollide = true
				cube.Anchored = true
				print("cube spawned")
			end
		end
	end
end) 

Any input that leads to a solution to this situation is much appreciated!