Delete unnecessary duplicate parts with this script

Greetings fellow developers! :wave: :wave: :wave:

What I’m about to show you is a script that you can simply paste into the command bar and it will do the job of deleting unnecessary duplicate parts for you. It happens very often when we duplicate a part and then the part never gets used, or you just accidently press ctrl+d and it leaves two parts of the same type on top of each other. And when you play your game, your character starts moving 1ft every minute because you have all these unnecessary parts all over the place causing your game to lag. This script has been tested A LOT, so don’t worry about your baseplate disappearing or all of your terrain going missing. But if for some reason something goes wrong, please leave a reply explaining the issue you are facing so I can fix the problem. If your game freezes, don’t worry. It is a very common thing for it to do. If your game is really big then it might take a couple of minutes until it unfreezes. Just keep your hands off your device and you should be good to go. And without further ado, here is the script:

local ChangeHistory = game:GetService("ChangeHistoryService")

local amountDeleted = 0

local Check = Instance.new("IntValue")
Check.Name = "Check"

warn("KEEP HANDS AWAY FROM MOUSE AND KEYBOARD. YOU WILL RECEIVE A MESSAGE WITH THE AMOUNT OF OBJECTS DELETED WHEN THE PROCESS IS DONE.")
task.wait(0.4)

local function destroyDuplicate()
	amountDeleted = 0
	for a, b in pairs(game.Workspace:GetDescendants()) do
		local success, errormessage = pcall(function()
			if b:IsA("BasePart") then
				Check.Parent = b
			end
		end)
		for c, d in pairs(game.Workspace:GetDescendants()) do
			if d:IsA("BasePart") and Check.Parent ~= d and d.CanCollide == true then
					local success, errormessage = pcall(function()
					if d.Position == b.Position and d.Size == b.Size and d.Orientation == b.Orientation and d.Name == b.Name and d.CastShadow == b.CastShadow and d.Transparency == b.Transparency and d.Material == b.Material and #d:GetTouchingParts() ~= 0 and #b:GetTouchingParts() ~= 0 then
						if #d.Parent:GetChildren() == 1 then
							d.Parent.Parent = nil
							amountDeleted = amountDeleted + 2
						else
							d.Parent = nil
							amountDeleted = amountDeleted + 1
						end
					end
				end)
				elseif d:IsA("BasePart") and Check.Parent ~= d and d.CanCollide == false then
					local success, errormessage = pcall(function()
						local OverPars1 = OverlapParams.new()
						OverPars1.FilterType = Enum.RaycastFilterType.Whitelist
						OverPars1.FilterDescendantsInstances = {d}
						local OverPars2 = OverlapParams.new()
						OverPars2.FilterType = Enum.RaycastFilterType.Whitelist
						OverPars2.FilterDescendantsInstances = {b}
						if d.Position == b.Position and d.Size == b.Size and d.Orientation == b.Orientation and d.Name == b.Name and d.CastShadow == b.CastShadow and d.Transparency == b.Transparency and d.Material == b.Material and #game.Workspace:GetPartBoundsInBox(d.CFrame, d.Size, OverPars1) ~= 0 and #game.Workspace:GetPartBoundsInBox(b.CFrame, b.Size, OverPars2) ~= 0 then
							if #d.Parent:GetChildren() == 1 then
								d.Parent.Parent = nil
								amountDeleted = amountDeleted + 2
							else
								d.Parent = nil
								amountDeleted = amountDeleted + 1
							end
						end
					end)
				end
			end
		end
	if amountDeleted == 1 then
		Check.Parent = nil
		print(amountDeleted.." Object was removed...")
	else
		Check.Parent = nil
		print(amountDeleted.." Objects were removed...")
	end
end

destroyDuplicate()

ChangeHistory:SetWaypoint("Delete Duplicate Objects")

When it is done doing its job, it will print the amount of objects it deleted. I tested this on a game I am currently working on, and it was able to delete 304 duplicate objects including meshes, parts, models, etc.

If you are still confused and don’t know what I’m talking about, it’s alright. Here’s a super short video presentation of what the script does: Short Video Presentation
As you can see, the script deleted the other part which was on top and printed the amount of objects that were removed.

If you are still a little hesitant and don’t think this script will fully work, here’s a Roblox Studio file where you can test the code yourself and see some results: DeleteDuplicatesScript - Presentation.rbxl (110.8 KB)

If you want to save the code in your computer so you don’t have to be going back and forth to this post, here’s a .txt file you can download: DeleteDuplicatesScript - Paste In Command Bar.txt (2.5 KB)

If something goes wrong, and you want to undo the changes, don’t panic. If you press the undo button everything should go back to normal.

And with that said, do whatever you want with the script. Oh, and also, thank you for taking time out of your day to read about my hopefully helpful for you creation. :slightly_smiling_face: :slightly_smiling_face: :slightly_smiling_face:

13 Likes

add a confirmation if they want to delete something. this resource should also support an undo feature, there’s also this which you should really absolutely use.

2 Likes

Would ChangeHistoryService work in this script? I would love to add it in, but I’m not sure because it says ChangeHistoryService is for plugin developers to use.

Command bar scripts can use plugin features

1 Like

Got it. I’ll play around with it and see how it goes.

Script has been updated and now has an undo feature. Big thanks to @reimakesgames, and @GibusWielder, for the feedback.

Post has been edited and now has the new script.

1 Like

It was a bad idea to run this on a place with 500k+ instances :joy:

This has saved my day, Thank you!

I have found a much better way to do this for anyone who has the same issue as me but knew that this fix was too over-engineered. (No offense to OP)

task.wait(15) -- Waits for everything to load

nameslist = {}

for i, v in pairs(workspace:GetChildren()) do
    if table.find(nameslist,v.Name) ~= nil then
        print(v)
        v:Remove()
    else
        nameslist[#nameslist+1] = v.Name
    end
    task.wait()
end

By the way, you should be using :Destroy or the objects will be left in memory.

This removes the objects with the same name. Not duplicates. Also add ChangeHistory back: ChangeHistoryService | Documentation - Roblox Creator Hub

You’re not using :GetDescendants either which means duplicates can still remain.
I recommend sticking to the original script.

1 Like

I just normally use this, since sometimes things have the same name but might not be overlapping

--[[ Use Run Command ]]--

for _, element in ipairs(game.Workspace:GetDescendants()) do 
	if not element or not element.Parent then continue end 
	if not element:IsA("Part") then continue end
	local touchingparts = element:GetTouchingParts() 
	for _, part in ipairs(element:GetTouchingParts()) do
		if element.Size == part.Size and element.CFrame == part.CFrame and not element.Locked then 
			part:Destroy() 
		end 
	end
end

Alternatively with the OP’s counter

--[[ Use Run Command ]]--

local deleteCount = 0

for _, element in ipairs(game.Workspace:GetDescendants()) do 
	if not element or not element.Parent then continue end 
	if not element:IsA("Part") then continue end
	local touchingparts = element:GetTouchingParts() 
	for _, part in ipairs(element:GetTouchingParts()) do
		if element.Size == part.Size and element.CFrame == part.CFrame and not element.Locked then 
			part:Destroy() 
			deleteCount += 1
		end 
	end
end

warn(`deleted {deleteCount} objects`)

Thanks for spotting that. I haven’t used or updated this script in a while.