Help with grid placement

Hello!

I’m working on a game that requires a block placement system revolving around a grid. Currently, most placement works just fine, you can place blocks on top of each other and on the ground wherever. The only problem arrives when you try to place blocks in front or behind another block, in which it doesn’t detect that block being there and rather decides to place a block inside of the already existing block.

I’ve had an idea of checking if a block already occupies a given position, but I feel like that just isn’t the best way to approach this. I’ve looked at other forum posts to see how they approached grid placement, but it felt just way too advanced for what we’re working on.

Here’s some code that I think you’ll find useful:

Client main:

-- services --
local runService = game:GetService('RunService')
local uis = game:GetService('UserInputService')
local players = game:GetService('Players')
local replicatedStorage = game:GetService('ReplicatedStorage')

-- shortcuts --
local vec3 = Vector3.new

-- variables --
local tool = script.Parent
local char = players.LocalPlayer.Character or players.LocalPlayer.CharacterAdded:Wait()
local cam = workspace.CurrentCamera
local placeBlock = replicatedStorage.Remotes.placeBlock
local blocks = replicatedStorage:WaitForChild('Blocks')

local equipped = false

local gridIncrement = 4

-- script-only variables --
local _block

local _selectedBlock = 'Metal'

-- raycastParams --
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

raycastParams.FilterDescendantsInstances = {char, _block}

-- functions --
local function roundToIncrement(x, y, z, increment)
	return vec3(math.round(x/increment)*increment, math.round(y/increment)*increment, math.round(z/increment)*increment)
end

local function raycast()
	local mousePos = uis:GetMouseLocation()
	print('raycast!')
	local mouseRay = cam:ViewportPointToRay(mousePos.X, mousePos.Y)
	local result = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
	return result
end

local function onEquip()
	print('equipped')
	equipped = true
	if not _block then
		local part = blocks[_selectedBlock]:Clone()
		_block = part
		part.Transparency = 0.5
		part.CastShadow = false
		part.CanCollide = false
		part.Anchored = true
		part.CanQuery = false
		part.Parent = workspace
		
		local result = raycast()
		if result ~= nil then part.Position = roundToIncrement(result.Position.X, result.Position.Y, result.Position.Z, gridIncrement) end
	end
end

local function onUnequip()
	equipped = false
	print('unequipped')
	if _block then
		_block:Destroy()
		_block = nil
	end
end

local function onActivated()
	print(_block)
	if _block ~= nil and equipped == true then
		placeBlock:FireServer(_selectedBlock, _block.Position)
	end
end

local function onRenderStep()
	if equipped then
		local result = raycast()
		if _block and result ~= nil then
			_block.Position = roundToIncrement(result.Position.X, result.Position.Y, result.Position.Z, gridIncrement)
		end
	end
end

-- connections --
tool.Equipped:Connect(onEquip)
tool.Unequipped:Connect(onUnequip)
tool.Activated:Connect(onActivated)
runService.RenderStepped:Connect(onRenderStep)

Server main:

local remote = game.ReplicatedStorage.Remotes.placeBlock

remote.OnServerEvent:Connect(function(player, blockToPlace : string, position)
	print(blockToPlace, position)
	local newBlock = game.ReplicatedStorage.Blocks:FindFirstChild(blockToPlace):Clone()
	newBlock.Position = position
	newBlock.Anchored = true
	newBlock.CanQuery = true
	newBlock.CanCollide = true
	newBlock.Transparency = 0
	newBlock.Parent = workspace
	print('placed')
end)

Any help would be hugely appreciated!

When you’re placing an object use WorldRoot:GetPartsInPart(selectedPart)
And then if one of those parts is within a magnitude of 1, then the block can’t go there

Was able to fix on my own. I appreciate the help, though!

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