Room Placement Issues

Hello, fellow DevForum users. It appears that I am having an issue with my Room Placement local script, it is yet to be finished, however, there are issues that I cannot figure out how to fix.

Issues:

  1. Placement has issues when moving the end point to a negative X or Z axis.
  2. The preview is slightly elevated when placing the end point.

Here is the source code for anyone interested.

local uis = game:GetService("UserInputService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local runs = game:GetService("RunService")
local players = game:GetService("Players")
local camera = workspace.CurrentCamera

local placeBut = script.Parent.Place
local start
local ending
local preview

local isPlacingRoom = false

local region = nil
local player = players.LocalPlayer
local mouse = player:GetMouse()

local function snapToGrid(pos:Vector3)
	local x = math.floor(pos.X / 4) * 4
	local y = math.floor(pos.Y / 4) * 4
	local z = math.floor(pos.Z / 4) * 4
	
	return Vector3.new(x,y,z)
end

placeBut.MouseButton1Click:Connect(function()
	if preview == nil and start == nil and ending == nil then
		preview = Instance.new("Part")
		preview.Transparency = .5
		preview.CanCollide = false
		preview.Size = Vector3.new(1,8,1)
		preview.Anchored = true
		preview.BrickColor = BrickColor.new("Bright green")
		preview.Parent = camera
		preview.Material = Enum.Material.SmoothPlastic
		isPlacingRoom = true
	end
end)

runs.RenderStepped:Connect(function()
	if start == nil and preview ~= nil and isPlacingRoom then
		local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
			mouse.UnitRay.Origin,
			mouse.UnitRay.Direction*10000
			),
			{
				player.Character,
				preview,
			},
			true,
			true
		)
		if hit then
			local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
			preview.CFrame = CFrame.new(preview.Position:Lerp(snapped,.4))*CFrame.Angles(math.rad((preview.Position-snapped).Magnitude),0,0)
		end
	elseif start ~= nil and ending == nil and isPlacingRoom then
		local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
			mouse.UnitRay.Origin,
			mouse.UnitRay.Direction*10000
			),
			{
				player.Character,
				preview,
			},
			true,
			true
		)
		if hit then
			local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
			if snapped ~= start then
				region = Region3.new(start,snapped+Vector3.new(0,8,0))
				preview.Size = preview.Size:Lerp(region.Size,.25)
				preview.CFrame = preview.CFrame:Lerp(region.CFrame,.25)
			end
		end
	end
end)

uis.InputBegan:Connect(function(iput)
	if iput.KeyCode == Enum.KeyCode.Q then
		if preview then
			preview:Destroy()
			preview = nil
			isPlacingRoom = false
			start = nil
			ending = nil
		end
	end
	if iput.UserInputType == Enum.UserInputType.MouseButton1 then
		if start == nil and preview ~= nil and isPlacingRoom then
			local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
				mouse.UnitRay.Origin,
				mouse.UnitRay.Direction*10000
				),
				{
					player.Character,
					preview,
				},
				true,
				true
			)
			if hit then
				local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
				start = snapped
			end
		elseif start ~= nil and ending == nil and isPlacingRoom then
			preview:Destroy()
			preview = nil
			isPlacingRoom = false
			start = nil
			ending = nil
		end
	end
end)

Any help is greatly appreciated.

3 Likes

the problem lies in the snapToGrid function. It doesn’t handle negative values correctly. To fix this, you can modify the function like this:

local function snapToGrid(pos: Vector3)
    local x = math.floor(pos.X / 4) * 4
    local y = math.floor(pos.Y / 4) * 4
    local z = math.floor(pos.Z / 4) * 4
    return Vector3.new(x, y, z)
end

This change ensures that the position is snapped correctly, regardless of whether it’s positive or negative.
2. preview slightly elevated when placing the end point: the problem miiight occur because you’re adding Vector3.new(0, preview.Size.Y/2, 0) to the position when calculating the snapped position. This causes the preview to be slightly elevated. Update the line to local snapped = snapToGrid(pos - hit.Position) + hit.Position in both the RenderStepped and InputBegan event handlers.

