I need help making a Grid Based Formula that ignores an object's rotation/tilt

I’ve been working on a small project, and I added rotation and tilting, but it caused issues. The grid system I made was offset and so on. What I am trying to get is a grid formula that does not require an object’s face (NormalID).

My Script:

local replicatedStorage = game:GetService("ReplicatedStorage")
local runService = game:GetService("RunService")
local userInputs = game:GetService("UserInputService")

local eventsFolder = replicatedStorage:WaitForChild("Events")

local spawnBlock = eventsFolder:WaitForChild("SpawnBlock")

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

local placeholder = nil

local rotation = 0
local tilt = 0

local beingHeld = false

function getGridPos(x, y, z)
	return Vector3.new(math.round(x/4) * 4, math.round(y/4) * 4 + 2, math.round(z/4) * 4)
end

if workspace.Placeholders:FindFirstChild("Block") then
	workspace.Placeholders.Block:Destroy()
end

mouse.Button1Down:Connect(function()
	
	if beingHeld == true then
		
		if placeholder then

			if placeholder.Position then

				spawnBlock:FireServer(placeholder.CFrame)

			end

		end
		
	end
	
end)

script.Parent.Equipped:Connect(function()
	
	beingHeld = true
	
end)
script.Parent.Unequipped:Connect(function()
	
	beingHeld = false
	
end)

runService.Heartbeat:Connect(function()
	
	if mouse.Target then
		
		if mouse.Target.Parent == workspace.Blocks then

			if not placeholder then
				
				placeholder = replicatedStorage.Blocks:FindFirstChild(player.Object.Value):Clone()
				placeholder.Parent = workspace.Blocks
				
				placeholder.Size = Vector3.new(4, 4, 4)
				placeholder.Transparency = 0.5
				placeholder.Anchored = true
				placeholder.CanCollide = false
				
				mouse.TargetFilter = placeholder
				
			else
				
				if beingHeld == true then

					placeholder.Transparency = 0.5

				else

					placeholder.Transparency = 1

				end
				
			end
			placeholder.Color = player:WaitForChild("Color3Value").Value
			
			local targetPos = mouse.Target.Position
			--targetPos = getGridPos(targetPos.X, targetPos.Y, targetPos.Z)

			local targetSurface = mouse.TargetSurface.Name

			if targetSurface == "Top" then
				
				targetPos += Vector3.new(0, 4, 0)
				
			elseif targetSurface == "Bottom" then
				
				targetPos += Vector3.new(0, -4, 0)
				
			elseif targetSurface == "Front" then
				
				targetPos += Vector3.new(0, 0, -4)
				
			elseif targetSurface == "Back" then

				targetPos += Vector3.new(0, 0, 4)
				
			elseif targetSurface == "Left" then

				targetPos += Vector3.new(-4, 0, 0)
				
			elseif targetSurface == "Right" then

				targetPos += Vector3.new(4, 0, 0)
				
			end]]
			
			placeholder.CFrame = CFrame.new(targetPos) * CFrame.Angles(math.rad(tilt), math.rad(rotation), 0)
			
		end
		
	end
	
end)

userInputs.InputBegan:Connect(function(input, gP)
	
	if not gP then
		
		if input.KeyCode == Enum.KeyCode.R then
			
			rotation += 90
			
		elseif input.KeyCode == Enum.KeyCode.T then
			
			tilt += 90
			
		end
		
	end
	
end)

player:WaitForChild("Object"):GetPropertyChangedSignal("Value"):Connect(function()
	
	if placeholder then
		
		placeholder:Destroy()
		placeholder = nil
		
	end
	
end)

Can you maybe go in further detail what this exactly is? Is it some sort of building system?

It’s like the game Blockate.

limit

Look into these posts:

They show how to solve your problem

function Round(x, mult)
	return math.floor((x / mult) + 0.5) * mult
end

– made by sleitnick in the second link I provided

For that function, it puts the cube inside the target!
Heres the game link: The Game

