Trying to create a wall placement

I am currently trying to create a wall placement system. I am using this to kinda get an idea of what to do, but I want to really code it myself. I am aiming for the same thing that bloxburg has or the sims when it comes to wall placement.

I am trying to also keep everything modular, so it’s easy to access and find.

This what I have for the module so far:

local drawWall = {}

local replicatedStorage = game:GetService('ReplicatedStorage')
local runService = game:GetService('RunService')

local remotes = replicatedStorage:WaitForChild('Remotes')
local placing = remotes:WaitForChild('Placing')

function roundVector(vector, unit)
return vector - Vector3.new(vector.X%unit, 0, vector.Z%unit)
end

function drawWall:StartPlacingWall(player, mouse)
	local playersPlot = workspace.Plots:FindFirstChild(player.Name)
	if playersPlot then
		local renderStepped
		
		local model = Instance.new('Model', playersPlot.Objects.Walls)
		model.Name = 'Wall'
		
		local pole1 = Instance.new('Part', model)
		pole1.Name = 'Pole1'
		pole1.Anchored = true
		pole1.Transparency = 0.2
		pole1.Material = Enum.Material.SmoothPlastic
		pole1.Shape = 'Cylinder'
		pole1.Size = Vector3.new(10, 0.4, 0.4)
		
		local renderStepped = runService.RenderStepped:connect(function()
			local mouseP = mouse.Hit.p
			pole1.CFrame = CFrame.new(roundVector(mouseP + Vector3.new(0, pole1.Size.X/2, 0), 2) + Vector3.new(0.2,0,0.5))*CFrame.Angles(0, 0, math.rad(90))
		end)
	end
end

return drawWall

So when they click a button it starts this up, and the pole object is created, and moves with the mouse. First problem I am encountering is that it can be moved off of the players base platform. It also does not align with the grid of the base part either.


Kinda hard to see it but it’s definately not inline with the grid (should be on every intersection) the grid sizes are 1x1 too.

Next problem is when I click cancel it doesn’t go away. Local script provided below

var.Build.Activated:Connect(function()
	var.Editing = true
	
	humanoid.WalkSpeed = 0
	humanoid.JumpPower = 0
	
	var.Build.Label.Text = 'Close'
	
	if var.Editing and not var.PlacingItem then
		var.PlacingItem = true
		local drawWall = require(draw.DrawWall)
		drawWall:StartPlacingWall(player, mouse)
	else
		var.Editing = false
		var.PlacingItem = false
		
		humanoid.WalkSpeed = 16
		humanoid.JumpPower = 50
		
		var.Build.Label.Text = 'Build'
	end
end)

Any help would be greatly appreciated! I’ve go over the code for the place linked above and tried it in my place, but the wall parts glitch out and I really want to try coding it in a way that I can read, as the sample code I found too confusing.

6 Likes

To get the thing to snap to the grid, you can round the components of the position vector to the nearest S, where S is the size of the grid cells you want. For example, if p is the world position of where the mouse is pointing, you could get a “snapped” version of it like so:

local gridSize = 4

function round(number, to)
	--Takes two numbers and returns the first (number) rounded to the nearest multiple of (to).
	--E.g. round(1.79, 1) would return 2, because 2 is the closest multiple of (to) to (number)  
	return math.floor(number/to + 0.5) * to
end

--The position of the mouse, with the X and Y components rounded to the nearest 4
local p_snapped = Vector3.new(
	round(p.X, gridSize),
	p.Y,
	round(p.Z, gridSize)
)

I did some editing around and managed to figure it out changing a few things around on this line:

pole1.CFrame = CFrame.new(roundVector(mouseP + Vector3.new(0, pole1.Size.X/2, 0), 1))*CFrame.Angles(0, 0, math.rad(90))

It references this function too:

function roundVector(vector, unit)
    return vector - Vector3.new(vector.X%unit, 0, vector.Z%unit)
end

which I have no clue what this function actually does

In Lua, ‘%’ is the modulus operator. This is basically just taking the remainder of (num on left) / (num on right). For example, 10%3 = 1 because the nearest number divisible by 3 is 9, so there is 1 as a remainder since 10-9 = 1. It also handles decimal places, so if you do 10.5%3, you would get 1.5.

Think of roundVector as a "math.floor" for vectors. If we wanted to implement math.floor ourselves, we could do this:

function floor(num)
    return num - num%1 -- num%1 will just return the decimal part of the number
end

So basically the function takes the actual number and "takes away" the decimal part.
roundVector is essentially doing the same thing, except it is not doing anything with the Y component.

The problem with this is that it will only round down, just like math.floor only rounds down.
@ThanksRoBama posted how to snap it to the grid using a round function so that your object will snap to the closest grid. Here would be a better roundVector function for this case:

function round(number, to)
	to = to or 1
	return math.floor(number/to + 0.5) * to
end

function roundVector(vector, unit)-- without affecting y though. and unit should be grid size
	return Vector3.new(round(vector.X, 1/unit), vector.Y, round(vector.Z, 1/unit))
end
3 Likes

Ok using that now, but I also changed the grid size since and I get this problem:

local gridSize = 4

function round(number, to)
	to = to or 1
	return math.floor(number/to + 0.5) * to
end

function roundVector(vector, unit)
	return Vector3.new(round(vector.X, unit), vector.Y, round(vector.Z, unit))
end

and then when I call the CFrame:

pole1.CFrame = CFrame.new(roundVector(mouseP + Vector3.new(0, pole1.Size.X/2, 0), gridSize))*CFrame.Angles(0, 0, math.rad(90))

I’m pretty sure I could just move the baseplate over by 1 stud in studio, but I’m sure there is a way to get the baseplates position and work the grid out from that? That way I wouldn’t have to test every single baseplate to make sure it works

1 Like

Sorry, I forgot an important part of that code. I updated it. Try it now