Here is a very odd one for you

I’m trying to make a placement system however when trying to place the item it’s pulsing towards the camera. here are my two scripts involved.
guiplacement

-- Client-Side Script: PlacementGuiScript
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local user = Players.LocalPlayer
local placementEvent = ReplicatedStorage:WaitForChild("PlacementEvent")
local getPlotFunction = ReplicatedStorage:WaitForChild("GetPlotFunction")  -- Add this line

local leaderstats = user:WaitForChild("leaderstats")  -- Wait for leaderstats to be available
local moneyValue = leaderstats:WaitForChild("Money")  -- Wait for Money to be available

local getSetMoney = ReplicatedStorage:WaitForChild("GetSetMoney")

-- Function to handle leaderboard setup
local function handleLeaderboardSetup()
	
end

local leaderboardSetupEvent = ReplicatedStorage:WaitForChild("LeaderboardSetupEvent", 5)
if leaderboardSetupEvent then
	leaderboardSetupEvent.OnClientEvent:Connect(handleLeaderboardSetup)
else
	
end

local buildConfigs = script.Parent
local blocksShowcase = buildConfigs:WaitForChild("BlocksShowcase")
local scrollFrame = blocksShowcase:WaitForChild("ScrollFrame")
local openGuiButton = buildConfigs:WaitForChild("OpenGui")

local closeButton = blocksShowcase:FindFirstChild("Close")

local placementEvent = ReplicatedStorage:WaitForChild("PlacementEvent")

local selectedBlock = nil
local previewBlock = nil
local isPreviewing = false

local function startPreview(selectedBlockModel)
	if not selectedBlockModel then
		warn("No block selected for preview")
		return
	end

	if previewBlock then
		previewBlock:Destroy()  -- Remove any existing preview block
	end

	previewBlock = selectedBlockModel:Clone()
	previewBlock.Parent = workspace  -- Place it in the workspace
	for _, part in pairs(previewBlock:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Transparency = 0.5  -- Making the block semi-transparent
			part.CanCollide = false  -- Ensure it doesn't collide with other objects
		end
	end

	isPreviewing = true  -- Set the previewing flag
end

local function updatePreview()
	if isPreviewing and previewBlock then
		local mousePosition = UserInputService:GetMouseLocation()
		local ray = workspace.CurrentCamera:ScreenPointToRay(mousePosition.X, mousePosition.Y)
		local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000)
		if raycastResult then
			local hitPosition = raycastResult.Position
			previewBlock:SetPrimaryPartCFrame(CFrame.new(hitPosition) * CFrame.new(0, previewBlock.PrimaryPart.Size.Y / 2, 0))
		end
	end
end


game:GetService("RunService").Heartbeat:Connect(updatePreview)
local placeItemButton = buildConfigs:WaitForChild("PlaceItemButton")  -- Adjust according to its actual location


local function handlePlaceItemButtonClick()
	if selectedBlock then
		local plotName = getPlotFunction:InvokeServer()
		if plotName then
			placementEvent:FireServer("place", selectedBlock.Name, plotName)
			selectedBlock = nil  -- Reset selected block after placing
			placeItemButton.Visible = false  -- Hide the "Place Item" button
		else
			warn("No plot found for the player")
		end
	else
		warn("No block selected for placement")
	end
end

if placeItemButton then
	placeItemButton.MouseButton1Click:Connect(handlePlaceItemButtonClick)
end

local function placeBlock()
	if isPreviewing and previewBlock and selectedBlock then
		local position = previewBlock.PrimaryPart.Position
		local rotation = previewBlock.PrimaryPart.Orientation -- Assuming the block has a PrimaryPart

		-- Send the placement command with position and rotation
		placementEvent:FireServer("place", selectedBlock.Name, position, rotation)

		-- Cleanup
		previewBlock:Destroy()
		previewBlock = nil
		isPreviewing = false
		selectedBlock = nil
		placeItemButton.Visible = false
		cancelButton.Visible = false
	end
