Block-Grid Placement Issue

Hey guys, I’m running into a bit of trouble with this block placement system.

Server Script:

local replicatedStorage = game:GetService("ReplicatedStorage")
local placeEvent = replicatedStorage:WaitForChild("Remotes"):WaitForChild("PlaceEvent")

placeEvent.OnServerEvent:Connect(function(player, target, target_surface, block_type)
	local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block_type) 
	if blockTemplate then
		local newBlock = blockTemplate:Clone()
		print(newBlock.Name)
		newBlock.Parent = workspace

		local offset = Vector3.new(0, 0, 0)
		if target_surface == Enum.NormalId.Back then
			offset = Vector3.new(0, 0, 3)
		elseif target_surface == Enum.NormalId.Front then
			offset = Vector3.new(0, 0, -3)
		elseif target_surface == Enum.NormalId.Top then
			offset = Vector3.new(0, 3, 0)
		elseif target_surface == Enum.NormalId.Bottom then
			offset = Vector3.new(0, -3, 0)
		elseif target_surface == Enum.NormalId.Right then
			offset = Vector3.new(3, 0, 0)
		elseif target_surface == Enum.NormalId.Left then
			offset = Vector3.new(-3, 0, 0)
		end
		
		
		newBlock.Position = target.Position + offset
	else
		print("Block template not found for type: " .. block_type)
	end
end)

Local Script:

local LocalPlayer = game.Players.LocalPlayer
local mouse = LocalPlayer:GetMouse()

local block = LocalPlayer.PlayerGui:WaitForChild("Block")



local replicatedStorage = game:GetService("ReplicatedStorage")
local enabled = false
local currentBlock = nil  -- To keep track of the current block

local function handleClick()
	enabled = not enabled  -- Toggle the enabled state
	if enabled then
		print("Script enabled")
	else
		print("Script disabled")
	end
end

script.Parent.MouseButton1Click:Connect(handleClick)

mouse.Move:Connect(function()
	if currentBlock then
		currentBlock:Destroy()  -- Destroy the current block if it exists
		currentBlock = nil  -- Reset the current block
	end

	local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block.Value)
	if enabled and blockTemplate then
		local previewBlock = blockTemplate:Clone()
		previewBlock.Parent = workspace.PreviewBlocks

		local offset = Vector3.new(0, 0, 0)
		if mouse.TargetSurface == Enum.NormalId.Back then
			offset = Vector3.new(0, 0, 3)
		elseif mouse.TargetSurface == Enum.NormalId.Front then
			offset = Vector3.new(0, 0, -3)
		elseif mouse.TargetSurface == Enum.NormalId.Top then
			offset = Vector3.new(0, 3, 0)
		elseif mouse.TargetSurface == Enum.NormalId.Bottom then
			offset = Vector3.new(0, -3, 0)
		elseif mouse.TargetSurface == Enum.NormalId.Right then
			offset = Vector3.new(3, 0, 0)
		elseif mouse.TargetSurface == Enum.NormalId.Left then
			offset = Vector3.new(-3, 0, 0)
		end

		previewBlock.Position = mouse.Target.Position + offset
		currentBlock = previewBlock  -- Set the current block to the newly created one
		
		
		
		
	end
end)

mouse.Button1Down:Connect(function()
	if enabled and mouse.Target and mouse.Target.Name then
		replicatedStorage.Remotes.PlaceEvent:FireServer(mouse.Target, mouse.TargetSurface, block.Value)
	else
		print('Conditions not met')
	end
end)

I’m getting the following error:
ServerScriptService.Script:27: attempt to index nil with 'Position'

Line 27 (the line the error is coming from) is as follows:
newBlock.Position = target.Position + offset

I got the same error message for this line in the LocalScript only once, the server error is much more frequent for some reason:
previewBlock.Position = mouse.Target.Position + offset

The previewBlock is a semi-transparent block that shows where you are about to place the newBlock.

I’m kind of new to scripting, so any help is much appreciated! If you have any questions, feel free to ask :slight_smile:

1 Like

My guess is that the part you are trying to pass exists on the client, but not the server. It looks like the mouse is always going to be hovering over the previewBlock. The previewBlock only exists on the client.

2 Likes

Not entirely sure I know what you mean. But I did discover something interesting. Commenting out the previewBlock system from the local script fixes the errors. I do need the previewBlock system to be functional though.

Here is the code that when commented out, fixes the errors:

--mouse.Move:Connect(function()
--	if currentBlock then
--		currentBlock:Destroy()  -- Destroy the current block if it exists
--		currentBlock = nil  -- Reset the current block
--	end

--	local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block.Value)
--	if enabled and blockTemplate then
--		local previewBlock = blockTemplate:Clone()
--		previewBlock.Parent = workspace.PreviewBlocks

--		local offset = Vector3.new(0, 0, 0)
--		if mouse.TargetSurface == Enum.NormalId.Back then
--			offset = Vector3.new(0, 0, 3)
--		elseif mouse.TargetSurface == Enum.NormalId.Front then
--			offset = Vector3.new(0, 0, -3)
--		elseif mouse.TargetSurface == Enum.NormalId.Top then
--			offset = Vector3.new(0, 3, 0)
--		elseif mouse.TargetSurface == Enum.NormalId.Bottom then
--			offset = Vector3.new(0, -3, 0)
--		elseif mouse.TargetSurface == Enum.NormalId.Right then
--			offset = Vector3.new(3, 0, 0)
--		elseif mouse.TargetSurface == Enum.NormalId.Left then
--			offset = Vector3.new(-3, 0, 0)
--		end

--		previewBlock.Position = mouse.Target.Position + offset
--		currentBlock = previewBlock  -- Set the current block to the newly created one
		
		
		
		
--	end
--end)

Try:

local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block.Value)
print(blockTemplate)

If blockTemplate is nil then the .Position part of your script will error.

You can change to:
local blockTemplate = replicatedStorage.Blocks:WaitForChild(block.Value)
to make sure it is always found.

1 Like

I added that print statement, and it worked properly.

Changing to WaitForChild() didn’t change anything.

I mean that the previewBlock only exists on the client. My guess is that you cannot pass it to the server because of that.

Since the previewBlock demonstrates where you are going to place, it is under your mouse quite a bit, so it will be the value of mouse.Target frequently.

1 Like

So how do I fix that?

(filler text for character limit)

Make the same change to the Server Script.

Use WaitForChild and print it.

2 Likes

Set Mouse.TargetFilter to previewBlock every time you update the preview. If you ever need to filter multiple instances out, place them in a folder and set the TargetFilter to that folder.

1 Like

I did, and still no changes

(filler characters)

So would that line be

mouse.TargetFilter = previewBlock

And can you tell me the exact place I need to add that line?

Yes, that looks right. Try adding it after the line

local previewBlock = blockTemplate:Clone()
1 Like

Wow, that works, tysm for your help!!

One more thing, if you don’t mind.

How do I make it so you have to be within a certain stud radius of the mouse.Target in order to have the preview and be able to place the block? Because right now, you can place blocks all the way from the other side of the map

Check the distance between the character (or camera) and mouse.Hit:

if (character.HumanoidRootPart.Position - mouse.Hit.p).Magnitude < MAX_DISTANCE then
 -- do whatever, you can also reverse the inequality and put a return here
end
1 Like

Perfect, it works great! Thank you so much for all of your help :slight_smile:

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