Need Help With PickUp System

I am fairly new at scripting but I made a pickup system where a zone is made around a dropped object and if you enter the zone you have the ability to pick it up. However, when there’s two objects the pickup system picks both of them up. My idea was to check the distance of both objects and whichever was closest is the one that would be picked up. But I kinda don’t know how to do that. I am just having a problem with getting the distances of both objects and comparing them. How would I do that? Also What if there’s 3 objects or more?

Here’s the pickup system I made so far:

Here’s the pickup system if there’s two objects

I just need help with how to do it. Any and all help would be appreciated. : )

Can i see your pickup script?

[char]

1 Like

Here is my script

--Var
--Objects
local Tool = script.Parent.Parent
local Handle = script.Parent
local gun = Tool:WaitForChild("gun")
local TouchInterest = script.Parent:FindFirstChild("TouchInterest")
local TouchField = Handle.TouchField

--Services


--MemoryVars
local PickUpKey = game.ReplicatedStorage:WaitForChild("PickUp").WeaponPickUpKey.Value

--Important Vars
local MaxPickUpDist = 30

--Events
local PickUpEvent = game.ReplicatedStorage:WaitForChild("PickUp").PickUp

--Destroy TouchInterest
if TouchInterest then
	if TouchInterest.Name == "TouchInterest" then
		TouchInterest:Destroy()
	end
end

--Pickup
local Container = script.Parent:WaitForChild("TouchField") 
local Zone = require(game.ReplicatedStorage.Zone)
local Zone1 = Zone.new(Container)
local PickUp = false
local PickUpGUI 
local Player
local rayloop = false
local canPickUp = false

Zone1.playerEntered:Connect(function(player)
	canPickUp = false
	if Tool.Parent.Name ~= player.Name then
		Player = player
		PickUpGUI = player.PlayerGui.GunPickUp.GunPickUpText
		PickUp = false
		--Ray
		local RayParams1 = RaycastParams.new()
		RayParams1.FilterDescendantsInstances = {gun, Handle, TouchField, Zone1}
		RayParams1.FilterType = Enum.RaycastFilterType.Blacklist
		RayParams1.IgnoreWater = false
		rayloop = true
		
		while rayloop == true do
		local Ray1 = Ray.new(gun.Position, player.Character.LowerTorso.Position - gun.Position, RaycastParams)
		local hit, position = game.Workspace:FindPartOnRayWithIgnoreList(Ray1, {gun, Handle, TouchField})
		
		if hit then
			if hit.Parent:FindFirstChild("Humanoid") then
				PickUpGUI.Text = "Press " .. tostring(PickUpKey) .. " To Pick Up " .. Tool.Name
				PickUpGUI.Visible = true
				PickUp = true
				canPickUp = true
			else			
				PickUp = false
				PickUpGUI.Visible = false
				canPickUp = false
				end
			else 
				PickUpGUI.Visible = false
				canPickUp = false
			end
			wait(0.5)
		end
	else
		PickUp = false
		PickUpGUI.Visible = false
		canPickUp = false
	end
end)

Zone1.playerExited:Connect(function(player)
	local PickUpGUI = player.PlayerGui.GunPickUp.GunPickUpText
	PickUpGUI.Visible = false
	PickUp = true
	rayloop = false
	canPickUp = false
end)

PickUpEvent.OnServerEvent:Connect(function(player)
	local Dist = player:DistanceFromCharacter(Tool.Handle.Position)
	
	if Dist < MaxPickUpDist then
		if canPickUp == true then
			if Tool then
			canPickUp = false
			Tool.Parent = player.Backpack
		end
	end
	end
end)

it’s messy but it works.

Instead of showing the pickup gui for each item that is close, iterate through which items can be picked up and then only enable the gui for the closest item.

1 Like

I’ll try that and see how it works out.

this is how you can find the closest item when you try to pick them up

local userInputService = game:GetService("UserInputService")
local player = game.Players.LocalPlayer
local pickupDistance = 10

local function Pickup()
	-- loop all items and get the closest
	local closestItem = nil
	local closestDistance = math.huge
	for i, child in ipairs(workspace.Items) do
		local distance = player:DistanceFromCharacter(child.Position)
		if distance == 0 then continue end
		if distance > closestDistance then continue end
		closestItem = child
		closestDistance = distance
	end
	-- if no item was found exit
	if closestItem == nil then return end
	-- if the closest items distance is more then pickupdistance then exit
	if closestDistance > pickupDistance then return end
	print("PICKUP:", closestItem.Name, closestDistance)
end

userInputService.InputBegan:Connect(function(input, processed)
	-- if the input was processed then exit the function early and do nothing
	if processed == true then return end
	-- if the input was not a keyboard then exit the function early and do nothing
	if input.UserInputType ~= Enum.UserInputType.Keyboard then return end
	-- if the input was not key E then exit the function early and do nothing
	if input.KeyCode ~= Enum.KeyCode.E then return end
	-- call the pickup function
	Pickup()
end)

If this is the script for a single tool, then I’m sorry to say, but there’s a much better way to do it. You can have a local script inside of the player and check for tools in a radius using WorldRoot:GetPartBoundsInRadius. Once you get a list of the tools in a radius, check which one is the closest using:

-- Returns the tool closest to a given position
local function getClosestTool(position: Vector3, radius: number): Tool
	local currentDistance: number = radius
	local currentTool
	
	for _, part: BasePart in pairs(workspace:GetPartBoundsInRadius(position, radius)) do
		local tool: Tool? = part:FindFirstAncestorWhichIsA("Tool")
		if (part.Name == "Handle") and tool then
			local distance: number = (part.Position - position).Magnitude
			if (distance < currentDistance) then
				currentDistance = distance
				currentTool = tool
			end
		end
	end
	return currentTool
end
2 Likes

Welp I found an answer on how to do this.