How do I delete all objects of a certain type when their count exceeds a certain number?

Hello everyone!

I’ve been working on a factory in my game that works a lot like a tycoon; a dropper drops items, where they cruise along a conveyor belt until falling into an incinerator.

The objects aren’t just normal bricks, they’re actually random models that range from memes to classic Roblox items.

The issue is, sometimes they can get stuck on corners/snags. And when this happens, it starts a sort of traffic jam. One objects stops the one behind it from passing, which stops the next and so on. In the end, the entire system can get clogged. This not only looks bad, but could be detrimental to the game’s performance, which is why I’ve tried to fix it.

One way I’ve sought to remedy this is by offering a button that allows a player to destroy all the items on the conveyor with a 30 second cooldown, which is working fine and I intend to keep. However, I do not wish to hand the responsibility of keeping one of my game features to the player, which is why I am trying to implement a new system.

My current setup is that all objects are cleared after 3 minutes, set up in a while true loop. While this works fine, I am looking for a way to count the amount of objects in the workspace and, if it exceeds a set number, clear them all. That way, the deletion can happen only when a player decides or when the items exceed a limit.

I already have the code for deleting the objects, I just can’t seem to find a way to check how many there are and destroy them if they exceed a certain value. Does anyone have any ideas how to do this?

Code that automatically destroys the objects after 3 mins:

while true do
	wait(180)
	for i,v in pairs (workspace:GetDescendants()) do
		if v.Name == "Clonedobject" then
			v:Destroy()
		end
	end
	print("Destroyed all objects!")
end

code for manual deletion

local ProximityPrompt = script.Parent
local sound = script.Parent.Boom

ProximityPrompt.Triggered:Connect(function(plr)
	ProximityPrompt.Enabled = false
	
	for i,v in pairs (workspace:GetDescendants()) do
		if v.Name == "Clonedobject" then
			v:Destroy()
		end
	end
	sound:Play()
	wait(30)
	
	ProximityPrompt.Enabled = true	
end)

UPDATE: I thought it would be a good idea to add the code that spawns said items:

local replicatedStorage = game.ReplicatedStorage







while true do
	local randomnumber = math.random(0, 16)

	if randomnumber == 0 then
		main = replicatedStorage.Tycoonparts["hacker"]
	end

	if randomnumber == 1 then
		main = replicatedStorage.Tycoonparts["car"]
	end
	
	if randomnumber == 2 then
		main = replicatedStorage.Tycoonparts["fly"]
	end
	
	if randomnumber == 3 then
		main = replicatedStorage.Tycoonparts["cartridecart"]
	end
	
	if randomnumber == 4 then
		main = replicatedStorage.Tycoonparts["cone"]
	end
	
	if randomnumber == 5 then
		main = replicatedStorage.Tycoonparts["container"]
	end
	
	if randomnumber == 6 then
		main = replicatedStorage.Tycoonparts["eye"]
	end
	
	if randomnumber == 7 then
		main = replicatedStorage.Tycoonparts["fakespawn"]
	end

	if randomnumber == 8 then
		main = replicatedStorage.Tycoonparts["minihouse"]
	end

	if randomnumber == 9 then
		main = replicatedStorage.Tycoonparts["mogus cube"]
	end

	if randomnumber == 10 then
		main = replicatedStorage.Tycoonparts["robonoob "]
	end

	if randomnumber == 11 then
		main = replicatedStorage.Tycoonparts["selectbox"]
	end	
	
	if randomnumber == 12 then
		main = replicatedStorage.Tycoonparts["swordinstone"]
	end

	if randomnumber == 13 then
		main = replicatedStorage.Tycoonparts["troll"]
	end

	if randomnumber == 14 then
		main = replicatedStorage.Tycoonparts["tv"]
	end

	if randomnumber == 15 then
		main = replicatedStorage.Tycoonparts["upsidedown"]
	end

	if randomnumber == 16 then
		main = replicatedStorage.Tycoonparts["brick"]
	end	
	
	wait(5)
	local newobj = main:Clone()
	newobj.Name = "Clonedobject"
	newobj.Parent = workspace
	newobj:PivotTo(game.Workspace.Partdestination.CFrame)
end

(please excuse my primitive way of doing this; it does in fact work and I came up with it by myself! :slight_smile: )

Thanks in advance,
~innit_2winnit

What I would do is add to a value every time a part is spawned, and take away every time it is deleted. Next I would add a heartbeat function that checks if the value is above a certain point. Example:

local runService = game:GetService("RunService")
local value = script.PartChecker --Where the value is

RunService.Heartbeat:Connect(function(delete)
	if value.Value >= 200 then--Pick number of parts 
          --Rest here
end)

that is actually a great idea! thank you, let me try to implement that now.

update: just realised I can’t add that because the objects are naturally deleted by falling into the void under the map; there’s no part that does the deleting (because I couldn’t find a way to delete a model using a part and felt that the void was a fine alternative).

CollectionService would help nicely here. Just tag every item and delete all items with those tags every 3 minutes. Same thing with the manual deletion. Most methods from CollectionService return arrays, so simple pairs loops can efficiently get all of your items without having to utilize methods such as GetChildren and GetDescendants.

Keep in mind your current script is deleting all items every 3 minutes, not just the tycoon of the player who is wanting to delete their items. If you want to fix this, just add another tag using CollectionService that is the player’s UserId and check for that.

