How do i add snapping, rotating, and movement to my placing system

I’m not sure at all how to do the snapping and no tutorials have helped so i would appreciate an example or explanation

this is my current code and i’ve only gotten it to place and move i attempted rotation but got lost

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local CollectionService = game:GetService("CollectionService")

local Preview = nil
local CurrentBuild = ReplicatedStorage["Build Items"]["Wooden Wall"]

local function preparePreview(preview: Model)
	for _, part in preview:GetChildren() do
		if part:IsA("BasePart") then
			part.CanCollide = false
			part.Transparency = 0.5
		end
	end
end

Preview = CurrentBuild:Clone()
Preview.Parent = workspace
preparePreview(Preview)

--[[Player Variables]]
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local cc = workspace.CurrentCamera

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {player.Character, Preview}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local function GetMouseLocation()
	local MouseLocation = UserInputService:GetMouseLocation()
	local UnitRay = cc:ViewportPointToRay(MouseLocation.X, MouseLocation.Y)
	local cast = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 1000, raycastParams)
	
	if cast and CurrentBuild ~= nil then
		local hitPosition = cast.Position

		local hitNormal = cast.Normal
		local lookAtPosition = hitPosition + hitNormal
		local orientation = CFrame.lookAt(hitPosition, lookAtPosition, hitNormal)
		Preview:PivotTo(orientation)
		CurrentBuild:PivotTo(Preview:GetPivot())
	else
		return
	end
end

local RotationAmount = 0

UserInputService.InputBegan:Connect(function(input, gpe)
	if gpe then return end
	
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		Preview = nil
		ReplicatedStorage.Communication.Placement:FireServer(CurrentBuild)
	end
end)

local Running

Running = RunService.RenderStepped:Connect(function()
	if Preview == nil then
		Running:Disconnect()
	else
		GetMouseLocation()
	end
end)
1 Like

For the snapping part, you only need one small modification on line 37:

local hitPosition = cast.Position - Vector3.new(cast.Position.X % 1, cast.Position.Y % 1, cast.Position.Z % 1)

to alter the size of the snapping grid, simply change the % 1 to % yourDesiredGridSize


The percent sign is called the modulo operator. It takes a number like in your case cast.Position.X divides it by your grid size (1) and returns the remainder.

Here are some examples:

10 % 2 = 0
1 % 2 = 1
16 % 5 = 1
3.42 % 1 = 0.42
100 % 25 = 0
10.87 % 3 = 1.87

thanks still looks complicated to me but i’ll try to understand it

also i assumed this was using raycastparams i only want to include parts with a tag “Grid” but dont know if that possible or not to use tags or if i would have to give a path for every grid instead

If I understand correctly, you only want parts with a “Grid” tag to be snapped to the grid.
You can achieve this simply by adding a check for the tag:

local hitPosition = cast.Position
if Preview:HasTag("Grid") then
    hitPosition -= Vector3.new(cast.Position.X % 1, cast.Position.Y % 1, cast.Position.Z % 1)
end

the preview is the actual build but the grids are parts i want to build on

also im trying to replicate the build to the server but… its saying attempt to index nil even though it exist

game.ReplicatedStorage.Communication.Placement.OnServerEvent:Connect(function(player, build: Model)
	print(build)
end)
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local CollectionService = game:GetService("CollectionService")

local Preview = nil
local CurrentBuild = ReplicatedStorage["Build Items"]["Wooden Wall"]

local ReplicatedModel = CurrentBuild:Clone()

local function preparePreview(preview: Model)
	for _, part in preview:GetChildren() do
		if part:IsA("BasePart") then
			part.CanCollide = false
			part.Transparency = 0.5
		end
	end
end

Preview = CurrentBuild:Clone()
Preview.Parent = workspace
preparePreview(Preview)

--[[Player Variables]]
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local cc = workspace.CurrentCamera

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {player.Character, Preview}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude

local function GetMouseLocation()
	local MouseLocation = UserInputService:GetMouseLocation()
	local UnitRay = cc:ViewportPointToRay(MouseLocation.X, MouseLocation.Y)
	local cast = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 1000, raycastParams)

	if cast and CurrentBuild ~= nil then
		local hitPosition = cast.Position - Vector3.new(cast.Position.X % 2, cast.Position.Y % 2, cast.Position.Z % 2)
		
		local hitNormal = cast.Normal
		local lookAtPosition = hitPosition + hitNormal
		local orientation = CFrame.lookAt(hitPosition, lookAtPosition, hitNormal)
		Preview:PivotTo(orientation)
	else
		return
	end
end

local RotationAmount = 0

UserInputService.InputBegan:Connect(function(input, gpe)
	if gpe then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		ReplicatedModel:PivotTo(Preview:GetPivot())
		print(ReplicatedModel)
		ReplicatedModel.Parent = workspace
		ReplicatedStorage.Communication.Placement:FireServer(ReplicatedModel)
		Preview = nil
	end
end)

local Running

Running = RunService.RenderStepped:Connect(function()
	if Preview == nil then
		Running:Disconnect()
	else
		GetMouseLocation()
	end
end)

In that case, you can change your RaycastParams to the following:

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Include
raycastParams.FilterDescendantsInstances = CollectionService:GetTagged("Grid")

-- Add support for StreamingEnabled
CollectionService:GetInstanceAddedSignal("Grid"):Connect(function(streamedObject)
	raycastParams:AddToFilter(streamedObject)
end)

I tried the code you posted and I didn’t get any errors. What does the error message say exactly, and on which line does the error occur?

nvm i figured out how to replicate it but still need help with rotating

Multiply the CFrame of the item with CFrame.Angles(math.rad(xRotation), math.rad(yRotation), math.rad(zRotation))

You will need to keep track of the current x, y and z rotation that’s requested by the user.

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