How do I fix my building system?

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    a 5x5 grid building system
  2. What is the issue? Include screenshots / videos if possible!
    when I place blocks aiming my mouse at another block instead of putting the block next to that side of the other block it places it randomly
  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
I know it’s because of the offset system in case there’s another block where i’m trying to click but it should just place it next to that side instead of registering as trying to put it in the same place, and I also can’t think of something that could fix it.

server script:

local Tool = script.Parent
local RESET_SECONDS = 0.05
local debounce = false
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera

function MouseRaycast()
	local mousePosition = UserInputService:GetMouseLocation()
	local mouseray = camera:ViewportPointToRay(mousePosition.X,mousePosition.Y)
	local raycastresult = workspace:Raycast(mouseray.Origin,mouseray.Direction * 2000)

	return raycastresult
end

local position = nil
local placeevent = script:WaitForChild("RemoteEvent")

	RunService.RenderStepped:connect(function()
		local result = MouseRaycast()
		if result and result.Position then
		position = result.Position
	end
	end)

Tool.Activated:connect(function()
	local Players = game:GetService("Players")

	if not debounce and position then
		debounce = true

		script.Parent.Handle.Call:Play()
		placeevent:FireServer(position)
		if script.Parent.Left.Value == 0 then
			script.Del:FireServer()
		end
		script.Parent.Left.Value-=1
		script.Parent.ToolTip = script.Parent.Left.Value .. " sand blocks"
		--You can also use Math.Random() to calculate your spawns to a random set of values for the Vector3.new() Values
		wait(RESET_SECONDS)
		debounce = false
	end
end)

place event:

local occupiedPositions = {}

script.Parent.OnServerEvent:Connect(function(plr, position)
	local function round(num)
		local divided = num / 5
		local rounded = 5 * math.floor(divided)
		return rounded
	end

	local blockSize = Vector3.new(5, 5, 5)  -- Adjust according to your block size

	local roundedPosition = Vector3.new(round(position.X), round(position.Y), round(position.Z))

	-- Check if the rounded position is occupied
	while occupiedPositions[roundedPosition] do
		roundedPosition = roundedPosition + (Vector3.new(math.random(-1,1),1,math.random(-1,1))*5)  -- Adjust the position
	end

	-- Mark the position as occupied
	occupiedPositions[roundedPosition] = true

	local noob1 = game.ServerStorage.Blocks.SandBlock:Clone() -- Cloning
	noob1.Parent = workspace -- Parent
	noob1.Position = roundedPosition
end)

It’s hard to explain but I hope it’s understandable

2 Likes

Try changing the code so that instead of placing it at where the raycast hits, it places it at the position of the block it hits with a given offset based on the normal of the face which it hits. I found this to work well when making a building system for an old project.

Here is what the code may look like:

local Position = RayCast.Instance.Position

local GridSize = 5

local Normal = RayCast.Normal
local Offset = Vector3.new(Normal.X*GridSize, Normal.Y*GridSize, Normal.Z*GridSize)

local NewPosition = Position + Offset

You would have to use an if statement to check for RayCast.Instance and to check that it is a BasePart as it can also return Terrain.

1 Like

sounds useful. but… what is a normal? I could incorporate this in my code if I knew

1 Like

The normal is the surface which the raycast hits. For example the top surface would have a normal id of (0, 1, 0) to represent that face of the object. On cubes/cuboids, there are 6 possible normals for each of the faces; however, on meshes, there are often more as it may have more faces at different angles compared to a simple cube.

1 Like

wait hold on, it doesn’t work, i’m trying to use the normal but it’s erroring and I don’t know why

local script:
local Tool = script.Parent
local RESET_SECONDS = 0.05
local debounce = false
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera

function MouseRaycast()
	local mousePosition = UserInputService:GetMouseLocation()
	local mouseray = camera:ViewportPointToRay(mousePosition.X,mousePosition.Y)
	local raycastresult = workspace:Raycast(mouseray.Origin,mouseray.Direction * 2000)
	local norm = raycastresult.Normal

	return raycastresult, norm
end

local position = nil
local normie = nil
local placeevent = script:WaitForChild("RemoteEvent")

	RunService.RenderStepped:connect(function()
		local result = MouseRaycast()
		if result and result.Position then
		position = result.Position
		local normie = result.Normal
	end
end)

Tool.Activated:connect(function()
	local Players = game:GetService("Players")

	if not debounce and position then
		debounce = true

		script.Parent.Handle.Call:Play()
		placeevent:FireServer(position, normie)
		if script.Parent.Left.Value == 0 then
			script.Del:FireServer()
		end
		script.Parent.Left.Value-=1
		script.Parent.ToolTip = script.Parent.Left.Value .. " sand blocks"
		--You can also use Math.Random() to calculate your spawns to a random set of values for the Vector3.new() Values
		wait(RESET_SECONDS)
		debounce = false
	end
end)

place event:

local occupiedPositions = {}

script.Parent.OnServerEvent:Connect(function(plr,position,normal)
	local function round(num)
		local divided = num / 5
		local rounded = 5 * math.floor(divided)
		return rounded
	end

	local blockSize = Vector3.new(5, 5, 5)  -- Adjust according to your block size

	local roundedPosition = Vector3.new(round(position.X), round(position.Y), round(position.Z))

	-- Check if the rounded position is occupied
	while occupiedPositions[roundedPosition] do
		roundedPosition = roundedPosition + normal
	end

	-- Mark the position as occupied
	occupiedPositions[roundedPosition] = true

	local noob1 = game.ServerStorage.Blocks.SandBlock:Clone() -- Cloning
	noob1.Parent = workspace -- Parent
	noob1.Position = roundedPosition
end)

I changed them a bit and it doesn’t work as intended

1 Like

Perhaps the normal doesn’t exist. You should implement some error checking to make sure RaycastResult and RaycastResult.Normal isn’t nil

1 Like

You would need to multiply the x, y and z values by the block size as this will change the normal; for example, from (0, 1, 0) to (0, 5, 0) as this would change the offset to fit in the grid.

1 Like

update: i figured it out after a bit of research and got it to work exactly as intended. thank you to everyone who contributed as well.

1 Like

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