end

-- Connect this function to wherever you want to listen for the mouse movements or touch
updatePreview()

UserInputService.InputBegan:Connect(function(input, isProcessed)
	if isProcessed then return end  -- Ignore processed inputs
	if input.KeyCode == Enum.KeyCode.E then  -- Replace with your desired key
		placeBlock()
	end
end)
-- Function to place a block on a plot
local function placeBlock()
	
	local currentMoney = getSetMoney:InvokeServer("get")
	local plotName = getPlotFunction:InvokeServer()
	if selectedBlock and currentMoney and plotName then
		local cost = selectedBlock:FindFirstChild("Cost")
		if cost and currentMoney >= cost.Value then
			
			placementEvent:FireServer("preview", selectedBlock.Name, plotName)
			-- Wait for user confirmation here. This is a placeholder; replace it with your confirmation logic.
			local userConfirmed = true  
			if userConfirmed then
				
				getSetMoney:InvokeServer("set", currentMoney - cost.Value)
				placementEvent:FireServer("place", selectedBlock.Name, plotName)
			else
				
			end
		else
			
		end
	else
		
	end
end
-- Function to toggle BlocksShowcase GUI visibility
local function toggleGui()
	blocksShowcase.Visible = not blocksShowcase.Visible
end

-- Function to close BlocksShowcase GUI
local function closeGui()
	blocksShowcase.Visible = false
end

-- Create and configure UIGridLayout
local gridLayout = Instance.new("UIGridLayout")
gridLayout.CellPadding = UDim2.new(0, 10, 0, 10)
gridLayout.CellSize = UDim2.new(0, 128, 0, 128)
gridLayout.FillDirection = Enum.FillDirection.Horizontal
gridLayout.SortOrder = Enum.SortOrder.LayoutOrder
gridLayout.Parent = scrollFrame

-- Populating blocks
for _, category in pairs({"Builds", "Decoration", "Interior"}) do
	local categoryFolder = ReplicatedStorage:FindFirstChild("Builder"):FindFirstChild(category)
	if categoryFolder then
		
		for _, block in pairs(categoryFolder:GetChildren()) do
			if block:IsA("Model") then
				local cost = block:FindFirstChild("Cost")
				if cost then
					
					local blockButton = Instance.new("TextButton")
					blockButton.Size = UDim2.new(0, 128, 0, 128)
					blockButton.BackgroundColor3 = Color3.fromRGB(236,236,236)  -- Background color of the button
					blockButton.Text = ""
					blockButton.TextColor3 = Color3.fromRGB(0, 0, 0)  -- Text color of the button (not applicable here since Text is empty)
					blockButton.Parent = scrollFrame

					local viewport = Instance.new("ViewportFrame")
					viewport.Size = UDim2.new(1, 0, 1, 0)  -- Fill the entire TextButton
					viewport.Parent = blockButton
					viewport.BackgroundColor3 = Color3.fromRGB(236,236,236)

					local camera = Instance.new("Camera")
					camera.Parent = viewport
					viewport.CurrentCamera = camera
					camera.CameraType = Enum.CameraType.Scriptable
					if block.PrimaryPart then
						camera.CFrame = CFrame.new(block.PrimaryPart.Position + Vector3.new(5, 5, 5), block.PrimaryPart.Position)
					end
					
					local rotationCFrame = CFrame.Angles(0, math.rad(10), 0)  -- Rotating 10 degrees around Y-axis

					if block.PrimaryPart then
						local targetPosition = block.PrimaryPart.Position
						local originalPosition = targetPosition + Vector3.new(5, 5, 5)
						local originalCFrame = CFrame.new(originalPosition, targetPosition)

						-- Apply rotation to the original CFrame and re-adjust the position
						local rotatedCFrame = originalCFrame * rotationCFrame
						local newPosition = rotatedCFrame.Position
						camera.CFrame = CFrame.new(newPosition, targetPosition)
					end

					local costLabel = Instance.new("TextLabel")
					costLabel.Size = UDim2.new(1, 0, 0, 20)
					costLabel.Position = UDim2.new(0, 0, 1, -20)
					costLabel.Text = " " .. block.Name .. " | " .. cost.Value
					costLabel.TextColor3 = Color3.fromRGB(255, 255, 255)  -- Text color of the label
					costLabel.BackgroundColor3 = Color3.fromRGB(85, 85, 0)  -- Background color of the label
					costLabel.Parent = blockButton

					local displayBlock = block:Clone()
					displayBlock.Parent = viewport

					-- Add rotation here
					local rotationCFrame = CFrame.Angles(0, math.rad(210), 0)  -- Rotating 10 degrees around Y-axis
					displayBlock:SetPrimaryPartCFrame(displayBlock.PrimaryPart.CFrame * rotationCFrame)
					local currentlySelectedButton = nil
					blockButton.MouseButton1Click:Connect(function()
						selectedBlock = block
						startPreview(selectedBlock)  -- Start the preview (ensure this function is defined correctly)
						placeItemButton.Visible = true  -- Show the "Place Item" button
						blocksShowcase.Visible = false  -- Close the blocksShowcase GUI
						if currentlySelectedButton then
							-- Reset the appearance of the previously selected button
							currentlySelectedButton.BackgroundColor3 = Color3.fromRGB(236, 236, 236)
						end
						-- Change the appearance of the current button to indicate selection
						blockButton.BackgroundColor3 = Color3.fromRGB(210, 210, 210)
						currentlySelectedButton = blockButton
					end)
				else
					
				end
			else
				
			end
		end
	else
		
	end
