Global rotation for placement system

Alright so i’ve been trying to make a placement system where you could grab an object, rotate it, weld it and stuff like that.

So the problem here is that i wanted it so the rotation for all object is the same and/or consistent. Example:

I grabbed a plank, then i pressed R to rotate it to (0,45,45). Now i grab another plank and my expectation is that it would also start at (0,45,45) so building is not a pain.

But…

Seems like trying to fix it myself just makes the code overcomplicated. Can anyone help me plz?

local RS = game:GetService("RunService")
local UIS = game:GetService("UserInputService")
local RP = game:GetService("ReplicatedStorage")
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera

local highlight = RP.Objects.HoverHighlight
local billboard = RP.Objects.BillboardGui
local baseOrientationCFrame = nil

local buttonHighlight = highlight:Clone()
buttonHighlight.FillTransparency = 1
buttonHighlight.OutlineColor = Color3.fromRGB(255, 200, 50)
buttonHighlight.Parent = nil

local isMobile = true
local rotationMode = "Y"
local rotationOffset = CFrame.new()
local SackDebounce = false
local currentTarget = nil
local carrying = false
local range = 12
local carryDistance = 9
local maxCarryDistance = 20
local carrySmooth = 0.09
local throwBoost = 8
local lastVelo = Vector3.new()
local targetPos = nil
local lastTouchPosition = nil
local PC

if UIS.TouchEnabled and not UIS.KeyboardEnabled and not UIS.MouseEnabled then
	PC = false
elseif not UIS.TouchEnabled and UIS.KeyboardEnabled and UIS.MouseEnabled then
	PC = true
end

local storeConnection
local mouse = player:GetMouse()
local mobileDebounce = false
local axisTemplate = RP:WaitForChild("RotationAxis")
local axisModel

function AttachRotationAxis(target)
	if axisModel then
		axisModel:Destroy()
	end
	axisModel = axisTemplate:Clone()
	axisModel.Parent = workspace
	axisModel.PrimaryPart = axisModel.Mid
	axisModel:SetPrimaryPartCFrame(CFrame.new(target.PrimaryPart.Position))
end

function RemoveRotationAxis()
	if axisModel then
		axisModel:Destroy()
		axisModel = nil
	end
end

local function updateAxisHighlight()
	if not axisModel then return end
	axisModel.X.Highlight.Enabled = false
	axisModel.Y.Highlight.Enabled = false
	axisModel.Z.Highlight.Enabled = false

	if rotationMode == "X" then
		axisModel.X.Highlight.Enabled = true
	elseif rotationMode == "Y" then
		axisModel.Y.Highlight.Enabled = true
	elseif rotationMode == "Z" then
		axisModel.Z.Highlight.Enabled = true
	end
end

RS.RenderStepped:Connect(function(dT)
	local target = nil
	local inFirstPerson = (camera.CameraType == Enum.CameraType.Custom 
		and (camera.CFrame.Position - camera.Focus.Position).Magnitude < 1.5)

	if inFirstPerson then
		local rayOrigin = camera.CFrame.Position
		local rayDirection = camera.CFrame.LookVector * range
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {player.Character}
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		local result = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

		while result and result.Instance and result.Instance.Name == "Hitbox" do
			rayOrigin = result.Position + rayDirection.Unit * 0.01
			result = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
		end

		if result and result.Instance and result.Instance.Parent 
			and result.Instance.Parent:IsA("Model") 
			and result.Instance.Parent:FindFirstChild("Pickup") then
			target = result.Instance
		end
	else
		local mouseTarget = mouse.Target
		if mouseTarget and mouseTarget.Name ~= "Hitbox" then
			if mouseTarget.Parent and mouseTarget.Parent:IsA("Model") 
				and mouseTarget.Parent:FindFirstChild("Pickup") then
				target = mouseTarget
			end
		end
	end

	if target and target.Parent and target.Parent:IsA("Model") and target.Parent:FindFirstChild("Pickup") then
		if not carrying and target.Parent ~= currentTarget then
			currentTarget = target.Parent
			highlight.Parent = target.Parent
			billboard.Parent = target.Parent
			billboard.Main.ObjName.Text = string.lower(target.Parent.Name)
			billboard.Main.Tag.Text = string.lower(target.Parent.Class.Value)
		end

		if target.Name == "Button" then
			buttonHighlight.Parent = target
		else
			buttonHighlight.Parent = nil
		end
	else
		if currentTarget ~= nil and not carrying then
			player.PlayerGui.BindsUI.Container.StoreBind.Visible = false
			player.PlayerGui.BindsUI.MobileButtons.Store.Visible = false
			currentTarget = nil
			highlight.Parent = nil
			billboard.Parent = nil
			SackDebounce = false
		end
	end

	if carrying and currentTarget and currentTarget.Parent then
		billboard.Parent = nil
		if inFirstPerson then
			targetPos = camera.CFrame.Position + camera.CFrame.LookVector * carryDistance
		else
			local screenPos = nil
			if isMobile and lastTouchPosition then
				screenPos = lastTouchPosition
			else
				screenPos = UIS:GetMouseLocation()
			end
			local ray = camera:ScreenPointToRay(screenPos.X, screenPos.Y)
			targetPos = ray.Origin + ray.Direction * carryDistance
		end

		local minY = 0
		if targetPos.Y < minY then
			targetPos = Vector3.new(targetPos.X, minY, targetPos.Z)
		end

		local newCFrame = currentTarget.PrimaryPart.CFrame:Lerp(
			CFrame.new(targetPos) * baseOrientationCFrame * rotationOffset, carrySmooth
		)
		currentTarget:SetPrimaryPartCFrame(newCFrame)
		currentTarget.PrimaryPart.AssemblyLinearVelocity = Vector3.zero

		if (camera.CFrame.Position - currentTarget.PrimaryPart.Position).Magnitude > maxCarryDistance then
			DropItem(false)
		end

		if axisModel then
			axisModel:SetPrimaryPartCFrame(currentTarget.PrimaryPart.CFrame)
		end
	end
end)

