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)
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)
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!
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)