1 Like

Could you rewrite the function to work, I used Roblox’s AI to generate it (as in the snap to grid function, the rest was made by me)?

sure i guess, hope it works

local uis = game:GetService("UserInputService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local runs = game:GetService("RunService")
local players = game:GetService("Players")
local camera = workspace.CurrentCamera

local placeBut = script.Parent.Place
local start
local ending
local preview

local isPlacingRoom = false

local region = nil
local player = players.LocalPlayer
local mouse = player:GetMouse()

local function snapToGrid(pos: Vector3)
    local x = math.floor(pos.X / 4) * 4
    local y = math.floor(pos.Y / 4) * 4
    local z = math.floor(pos.Z / 4) * 4
    return Vector3.new(x, y, z)
end

placeBut.MouseButton1Click:Connect(function()
    if preview == nil and start == nil and ending == nil then
        preview = Instance.new("Part")
        preview.Transparency = .5
        preview.CanCollide = false
        preview.Size = Vector3.new(1, 8, 1)
        preview.Anchored = true
        preview.BrickColor = BrickColor.new("Bright green")
        preview.Parent = camera
        preview.Material = Enum.Material.SmoothPlastic
        isPlacingRoom = true
    end
end)

runs.RenderStepped:Connect(function()
    if start == nil and preview ~= nil and isPlacingRoom then
        local hit, pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
            mouse.UnitRay.Origin,
            mouse.UnitRay.Direction * 10000
        ),
        {
            player.Character,
            preview,
        },
        true,
        true
        )
        if hit then
            local snapped = snapToGrid(pos - hit.Position) + hit.Position
            preview.CFrame = CFrame.new(preview.Position:Lerp(snapped, .4)) * CFrame.Angles(math.rad((preview.Position - snapped).Magnitude), 0, 0)
        end
    elseif start ~= nil and ending == nil and isPlacingRoom then
        local hit, pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
            mouse.UnitRay.Origin,
            mouse.UnitRay.Direction * 10000
        ),
        {
            player.Character,
            preview,
        },
        true,
        true
        )
        if hit then
            local snapped = snapToGrid(pos - hit.Position) + hit.Position
            if snapped ~= start then
                region = Region3.new(start, snapped + Vector3.new(0, 8, 0))
                preview.Size = preview.Size:Lerp(region.Size, .25)
                preview.CFrame = preview.CFrame:Lerp(region.CFrame, .25)
            end
        end
    end
end)

uis.InputBegan:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.Q then
        if preview then
            preview:Destroy()
            preview = nil
            isPlacingRoom = false
            start = nil
            ending = nil
        end
    end
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        if start == nil and preview ~= nil and isPlacingRoom then
            local hit, pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
                mouse.UnitRay.Origin,
                mouse.UnitRay.Direction * 10000
            ),
            {
                player.Character,
                preview,
            },
            true,
            true
            )
            if hit then
                local snapped = snapToGrid(pos - hit.Position) + hit.Position
                start = snapped
            end
        elseif start ~= nil and ending == nil and isPlacingRoom then
            preview:Destroy()
            preview = nil
            isPlacingRoom = false
            start = nil
            ending = nil
        end
    end
end)

When you say “Placement has issues when moving the end point to a negative X or Z axis” do you mean only when crossing over from - to + x or z, or do you mean the placement goes to a different location when you try to place it in a - value?

I don’t know if it’ll help, but try using math.round instead of math.floor. It would make the placement system snap more centered on the mouse grid rather than snapping the room to one corner of the mouse grid when you move the mouse over the grid?

1 Like

This solution did not work, I will provide a video of the issue with the code unedited shortly.

alright, thatll help, also read the other reply

1 Like

This should help, and also clear up the other reply.

Ah, so not really a grid placement system, more of a region placement system then.

Whatever’s going on isn’t firing your snapToGrid function because the placement box is picking whatever first spot and resizing from there but not in 4 stud increments.

theres also something that has to do with negatives where if you go to resize the other side it doesnt because i believe roblox simply doesnt allow negative sizes, there must be an adaptation in which the part angle is updated so it keeps using positives to change the size