UIS.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		LeftClick()
	end

	if input.KeyCode == Enum.KeyCode.R and carrying and currentTarget then
		if rotationMode == "X" then
			rotationOffset = rotationOffset * CFrame.Angles(math.rad(45), 0, 0)
		elseif rotationMode == "Y" then
			rotationOffset = rotationOffset * CFrame.Angles(0, math.rad(45), 0)
		elseif rotationMode == "Z" then
			rotationOffset = rotationOffset * CFrame.Angles(0, 0, math.rad(45))
		end
	end

	if input.KeyCode == Enum.KeyCode.T and carrying and currentTarget then
		if rotationMode == "X" then
			rotationMode = "Y"
		elseif rotationMode == "Y" then
			rotationMode = "Z"
		else
			rotationMode = "X"
		end
		updateAxisHighlight()
	end
end)

UIS.InputEnded:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end
	if input.UserInputType == Enum.UserInputType.MouseButton1 and carrying then
		LeftUnClick()
	end
end)

UIS.TouchStarted:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end
	if mobileDebounce then return end
	lastTouchPosition = input.Position
	LeftClick()
end)

UIS.TouchEnded:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end
	if carrying then LeftUnClick() end
end)

function LeftClick()
	if currentTarget ~= nil and currentTarget.Parent then
		if currentTarget.ServerOwner.Value == false then return end
		baseOrientationCFrame = currentTarget.PrimaryPart.CFrame.Rotation
		carrying = true
		RP.Remotes.PickupItem:FireServer(currentTarget)

		if not currentTarget:FindFirstChild("OriColl") then
			local val = Instance.new("StringValue")
			val.Parent = currentTarget
			val.Name = "OriColl"
			val.Value = currentTarget.PrimaryPart.CollisionGroup
		end

		for _, d in ipairs(currentTarget:GetDescendants()) do
			if d:IsA("MeshPart") or d:IsA("Part") then
				d.CollisionGroup = "Item"
			end
		end

		AttachRotationAxis(currentTarget)
		updateAxisHighlight()
	end
end

function LeftUnClick()
	carrying = false
	if currentTarget then DropItem(true) end
end

function DropItem(AddForce)
	if not currentTarget or not currentTarget.Parent then return end
	RemoveRotationAxis()

	local velocity = Vector3.new(0, 0, 0)
	if AddForce then
		velocity = (targetPos - currentTarget.PrimaryPart.Position) * throwBoost
	end

	RP.Remotes.DropItem:FireServer(currentTarget, velocity)

	for _, d in ipairs(currentTarget:GetDescendants()) do
		if d:IsA("MeshPart") or d:IsA("Part") then
			d.CollisionGroup = currentTarget:FindFirstChild("OriColl").Value
		end
	end

	carrying = false
end

UIS.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.Z then
		if carrying and currentTarget then
			RP.Remotes.WeldItem:FireServer(currentTarget)
		elseif currentTarget and not carrying then
			RP.Remotes.UnweldItem:FireServer(currentTarget)
		end
	end
end)

UIS.InputChanged:Connect(function(input, gameProcessed)
	if gameProcessed then return end
	if input.UserInputType == Enum.UserInputType.MouseWheel then
		carryDistance = math.clamp(carryDistance + input.Position.Z, 5, 15)
	end
end)
3 Likes

its probably because the target cframe is the rotation offset from the baseOrientationCFrame

if you want it to start at the same cframe no matter how the brick is oriented then baseOrientationCFrame should be removed

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