end

local cancelButton = buildConfigs:WaitForChild("CancelButton")  -- Assuming you have a CancelButton in your UI

local function handleCancelButtonClick()
	if isPreviewing and previewBlock then
		previewBlock:Destroy()
		previewBlock = nil
		isPreviewing = false
		selectedBlock = nil
		placeItemButton.Visible = false
		cancelButton.Visible = false
	end
end

if cancelButton then
	cancelButton.MouseButton1Click:Connect(handleCancelButtonClick)
end

UserInputService.InputBegan:Connect(function(input, isProcessed)
	if isProcessed then return end
	if input.KeyCode == Enum.KeyCode.Q then
		handleCancelButtonClick()
	end
end)


-- Connect the OpenGui button to toggle GUI
if openGuiButton then
	openGuiButton.MouseButton1Click:Connect(toggleGui)
end

-- Connect the Close button to close GUI
if closeButton then
	closeButton.MouseButton1Click:Connect(closeGui)
end

-- Optionally, you can use UserInputService for touch support
if UserInputService then
	UserInputService.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch and input.UserInputState == Enum.UserInputState.Begin then
			local touchPos = input.Position
			local guiPos = openGuiButton.AbsolutePosition
			local guiSize = openGuiButton.AbsoluteSize

			if touchPos.x >= guiPos.x and touchPos.x <= guiPos.x + guiSize.x and touchPos.y >= guiPos.y and touchPos.y <= guiPos.y + guiSize.y then
				toggleGui()
			end
		end
	end)
else
	
end
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local restoreEvent = ReplicatedStorage:WaitForChild("restoreEvent")

local restoreButton = script.Parent:FindFirstChild("RestoreButton")  -- Replace with the actual path to your Restore button

if restoreButton then
	restoreButton.MouseButton1Click:Connect(function()
		restoreEvent:FireServer()
	end)
end

The placement system script

-- Server-Side Script: PlacementSystem
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local myDataStore = DataStoreService:GetDataStore("PlayerBlocksDataStore")

local restoreEvent = Instance.new("RemoteEvent")
restoreEvent.Name = "restoreEvent"
restoreEvent.Parent = ReplicatedStorage

