Grid Placement System OOPSIE

Hey! I recently used @EgoMoose 's grid placement tutorial and came across an issue.
In the tutorial, he only includes how to put parts on one particular side (left,right,top,bottom,front or back) but not all at the same time.

I’ve tried doing this by myself, but it looks like it’s having seizures

Here’s the script:

local Ogblock = script:WaitForChild("Block")
local plr = game:GetService("Players").LocalPlayer
local mouse = plr:GetMouse()

local equipped
local model
local origCanvas
local canvas
local rotation = 0

local function getCanvas()
	local canvasSize = canvas.Size

	local up = Vector3.new(0, 1, 0)
	local back = -Vector3.FromNormalId(Enum.NormalId.Top)

	local dot = back:Dot(Vector3.new(0, 1, 0))
	local axis = (math.abs(dot) == 1) and Vector3.new(-dot, 0, 0) or up

	local right = CFrame.fromAxisAngle(axis, math.pi/2) * back
	local top = back:Cross(right).Unit

	local cf = canvas.CFrame * CFrame.fromMatrix(-back*canvasSize/2, right, top, back)
	local size = Vector2.new((canvasSize * right).magnitude, (canvasSize * top).Magnitude)

	return cf, size
end

local function isColliding(part)
	local isColliding = false

	for i, v in pairs(canvas:GetChildren()) do
		if v.Position == part.Position then
			isColliding = true
		end
	end
	
	return isColliding
end

local function getCframe(block,position,rot)
	local cf, size = getCanvas()

	local modelSize = CFrame.fromEulerAnglesYXZ(0, rot, 0) * model.Size
	modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))

	local lpos = cf:pointToObjectSpace(position);
	local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
	local x = math.clamp(lpos.x, -size2.x, size2.x);
	local y = math.clamp(lpos.y, -size2.y, size2.y);

	local g = 4
	if (g > 0) then
		x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
		y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
	end

	return cf * CFrame.new(x, y, -modelSize.y / 2) * CFrame.Angles(-math.pi / 2, rot, 0)
end

local function onRotate(actionName, userInputState, input)
	if equipped then
		if (userInputState == Enum.UserInputState.Begin) then
			rotation = rotation + math.pi / 2
		end
	end
end

local function onPlace(actionName, userInputState, input)
	if equipped then
		if (userInputState == Enum.UserInputState.Begin) then
			local cf = getCframe(model, mouse.Hit.Position, rotation)
			local tbl = {
				Color = plr:GetAttribute("Color");
				Material = plr:GetAttribute("Material")
			}
			
			game:GetService("ReplicatedStorage").Place:FireServer(tbl, cf, isColliding(model))
			
			canvas = origCanvas
			print(canvas)
		end
	end
end

game:GetService("ContextActionService"):BindAction("rotate", onRotate, false, Enum.KeyCode.R)
game:GetService("ContextActionService"):BindAction("place", onPlace, false, Enum.UserInputType.MouseButton1)

game:GetService("RunService").Heartbeat:Connect(function()
	if equipped == true then
		task.wait(1)
		if mouse.Target then
			if mouse.Target.Parent == canvas then
			
				if canvas ~= mouse.Target then canvas = mouse.Target end
			else
				if canvas ~= origCanvas then canvas = origCanvas end
			end
		end
		
		local cf = getCframe(model, mouse.Hit.Position, rotation)
		if model.CFrame ~= cf then model.CFrame = cf end
	end
end)

game:GetService("ReplicatedStorage"):WaitForChild("Enable").Event:Connect(function(bool)
	if type(bool) == "boolean" and bool == true then
		-- when plr begins build mode
		if model then model:Destroy() end

		model = Ogblock:Clone()
		model.Color = plr:GetAttribute("Color")
		model.Material = Enum.Material[plr:GetAttribute("Material")]
		model.Name = plr.Name
		model.Parent = workspace
		
		canvas = workspace.Plots[plr.Name]
		origCanvas = workspace.Plots[plr.Name]
		equipped = true
		
		task.delay(.1,function()
			-- sometimes it glitches so this is to prevent said glitch
			if model.Color ~= plr:GetAttribute("Color") then
				model.Color = plr:GetAttribute("Color")
				model.Material = Enum.Material[plr:GetAttribute("Material")]
			end
		end)
	else
		--- when plr cancels build mode
		if model then model:Destroy() end
		equipped = false
	end
end)
1 Like

make sure the transparent block is filtered by the mouse

mouse.TargetFilter = TheBlock

Although this made it a little better, it did not fully fix it, thank you though!

local userInputService = game:GetService("UserInputService")
local camera = workspace.CurrentCamera
local raycastParams = RaycastParams.new()
local gridSize = 4
local halfGridSize = gridSize / 2

-- this function will raycast from the camera to the mouse position and return raycastResult
local function RayCast()
    local mouse = userInputService:GetMouseLocation()
    local ray = camera:ViewportPointToRay(mouse.X, mouse.Y)
    return workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)
end

-- this will snap a vector3 to the grid
local function SnapToGrid(vector)
    local x = math.round(vector.X / gridSize) * gridSize
    local y = math.round(vector.Y / gridSize) * gridSize
    local z = math.round(vector.Z / gridSize) * gridSize
    return Vector3.new(x, y, z)
end

-- when a input changes call this function
userInputService.InputChanged:Connect(function(input, processed)
    -- if its not mouse move then return and do nothing
    if input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
    -- get the raycast results using the raycast function
    local raycastResult = RayCast()
    -- if the ray never hit anything then return and do nothing
    if raycastResult == nil then return end
    -- use the position and normal to work out where the position should be around
    local position = raycastResult.Position + raycastResult.Normal * halfGridSize
    -- snap this position to the grid and position the block there
    block.Position = SnapToGrid(position)
end)
3 Likes

You’re a genius, thank you so much!