My Vector Scripts:

function Round(x, mult)
	return math.floor((x / mult) + 0.5) * mult
end
targetPos = Vector3.new(Round(targetPos.X, 4), Round(targetPos.Y, 4), Round(targetPos.Z, 4))

I think I have achieved what you were going for:

local gridSize = Vector3.new(4,4,4)
local gridOffset = Vector3.new(2,2,2)

local function snap(position)
	local x = math.round((position.X - gridOffset.X) / gridSize.X)*gridSize.X
	local y = math.round((position.Y + gridOffset.Y) / gridSize.Y)*gridSize.Y
	local z = math.round((position.Z - gridOffset.Z) / gridSize.Z)*gridSize.Z
	return Vector3.new(x,y,z)
end
local localplayer = game.Players.LocalPlayer
local mouse = localplayer:GetMouse()


game:GetService("RunService").RenderStepped:Connect(function()
	local char = localplayer.Character
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Exclude
	params.FilterDescendantsInstances = {workspace.Move,char}
	
	local raycast = workspace:Raycast(workspace.CurrentCamera.CFrame.Position,mouse.Hit.LookVector*1000,params)
	
	if raycast and raycast.Position and raycast.Instance then
		workspace.Move.Position = snap(raycast.Position) + Vector3.new(gridOffset.X,-gridOffset.Y,gridOffset.Z)
	end
end)

mouse.Button1Down:Connect(function()
	local clone = workspace.Move:Clone()
	clone.Transparency = 0
	clone.Name = "clone"
	clone.CanCollide = true
	clone.Parent = workspace
end)
External Media

robloxapp-20240103-0123167.wmv (888.2 KB)

The workspace.Move is just a random part with size of 4

The right, back, and bottom of the object causes the placeholder to go INSIDE the object.

My ENTIRE script:

local runService = game:GetService("RunService")
local userInputs = game:GetService("UserInputService")

local eventsFolder = replicatedStorage:WaitForChild("Events")

local spawnBlock = eventsFolder:WaitForChild("SpawnBlock")

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

local placeholder = nil

local rotation = 0
local tilt = 0

local beingHeld = false

if workspace.Placeholders:FindFirstChild("Block") then
	workspace.Placeholders.Block:Destroy()
end

mouse.Button1Down:Connect(function()
	
	if beingHeld == true then
		
		if placeholder then

			if placeholder.Position then
				
				rotation = 0
				tilt = 0

				spawnBlock:FireServer(placeholder.CFrame)

			end

		end
		
	end
	
end)

script.Parent.Equipped:Connect(function()
	
	beingHeld = true
	
end)
script.Parent.Unequipped:Connect(function()
	
	beingHeld = false
	
end)

function Round(x, mult)
	return math.floor((x / mult) + 0.5) * mult
end

local function snap(position)
	local x = math.round((position.X - 4) / 4)*4
	local y = math.round((position.Y + 4) / 4)*4
	local z = math.round((position.Z - 4) / 4)*4
	return Vector3.new(x,y,z)
end