local placementEvent = Instance.new("RemoteEvent")
placementEvent.Name = "PlacementEvent"
placementEvent.Parent = ReplicatedStorage

-- Table to store placed blocks for each player
local playerBlocks = {}
local playerPlots = {}

local function clearPlayerPlot(userId)
	local oldPlotIdentifier = playerPlots[userId]
	if oldPlotIdentifier then
		local oldPlot = game.Workspace.Plots:FindFirstChild(oldPlotIdentifier)
		if oldPlot then
			for _, child in pairs(oldPlot:GetChildren()) do
				child:Destroy()
			end
		end
		playerPlots[userId] = nil -- Remove the plot from the playerPlots table
	end
end

-- Helper function to find a block in groups
local function findBlockInGroups(group, blockName)
	for _, child in pairs(group:GetChildren()) do
		if child.Name == blockName then
			return child
		elseif child:IsA("Folder") or child:IsA("Model") then
			local foundBlock = findBlockInGroups(child, blockName)
			if foundBlock then return foundBlock end
		end
	end
	return nil
end

local function onBlockPlaced(player, action, blockName, plotIdentifier, position, rotation)
	print("Received on server - Position:", position, "Rotation:", rotation)
	-- Ensure position and rotation are valid before proceeding
	if not position or not rotation then
		warn("Position or rotation is nil for onBlockPlaced.")
		return
	end

	-- Clear the old plot before setting a new one
	clearPlayerPlot(player.UserId)
	-- Now set the new plot
	playerPlots[player.UserId] = plotIdentifier

	local builder = ReplicatedStorage:FindFirstChild("Builder")
	local blockFound = findBlockInGroups(builder, blockName)

	if action == "preview" then
		-- Handle preview action
	elseif action == "place" then
		if blockFound then
			local blockToPlace = blockFound:Clone()
			local plotsFolder = game.Workspace:FindFirstChild("Plots")
			local plot = plotsFolder and plotsFolder:FindFirstChild(plotIdentifier) or nil

			if plot then
				-- Use the position and rotation provided by the player
				local targetPosition = plot.Position + Vector3.new(position.X, position.Y, position.Z)
				local targetRotation = CFrame.Angles(math.rad(rotation.X), math.rad(rotation.Y), math.rad(rotation.Z))
				local newCFrame = CFrame.new(targetPosition) * targetRotation
				blockToPlace:SetPrimaryPartCFrame(newCFrame)

				blockToPlace.Parent = plot

				-- If the block to place is a Model, anchor all its parts
				if blockToPlace:IsA("Model") then
					for _, part in pairs(blockToPlace:GetDescendants()) do
						if part:IsA("Part") then
							part.Anchored = true
						end
					end
				elseif blockToPlace:IsA("Part") then
					blockToPlace.Anchored = true
				end

				if not playerBlocks[player.UserId] then
					playerBlocks[player.UserId] = {}
				end

				table.insert(playerBlocks[player.UserId], blockToPlace)
			end
		end
	end
end

-- Function to save player blocks
local function savePlayerBlocks(player)
	if not player then
		
		return
	end
	local userId = player.UserId
	if not userId then
		
		return
	end
	local blocks = playerBlocks[userId]
	local plotIdentifier = playerPlots[userId]
	if not blocks and not plotIdentifier then
		-- You can use print instead of warn if you prefer.
		
		return
	elseif not blocks then
		
		return
	elseif not plotIdentifier then
		
		return
	end

	local plot = game.Workspace.Plots:FindFirstChild(plotIdentifier)
	if not plot then
		
		return
	end

	if blocks and plot then
		local plotPosition = plot.Position
		local blockData = {}

		for _, block in pairs(blocks) do
			local position
			if block:IsA("Part") then
				position = block.Position - plotPosition
			elseif block:IsA("Model") and block.PrimaryPart then
				position = block.PrimaryPart.Position - plotPosition
			else
				
				position = Vector3.new(0, 0, 0)
			end
			table.insert(blockData, {Name = block.Name, Position = {position.X, position.Y, position.Z}})
		end

		local jsonBlockData = HttpService:JSONEncode(blockData)

		local success, errorMessage = pcall(function()
			myDataStore:SetAsync(userId, jsonBlockData)
		end)

		if not success then
			
		else
			
		end
	end
