Objects inside of each other, grid based placement system

I am trying to create a grid based placement system. Whenever you try and place an object on the sides of another one, it goes inside of the object instead.

local replicated = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local ts = game:GetService("TweenService")
local buildings = replicated.Buildings
local functions = replicated.Functions
local player = players.LocalPlayer
local runService = game:GetService("RunService")
local camera = workspace.CurrentCamera
local mouse = player:GetMouse()
mouse.TargetFilter = workspace.Camera
local uis = game:GetService("UserInputService")

local selectedBuilding = nil
local rotation = 0

local gridSize = 4 -- in studs

function convertToGrid(cframe)
	local xPos = cframe.Position.X
	local zPos = cframe.Position.Z
	local rounded1 = math.round(xPos/gridSize)*gridSize
	local rounded2 = math.round(zPos/gridSize)*gridSize
	cframe = Vector3.new(rounded1, cframe.Position.Y, rounded2)
	return cframe
end

function createBuilding(building)
	local building = replicated.Buildings:FindFirstChild(building)
	
	if not building then warn("aint there homedog!!") return end
	
	local building = building:Clone()
	building.Parent = workspace.Camera
	selectedBuilding = building
end

runService.RenderStepped:Connect(function()
	if selectedBuilding then
		local hit = mouse.Hit
		local roundedPos = convertToGrid(hit)
		local roundedY = math.round(hit.Position.Y/gridSize)*gridSize
		local cframe = CFrame.new(Vector3.new(roundedPos.X, roundedY+selectedBuilding.PrimaryPart.Size.Y/2, roundedPos.Z)) * CFrame.Angles(0, math.rad(rotation), 0)
		selectedBuilding:SetPrimaryPartCFrame(cframe)
		
	end
end)

uis.InputBegan:Connect(function(input, proc)
	if input then
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			if selectedBuilding then
				print("YEAHJ")
				local success = functions.SpawnBuilding:InvokeServer(selectedBuilding.Name, selectedBuilding.PrimaryPart.CFrame)
				if success then

				end
			end
		elseif input.KeyCode == Enum.KeyCode.R then
			if selectedBuilding then
				rotation += 90
			end
		end
	end
end)



script.Parent.debug.MouseButton1Click:Connect(function()
	createBuilding("example")
end)
1 Like

My way of fixing the issue might be lazy but it should work. I made Blocks Placement System to Grid Remastered. It is my open sourced project. I do not want you to reuse my entire system in your game becacuse you want to use grid size equal to 4 but my system works only with grid size equal to 3. However I think that you should rework placing blocks if a surface of a block (which is not a baseplate) is being pointed with a mouse. Look at my piece of code used in my project:

local mouseSurface = mouse.TargetSurface
if mouseSurface == left then
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(leftOffset)
elseif mouseSurface == right then
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(rightOffset)
elseif mouseSurface == bottom then
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(bottomOffset)
elseif mouseSurface == top then
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(topOffet)
elseif mouseSurface == front then
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(frontOffset)
else --Back
	previewBlock.CFrame = mouseTarget.CFrame:ToWorldSpace(backOffset)
end

Here are also the offsets declarations:

local cframeNew, mathFloor = CFrame.new, math.floor
local leftOffset, rightOffset = cframeNew(-3, 0, 0), cframeNew(3, 0, 0)
local frontOffset, backOffset = cframeNew(0, 0, -3), cframeNew(0, 0, 3)
local bottomOffset, topOffet = cframeNew(0, -3, 0), cframeNew(0, 3, 0)

Basically depending on the pointed surface on a block, the script will take the position of this block and move the preview block properly. I suggest looking at my entire code if you still do not understand what I mean. I hope that it helps and have a nice day.
Edit:
I forgot to include surfaces so here it is:

local bottom, top, front = Enum.NormalId.Bottom, Enum.NormalId.Top, Enum.NormalId.Front
local left, right = Enum.NormalId.Left, Enum.NormalId.Right
3 Likes

This would work perfectly if everything in my game was the block I showed in the example. I have different sized buildings and this system just places the block in the center of the face instead of on the grid for anything above a 4x4 block.

I have an idea. Instead of using offset when pointing a specific surface, add a small number (0.1) to the current mouse.Hit value. For example when a mouse is pointing at the surface “right” of a block, get mouse.Hit, make a new CFrame almost the same but with Position.X plus 0.1. Then do the building math. For buildings make a one big part and inside of this part put the buildings stuff. The game should do the math only on this big part. The second part of my explanation might be confusing so let me know if you need futher explanation.