runService.Heartbeat:Connect(function()
	
	if mouse.Target then
		
		if mouse.Target.Parent == workspace.Blocks then

			if not placeholder then
				
				placeholder = replicatedStorage.Blocks:FindFirstChild(player.Object.Value):Clone()
				placeholder.Parent = workspace.Placeholders
				
				placeholder.Size = Vector3.new(4, 4, 4)
				placeholder.Transparency = 0.5
				placeholder.Anchored = true
				placeholder.CanCollide = false
				
				mouse.TargetFilter = placeholder
				
			else
				
				if beingHeld == true then

					placeholder.Transparency = 0.5

				else

					placeholder.Transparency = 1

				end
				
			end
			
			local targetPos
			
			local char = player.Character
			local params = RaycastParams.new()
			params.FilterType = Enum.RaycastFilterType.Exclude
			params.FilterDescendantsInstances = {placeholder,char}

			local raycast = workspace:Raycast(workspace.CurrentCamera.CFrame.Position,mouse.Hit.LookVector*1000,params)

			if raycast and raycast.Position and raycast.Instance then
				targetPos = snap(raycast.Position) + Vector3.new(4,-4,4)
				targetPos = CFrame.new(targetPos)

				placeholder.CFrame = targetPos * CFrame.Angles(math.rad(tilt), math.rad(rotation), 0)
			end
			
			placeholder.Color = player:WaitForChild("Color3Value").Value
			
			--[[local targetSurface = mouse.TargetSurface.Name
			
			local surfaceIDs = {
				Top = targetPos + Vector3.new(0,4,0),
				Bottom = targetPos - Vector3.new(0,4,0),
				Left = targetPos - Vector3.new(4,0,0),
				Right = targetPos + Vector3.new(4,0,0),
				Front = targetPos - Vector3.new(0,0,4),
				Back = targetPos + Vector3.new(0,0,4),
			}
			
			--targetPos = surfaceIDs[targetSurface]]
			
			--targetPos = Vector3.new(Round(targetPos.X, 4), Round(targetPos.Y, 4), Round(targetPos.Z, 4))
			
		end
		
	end
	
end)

userInputs.InputBegan:Connect(function(input, gP)
	
	if not gP then
		
		if input.KeyCode == Enum.KeyCode.R then
			
			rotation += 90
			
		elseif input.KeyCode == Enum.KeyCode.T then
			
			tilt += 90
			
		end
		
	end
	
end)

player:WaitForChild("Object"):GetPropertyChangedSignal("Value"):Connect(function()
	
	if placeholder then
		
		placeholder:Destroy()
		placeholder = nil
		
	end
	
end)

I fixed the issue with the snapping not working on some positions by subtracting a fraction of the inverted direction from the result.Position!
image

local Players			= game:GetService("Players")
local RunService 		= game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--
local placer 			= ReplicatedStorage:WaitForChild("PlacingTemplate"):Clone()
--
local gridSize 			= Vector3.new(4,4,4) 
local displacement		= Vector3.new(2,2,2)
local maxDistance 		= 1000
--
local localPlayer		= Players.LocalPlayer
local mouse				= localPlayer:GetMouse()
local currentCamera 	= workspace.CurrentCamera

placer.Name 			= "placing"
placer.Parent 			= workspace
placer.Anchored 		= true
placer.CanCollide		= false
--
local function snap(position)	
	local x = math.round(position.X / gridSize.X)*gridSize.X
	local y = math.round(position.Y / gridSize.Y)*gridSize.Y
	local z = math.round(position.Z / gridSize.Z)*gridSize.Z
	return Vector3.new(x,y,z)
end
local function place(cFrame)
	ReplicatedStorage.Place:FireServer(cFrame,Color3.fromRGB(math.clamp(cFrame.Position.Y*6,0,255),60,60))
end

RunService.RenderStepped:Connect(function()
	local character = localPlayer.Character
	
	local params 	= RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Exclude
	params.FilterDescendantsInstances = {placer,character}
	
	local direction = mouse.Hit.LookVector
	local origin 	= currentCamera.CFrame.Position
	local result 	= workspace:Raycast(origin,direction*maxDistance,params)
	
	
	if result and result.Position and result.Instance then
		local pos = result.Position - Vector3.new(0.05*direction.X,0.05*direction.Y,0.05*direction.Z) --going back by a little to ensure that the snapping will never overlap
		local newPos = snap(pos-displacement)
		
		placer.CFrame = CFrame.new(newPos+displacement)
	end
end)

mouse.Button1Down:Connect(function() place(placer.CFrame) end)

local pos = result.Position - Vector3.new(0.05*direction.X,0.05*direction.Y,0.05*direction.Z)
local newPos = snap(pos-displacement)

Hope this fixes your problem!

Here is a link to my full system:

THANK YOU SO MUCH!

limitlimitlimit

1 Like

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