end
--restore blocks using json
local function findBlockInGroups(group, blockName)
	for _, child in pairs(group:GetChildren()) do
		if child.Name == blockName then
			return child
		elseif child:IsA("Folder") or child:IsA("Model") then
			local foundBlock = findBlockInGroups(child, blockName)
			if foundBlock then return foundBlock end
		end
	end
	return nil
end
-- Restore blocks using json
local function restoreBlocks(player, plotName)
	if not player then
		
		return
	end
	
	local userId = player.UserId
	clearPlayerPlot(userId)
	local plotIdentifier = plotName or playerPlots[userId]
	playerPlots[userId] = plotIdentifier
	local plot = game.Workspace.Plots:FindFirstChild(plotIdentifier)

	if plot then
		local plotPosition = plot.Position
		local success, jsonBlockData = pcall(function()
			return myDataStore:GetAsync(userId)
		end)

		if success and jsonBlockData then
			
			local blockData
			local decodeSuccess, errorMessage = pcall(function()
				blockData = HttpService:JSONDecode(jsonBlockData)
			end)

			if not decodeSuccess then
				
				return
			end

			

			for _, data in pairs(blockData) do
				local blockToPlace = findBlockInGroups(ReplicatedStorage.Builder, data.Name)
				if not blockToPlace then
					
					return
				end

				blockToPlace = blockToPlace:Clone()
				local relativePosition = Vector3.new(unpack(data.Position))
				if blockToPlace then
					if blockToPlace:IsA("Model") and blockToPlace.PrimaryPart then
						blockToPlace:SetPrimaryPartCFrame(CFrame.new(plotPosition + relativePosition))
					elseif blockToPlace:IsA("Part") then
						blockToPlace.Position = plotPosition + relativePosition
					end
					blockToPlace.Parent = plot

					-- Handle anchoring correctly for both Model and Part
					if blockToPlace:IsA("Model") then
						for _, part in pairs(blockToPlace:GetDescendants()) do
							if part:IsA("Part") then
								part.Anchored = true
							end
						end
					elseif blockToPlace:IsA("Part") then
						blockToPlace.Anchored = true
					else
						
					end

				else
					
				end
			end
		else
			
		end
	else
		
	end
end

-- Connect the function to handle the event
placementEvent.OnServerEvent:Connect(onBlockPlaced)

-- Listen for the restore event
restoreEvent.OnServerEvent:Connect(restoreBlocks)

-- Listen for when a player leaves to save their blocks
Players.PlayerRemoving:Connect(savePlayerBlocks)

Here is a gif of the issue i’m facing.
If anyone can assist in fixing that would be amazing.
placement

I’ve managed to fix it by changing the raycast correctly here is the code:

local function updatePreview()
    if isPreviewing and previewBlock then
        local mousePosition = UserInputService:GetMouseLocation()
        local camera = workspace.CurrentCamera
        local ray = camera:ScreenPointToRay(mousePosition.X, mousePosition.Y)
        local raycastParams = RaycastParams.new()
        raycastParams.FilterDescendantsInstances = {user.Character, previewBlock}
        raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

        local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 500, raycastParams) -- Adjust the distance as needed
        if raycastResult then
            local hitPosition = raycastResult.Position
            local placementPosition = hitPosition + Vector3.new(0, previewBlock.PrimaryPart.Size.Y / 2, 0) -- Adjust the height offset as needed
            previewBlock:SetPrimaryPartCFrame(CFrame.new(placementPosition))
        end
    end
end

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