[SOLVED] How to Randomise Tagged Objects on different Tagged Handlers?

I do apologies if the title seems confusing; I’m about to explain the issue. The issue is that the Object (Banana) is being Tagged on the same Tagged Handlers (Trees). How to make a single Banana get Tagged on a random Tree each time you play the game?

Server Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")

local EventsFolder = ReplicatedStorage:WaitForChild("EVENTS")
local CollectRequest_Event = EventsFolder:WaitForChild("CollectRequest")
local Collect_Event = EventsFolder:WaitForChild("Collect")

local bananas = CollectionService:GetTagged("Banana")
local bananaTrees = CollectionService:GetTagged("BananaTree")

local BANANA_CONFIG = {
	MAX_BANANAS = 5,
	MIN_BANANAS = 0,
	BANANA_DISTANCE = 1,
}

-- Shuffle function to randomize table order
local function shuffle(t)
	for i = #t, 2, -1 do
		local j = math.random(1, i)
		t[i], t[j] = t[j], t[i]
	end
end

local function TagBananas()
	-- Clear all existing Banana Tags to prevent duplicates on same BananaTree
	for _, banana in CollectionService:GetTagged("Banana") do
		CollectionService:RemoveTag(banana, "Banana")
	end

	if #bananaTrees == 0 then return end

	-- Shuffle bananaTrees to randomize the tree selection order
	shuffle(bananaTrees)

	local possibleBananas = {}

	-- Step 1: Collect one candidate Banana from each BananaTree, along with its position
	for _, bananaTree in bananaTrees do
		local treeBananas = {}

		for _, descendant in bananaTree:GetDescendants() do
			if descendant:IsA("BasePart") and descendant.Name == "Banana" then
				table.insert(treeBananas, descendant)
			end
		end

		if #treeBananas > 0 then
			local chosenBanana = treeBananas[1]
			table.insert(possibleBananas, {
				Banana = chosenBanana,
				Position = chosenBanana.Position
			})
		end
	end

	if #possibleBananas == 0 then
		warn("No available Bananas to Tag.")
		return
	end

	-- Step 2: Shuffle candidates to randomize selection order
	shuffle(possibleBananas)

	local taggedBananaPositions = {}
	local numToTag = math.clamp(BANANA_CONFIG.MAX_BANANAS, BANANA_CONFIG.MIN_BANANAS, #possibleBananas)
	local taggedCount = 0

	local function AreBananasTooClose(pos1, pos2)
		return (pos1 - pos2).Magnitude < BANANA_CONFIG.BANANA_DISTANCE
	end

	-- Step 3: Tag Bananas ensuring minimum distance spacing
	for _, entry in possibleBananas do
		local currentPos = entry.Position
		local isTooClose = false

		for _, taggedPos in taggedBananaPositions do
			if AreBananasTooClose(taggedPos, currentPos) then
				isTooClose = true
				print("Skipping Banana at", entry.Banana:GetFullName(), "due to proximity")
				break
			end
		end

		if not isTooClose then
			CollectionService:AddTag(entry.Banana, "Banana")
			table.insert(taggedBananaPositions, currentPos)
			taggedCount += 1
			print("Tagged Banana:", entry.Banana:GetFullName())
		end

		if taggedCount >= numToTag then
			break
		end
	end

	-- Fallback: guarantee at least one Banana is Tagged
	if taggedCount == 0 and #possibleBananas > 0 then
		local fallback = possibleBananas[1]
		CollectionService:AddTag(fallback.Banana, "Banana")
		taggedCount = 1
		print("Fallback tagged Banana:", fallback.Banana:GetFullName())
	end

	if taggedCount < numToTag then
		warn("Only Tagged", taggedCount, "Banana; spacing rule blocked the other Bananas.")
	end
end

CollectRequest_Event.OnServerEvent:Connect(function(player, bananas)
	if bananas and CollectionService:HasTag(bananas, "Banana") then
		local sound = bananas:FindFirstChild("Grab")
		if not sound then
			sound = Instance.new("Sound")
			sound.Name = "Grab"
			sound.SoundId = "rbxassetid://444895479"
			sound.Volume = 0.5
			sound.RollOffMode = Enum.RollOffMode.Linear
			sound.RollOffMaxDistance = 10
			sound.RollOffMinDistance = 5
			sound.PlayOnRemove = true
			sound.Parent = bananas:IsA("BasePart") and bananas or bananas:FindFirstChildWhichIsA("BasePart") or bananas
		end

		if bananas and bananas.Parent then
			sound:Play()
			bananas:Destroy()
		end	
		
		task.delay(0.1, function()
			local remaining = #CollectionService:GetTagged("Banana")
			if remaining == 0 then
				for _, plr in ipairs(Players:GetPlayers()) do
					Collect_Event:FireClient(plr, "Victory")
				end
				for _, plr in ipairs(Players:GetPlayers()) do
					plr:Kick("All bananas have been collected!")
				end
			else
				Collect_Event:FireClient(player, remaining)
			end
		end)
	end
end)

TagBananas()

I’m sorry if my Script is messy, I haven’t organised the Script because I’m focused on trying to fix thid issue.

I would record the issue, however I simply cannot do that right now - I’m sleeping.

Don’t reinvent the wheel
Random exists

local rand = Random.new()
local tab = {}
rand:Shuffle(tab)

Also you are looking for table.clone() probably.

I’m still learning scripting, it’s not meant to clone the Tags to the Bananas but rather scan for random Banananas to Tag.

Current Server Script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")

local EventsFolder = ReplicatedStorage:WaitForChild("EVENTS")
local CollectRequest_Event = EventsFolder:WaitForChild("CollectRequest")
local Collect_Event = EventsFolder:WaitForChild("Collect")

local bananas = CollectionService:GetTagged("Banana")
local bananaTrees = CollectionService:GetTagged("BananaTree")

local BANANA_CONFIG = {
	MAX_BANANAS = 2,
	MIN_BANANAS = 0,
}

local function shuffle(tbl)
	local n = #tbl
	for i = n, 2, -1 do
		local j = math.random(1, i)
		tbl[i], tbl[j] = tbl[j], tbl[i]
	end
	return tbl
end

local function clamp(val, lower, upper)
	return math.max(lower, math.min(upper, val))
end

local function TagBananas()
	-- Clear existing "Banana" tags
	for _, banana in pairs(CollectionService:GetTagged("Banana")) do
		CollectionService:RemoveTag(banana, "Banana")
	end

	local bananaTrees = CollectionService:GetTagged("BananaTree")

	if #bananaTrees == 0 then
		warn("No BananaTrees found.")
		return
	end

	shuffle(bananaTrees)

	local taggedCount = 0
	local maxToTag = clamp(BANANA_CONFIG.MAX_BANANAS, 0, #bananaTrees)

	for i = 1, maxToTag do
		local tree = bananaTrees[i]

		local treeBananas = {}
		for _, descendant in pairs(tree:GetDescendants()) do
			if descendant:IsA("BasePart") and descendant.Name == "Banana" then
				table.insert(treeBananas, descendant)
			end
		end

		if #treeBananas > 0 then
			local chosenBanana = treeBananas[math.random(1, #treeBananas)]
			CollectionService:AddTag(chosenBanana, "Banana")
			taggedCount = taggedCount + 1
			print("Tagged Banana:", chosenBanana:GetFullName(), "from BananaTree:", tree:GetFullName())
		else
			warn("No Bananas found in BananaTree:", tree:GetFullName())
		end
	end

	if taggedCount == 0 then
		warn("No Bananas were tagged!")
	end
end

CollectRequest_Event.OnServerEvent:Connect(function(player, bananas)
	if bananas and CollectionService:HasTag(bananas, "Banana") then
		local sound = bananas:FindFirstChild("Grab")
		if not sound then
			sound = Instance.new("Sound")
			sound.Name = "Grab"
			sound.SoundId = "rbxassetid://444895479"
			sound.Volume = 0.5
			sound.RollOffMode = Enum.RollOffMode.Linear
			sound.RollOffMaxDistance = 10
			sound.RollOffMinDistance = 5
			sound.PlayOnRemove = true
			sound.Parent = bananas:IsA("BasePart") and bananas or bananas:FindFirstChildWhichIsA("BasePart") or bananas
		end

		if bananas and bananas.Parent then
			sound:Play()
			bananas:Destroy()
		end	
		
		task.delay(0.1, function()
			local remaining = #CollectionService:GetTagged("Banana")
			if remaining == 0 then
				for _, player in ipairs(Players:GetPlayers()) do
					Collect_Event:FireClient(player, "Victory")
					player:Kick("All bananas have been collected!")
				end
			else
				Collect_Event:FireClient(player, remaining)
			end
		end)
	end
end)

TagBananas()

Current Map Template: (will expand when issue is fixed)

KEY:
BT = BananaTree
T = Tree


T > BT > T > BT
T > T > BT > T
T > BT > T > T

For some reason the shuffle() doesn’t tag the Bananas from a random BananaTree in random order but rather "first-to-last) order, how to fix?

local function shuffle(tbl)
	local n = #tbl
	for i = n, 2, -1 do
		local j = math.random(1, i) -- I think the issue occurs here
		tbl[i], tbl[j] = tbl[j], tbl[i]
	end
	return tbl
end

The order is applied like this >


T > (BT) > T > (BT)
T > T > BT > T
T > BT > T > T

The () indicate the Bananas are tagged on those BananaTrees.

I fixed the issue, I didn’t Tag every BananaTree D:<

Fixed Server Script:

--// SERVICES
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")

--// EVENTS
local EventsFolder = ReplicatedStorage.EVENTS
local CollectRequest_Event = EventsFolder.CollectRequest
local Collect_Event = EventsFolder.Collect

--// BANANA CONFIGURATION
local BANANA_CONFIG = {
	MAX_BANANAS = 4,
	MIN_BANANAS = 0,
}

--// RANDOM SHUFFLE SET
local function shuffle(t)
	local n = #t
	for i = n, 2, -1 do
		local j = math.random(1, i)
		t[i], t[j] = t[j], t[i]
	end
	return t
end

--// CLAMPING SET
local function clamp(v, lower, upper)
	return math.max(lower, math.min(upper, v))
end

--// MAIN FUNCTION
local function TagBananas()
	-- Clear existing "Banana" tags
	for _, banana in CollectionService:GetTagged("Banana") do
		CollectionService:RemoveTag(banana, "Banana")
	end

	local bananaTrees = CollectionService:GetTagged("BananaTree")
	if #bananaTrees == 0 then
		warn("No BananaTrees found.")
		return
	end

	local candidateBananas = {}

	for _, bananaTree in bananaTrees do
		local bananasInTree = {}

		for _, descendant in bananaTree:GetDescendants() do
			if descendant:IsA("BasePart") and descendant.Name == "Banana" then
				table.insert(bananasInTree, descendant)
			end
		end

		if #bananasInTree > 0 then
			local taggedBananas = bananasInTree[math.random(1, #bananasInTree)]
			table.insert(candidateBananas, taggedBananas)
		else
			warn("BananaTree has no bananas:", bananaTree:GetFullName())
		end
	end

	if #candidateBananas == 0 then
		warn("No Bananas found in BananaTrees.")
		return
	end

	shuffle(candidateBananas)
	local maxToTag = clamp(BANANA_CONFIG.MAX_BANANAS, 0, #candidateBananas)

	for i = 1, maxToTag do
		local banana = candidateBananas[i]
		CollectionService:AddTag(banana, "Banana")
	end

	if maxToTag == 0 then
		warn("No Bananas were tagged!")
	else
		print("==== All Tagged Bananas ====")
		for _, b in CollectionService:GetTagged("Banana") do
			print(b:GetFullName())
		end
	end
end

--// COLLECT REQUEST SERVER
CollectRequest_Event.OnServerEvent:Connect(function(player, bananas)
	if bananas and CollectionService:HasTag(bananas, "Banana") then
		local sound = bananas:FindFirstChild("Grab")
		if not sound then
			sound = Instance.new("Sound")
			sound.Name = "Grab"
			sound.SoundId = "rbxassetid://444895479"
			sound.Volume = 0.5
			sound.RollOffMode = Enum.RollOffMode.Linear
			sound.RollOffMaxDistance = 10
			sound.RollOffMinDistance = 5
			sound.PlayOnRemove = true
			sound.Parent = bananas:IsA("BasePart") and bananas or bananas:FindFirstChildWhichIsA("BasePart") or bananas
		end
		
		if bananas and bananas.Parent then
			sound:Play()
			bananas:Destroy()
		end	

		task.delay(0.1, function()
			local remaining = #CollectionService:GetTagged("Banana")
			if remaining == 0 then
				for _, player in Players:GetPlayers() do
					Collect_Event:FireClient(player, "Victory") -- String message to indicate victory
					player:Kick("All bananas have been collected!")
				end
			else
				Collect_Event:FireClient(player, remaining)
			end
		end)
	end
end)

TagBananas()

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