Kind of both, I’m trying to also use the script for placing normal objects, like potted plants, and seats.

I believe so, I tried checking the magnitude and swapping the start with the end, however, that did not seem to work, which is exactly why I am asking on the DevForum(s).

I have created a very simple work around, here is the source code if someone in the future is having the same issue:

local uis = game:GetService("UserInputService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local runs = game:GetService("RunService")
local players = game:GetService("Players")
local camera = workspace.CurrentCamera

local placeBut = script.Parent.Place
local start
local ending
local preview

local isPlacingRoom = false

local player = players.LocalPlayer
local mouse = player:GetMouse()

local function snapToGrid(pos:Vector3)
	local x = math.floor(pos.X / 4) * 4
	local y = math.floor(pos.Y / 4) * 4
	local z = math.floor(pos.Z / 4) * 4
	
	return Vector3.new(x,y,z)
end

placeBut.MouseButton1Click:Connect(function()
	if preview == nil and start == nil and ending == nil then
		preview = Instance.new("Part")
		preview.Transparency = .5
		preview.CanCollide = false
		preview.Size = Vector3.new(1,8,1)
		preview.Anchored = true
		preview.BrickColor = BrickColor.new("Bright green")
		preview.Parent = camera
		preview.Material = Enum.Material.SmoothPlastic
		isPlacingRoom = true
	end
end)

runs.RenderStepped:Connect(function()
	if start == nil and preview ~= nil and isPlacingRoom then
		local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
			mouse.UnitRay.Origin,
			mouse.UnitRay.Direction*10000
			),
			{
				player.Character,
				preview,
			},
			true,
			true
		)
		if hit then
			local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
			preview.CFrame = CFrame.new(preview.Position:Lerp(snapped,.4))*CFrame.Angles(math.rad((preview.Position-snapped).Magnitude),0,0)
		end
	elseif start ~= nil and ending == nil and isPlacingRoom then
		local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
			mouse.UnitRay.Origin,
			mouse.UnitRay.Direction*10000
			),
			{
				player.Character,
				preview,
			},
			true,
			true
		)
		if hit then
			local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
			if snapped ~= start then
				local cframe = CFrame.new(start:Lerp(snapped,.5))
				local x = start.X-snapped.X
				if x < 0 then
					x -= (x*2)
				end
				local z = start.Z-snapped.Z
				if z < 0 then
					z -= (z*2)
				end
				local size = Vector3.new(x,8,z)
				preview.Size = preview.Size:Lerp(size,.25)
				preview.CFrame = preview.CFrame:Lerp(cframe,.25)
			end
		end
	end
end)

uis.InputBegan:Connect(function(iput)
	if iput.KeyCode == Enum.KeyCode.Q then
		if preview then
			preview:Destroy()
			preview = nil
			isPlacingRoom = false
			start = nil
			ending = nil
		end
	end
	if iput.UserInputType == Enum.UserInputType.MouseButton1 then
		if start == nil and preview ~= nil and isPlacingRoom then
			local hit,pos = workspace:FindPartOnRayWithIgnoreList(Ray.new(
				mouse.UnitRay.Origin,
				mouse.UnitRay.Direction*10000
				),
				{
					player.Character,
					preview,
				},
				true,
				true
			)
			if hit then
				local snapped = snapToGrid(pos+Vector3.new(0,preview.Size.Y/2,0)-hit.Position)+hit.Position
				start = snapped
			end
		elseif start ~= nil and ending == nil and isPlacingRoom then
			preview:Destroy()
			preview = nil
			isPlacingRoom = false
			start = nil
			ending = nil
		end
	end
end)

Footage of the script working:

Fix:
I accidently added +8 studs on the Y for the elevation issue and I did some pretty basic math for the sizing and positioning, source code for math is above.

Did it fix your negative value issue? The first video showed the original placement Part going to 0 but only moving after that. The new video doesn’t show the mouse going back past the original placement location.

I forgot to include that in the video, but it did fix my negative size issue by inverting the size on the X and Z if it was less than zero.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.