These principles can be used to check for the amount of items that currently exist as well with the GetInstanceAddedSignal event:

local CollectionService = game:GetService("CollectionService")
local Player = ... -- Exercise for the reader
local MAX_ITEMS = 200

-- Looped deletion

task.spawn(function()
  while task.wait(180) do
    local TaggedItems = CollectionService:HasTag("IS_ITEM")
    for _, item in pairs(TaggedItem) do
      item:Destroy()
    end
  end
end)

-- Manual deletion

ProximityPrompt.Triggered:Connect(function(Player)
  local TaggedItems = CollectionService:HasTag("IS_ITEM")
  for _, item in pairs(TaggedItem) do
    -- Check if the items we are wanting to delete belong to the requesting player
    if item:HasTag(tostring(Player.UserId)) then
      item:Destroy()
    end
  end
end)

-- Check for maximum items
---- This event will fire whenever an item is tagged
CollectionService.GetInstanceAddedSignal:Connect(function("IS_ITEM")
  local TaggedItems = CollectionService:HasTag("IS_ITEM")
  if #TaggedItems > MAX_ITEMS then
    ...
  end
end)

-- *Code may not be correct, proof of concept

Documentation:

You could add a part with a script kinda like this:

local part = script.Parent

part.Touched:Connect(function(delete)
    --Delete magic here
end)

I would add an if statement with tags if you want to prevent the player from getting deleted, but it would be a fun easter egg if you could. You could also add a fade animation with a simple for loop decreasing the transparency.

Yep! That is intended behavior (my game is not a tycoon game; rather it has a single factory that acts like a tycoon without the purchasing features).

I’m also not quite sure how to implement this using my item spawning system (I apologise for not including it originally - I knew i forgot something! :sweat_smile: ). If you could push me in the right direction that would be great but it’s absolutely no problem if you can’t! Thank you for your time :slight_smile:

1 Like

That was my original plan; increment an intvalue when an item is spawned, and decrement it when an item is deleted. The problem is I never found a way to program the “delete magic” :sweat_smile:, mainly because my items drop as models and not as parts which prevents me from finding specific solutions.
That being said, I really appreciate your time and support in helping! :slight_smile:

Make a folder in workspace. Then, make a script that makes a folder in that folder for each player. We then put the cloned objects for the player in their own folder. This makes it very easy to keep track of those items when you are developing the game later and it also makes them very easy to count.

This code creates a folder for each player in the clonedObjects folder. It also has a function to count the amount of objects that a player has given their name, and it has a function to get a player’s folder given their name. Put this at the top of your script so you can put the cloned objects into the player’s folder.

local clonedObjects = game.Workspace.clonedObjects -- folder where all the objects will go

game.Players.PlayerAdded:Connect(function(plr) -- detect when a player joins and get that player
	local plrFolder = Instance.new("Folder", clonedObjects) -- create the player's own folder in the cloned objects folder
	plrFolder.Name = plr.Name -- set the folder's name to the player's name so it is easy to find later
end)

function getPlayerFolder(name) -- function for getting a player's folder given their name
	return clonedObjects:FindFirstChild(name) -- searches for a child with the player's name in the cloned objects folder
end

function getObjects(plrName) -- function that counts the objects that a player has
	local plrFolder = getPlayerFolder(plrName) -- get the player's folder
	
	if plrFolder then -- check if it exists so we dont have an error
		return #plrFolder:GetChildren() -- this returns the length of array of the folder's children which is the amount of objects in it
	else -- if it doesnt exist then we warn ourselves so we can fix it and return false
		warn("No player found for " .. plrName)
		return false
	end
end

Sorry, I just realized that it was only one tycoon.

Make a folder in workspace to keep track of the objects. This code has a function to count the amount of objects currently in the folder.

local objects = {}
local clonedObjects = game.Workspace.clonedObjects -- folder where all the objects will go

function getObjects() -- function to get amount of objects
	return #clonedObjects:GetChildren() -- returns the length of the array of the folder's children which is the amount of things in it
end

When you create another object, add the object to the objects table.

table.insert(objects,object) -- insert the object to the last slot in the table so that the first slot is the oldest one

Now, you can delete the oldest spawned object.

if getObjects() > maxObjects then -- set maxobjects to your max objects
  objects[1]:Destroy() -- destroy the oldest object
  table.remove(objects,1) -- remove it from the table
end

Why not scale the instances so they are all a single, acceptable size? You could also define custom collision behaviour with the conveyor belt’s walls/borders.

Thank you so much for your help!
While I didn’t follow your steps exactly, your example code helped me to find the solution I was looking for! :slight_smile: Thank you ever so much for your time!

Here’s what I did:

Like you said, I made a folder for the objects to keep track of them.

newobj.Parent = workspace.ClonedObjects

Then, I made a script in the folder that infinitely checks the number of objects in the folder, and deletes them ALL if it exceeds a certain value:

local clonedobjs = script.Parent
while true do
	wait()
	num = #clonedobjs:GetChildren()
	if num > 25 then
		for i,v in pairs (workspace.ClonedObjects:GetDescendants()) do
			if v.Name == "Clonedobject" then
				v:Destroy()
			end
		end
		print("Destroyed", num, "objects!")
	end
end

And it works flawlessly! Once again, I thank your ever so much for your help. I will mark your answer as the solution :slight_smile:

~innit_2winnit

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