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!