In the search bar of the Explorer window, you can use a variety of filters to search for the objects that match certain conditions. You can combine these together to make the search more specific.
However, I don’t think the search bar filters will be an all-encompassing solution for your question so I’ll also include some code that can be run in the command bar that should be able to find specific value matches without having to specify what value it needs to match.
Search Bar Filters
For example, if you wanted to only look for MaterialVariants
that are named “CustomRoadTexture”, you could use the following search:
c:MaterialVariant and Name == "CustomRoadTexture"
c
is a bespoke filter that is checking the Class
.
Name
is a property filter that is checking the Name
property.
Or, if you wanted to look for all MaterialVariants
with a BaseMaterial
of Sand
that has its ColorMap
set to a specific texture ID, you could use the following search (replacing “ID” with the number / asset ID you want to search for):
c:MaterialVariant and BaseMaterial == "Sand" and ColorMap == "rbxassetid://ID"
This sort of idea applies for any combination of properties that you want to search for. It can have as few or as many properties as you want to search for (so you could just look for MaterialVariants
, or all MaterialVariants
with a specific BaseMaterial
, or all MaterialVariants
that have specific Color / Metalness / Roughness maps, etc.)
(Alternative solution) Command bar code
Unfortunately, I don’t know if there’s a way to specifically filter for objects that share the exact same value for one of its properties without also specifying what value to look for.
As a result, I’ve written the following code which will look through the MaterialService
and group items that are found to have a match for specific properties.
By default, it’ll clone the matching items and place them into designated folders so it’ll be able to show if an object has multiple different matches, and to make sure it doesn’t ruin any organizational system that was already there. This way, you can see what matches it finds and then use a property filter on those specific matching values in the Explorer search bar to find the original items that matched.
- This example doesn’t find matches for the “BaseMaterial”, “MaterialPattern”, and “StudsPerTile” properties by default, since those are less likely to indicate that two objects are “duplicates” (especially since
MaterialPattern
only has 2 different values at the moment). However, you could always choose to search for any of these properties by updating the relevant variables in the code from false
to true
.
(Note that this might not be the most efficient code; it’s just an example of a possible way to go about this)
Example Code
(I tested this in the Racing Starter Place and it worked really well since there are several MaterialVariants
included there by default)
Edit: I forgot to link to the page where you can access the starter place; it has been added
Edit 2: For anyone interested, and in case that Roblox Creator Hub page changes, here’s the announcement thread for that Racing Template Starter Place.
--[[ I'd recommend pasting this into a script, first
so that you can edit any of the "Settings" before
pasting it into the command bar and running the code
--]]
local MaterialService = game:GetService("MaterialService")
---
local Settings = {
LookThroughEntireGame = false, -- If false, only looks through MaterialService
LookForPropertyMatches = {
-- To choose a property where it should look for matches, update the variable to "true"
BaseMaterial = false,
ColorMap = true,
MetalnessMap = true,
NormalMap = true,
RoughnessMap = true,
MaterialPattern = false,
StudsPerTile = false,
Name = true,
Parent = false
},
ExcludeEmptyProperties = true, -- If true, properties without an assigned value will not be matched
ContainerForDuplicateMatches = game:GetService("ServerStorage"),
ClearAllClonedItemsFromPreviousSearchForMatchingItemsCheck = true
--[[ This helps make sure that the folder of matching items
does not become cluttered with matching items found from a previous test.
If you plan on running this code multiple times in a row,
setting this to true and then running the code in the command bar
will delete the "MaterialVariants: Duplicate Matching Folder"
folder that was created the previous time the code ran, and then it
will create a brand new one, only containing items from the newest test.
If you plan on removing the folder manually,
or if you want it to keep the cloned items from a previous test, keeping this
set to false will not delete anything from previous tests.
]]--
}
local MaterialVariants = {
AllObjects = {},
}
local function initialLoop()
warn("Running initial loop")
local container
if Settings.LookThroughEntireGame == true then
container = game
else
container = MaterialService
end
for _, item in container:GetDescendants() do
if item:IsA("MaterialVariant") then
item:SetAttribute("TemporaryIndexAttribute", #MaterialVariants.AllObjects + 1)
table.insert(MaterialVariants.AllObjects, item)
end
end
print("Total MaterialVariants found in "..container.Name..": "..tostring(#MaterialVariants.AllObjects))
end
initialLoop()
local function searchForMatchingItems()
warn("Now searching for all matching items")
local totalMatches = 0
local propertiesToSearchFor = {}
local container = Settings.ContainerForDuplicateMatches
local duplicateMatchingFolder = container:FindFirstChild("MaterialVariants: Duplicate Matching Folder")
if duplicateMatchingFolder then
if Settings.ClearAllClonedItemsFromPreviousSearchForMatchingItemsCheck == true then
duplicateMatchingFolder:Destroy()
duplicateMatchingFolder = nil
end
end
local function createPropertyMatchFolders()
for propertyToMatch, toLookThrough in Settings.LookForPropertyMatches do
if toLookThrough == false then continue end
table.insert(propertiesToSearchFor, propertyToMatch)
if not duplicateMatchingFolder:FindFirstChild(propertyToMatch) then
local propertyMatchFolder = Instance.new("Folder")
propertyMatchFolder.Name = propertyToMatch
propertyMatchFolder.Parent = duplicateMatchingFolder
end
end
end
if not duplicateMatchingFolder then
print("Duplicate Matching Folder does not exist. Creating a new one")
local newFolder = Instance.new("Folder")
newFolder.Name = "MaterialVariants: Duplicate Matching Folder"
newFolder.Parent = container
duplicateMatchingFolder = newFolder
end
createPropertyMatchFolders()
for index, originalItem in MaterialVariants.AllObjects do
for otherIndex, otherItem in MaterialVariants.AllObjects do
if otherIndex == index then continue end
for _, propertyName in propertiesToSearchFor do
if Settings.ExcludeEmptyProperties == true then
if
originalItem[propertyName] == nil
or originalItem[propertyName] == ""
or originalItem[propertyName] == " "
then
continue
end
end
if originalItem[propertyName] == otherItem[propertyName] then
local propertyMatchFolder = duplicateMatchingFolder:FindFirstChild(propertyName)
local specificValueMatchFolder = propertyMatchFolder:FindFirstChild(originalItem[propertyName])
if not specificValueMatchFolder then
local verySpecificFolder = Instance.new("Folder")
verySpecificFolder.Name = originalItem[propertyName]
verySpecificFolder.Parent = propertyMatchFolder
specificValueMatchFolder = verySpecificFolder
end
local itemsAddedToFolder = 0
local function checkIfItemAlreadyExistsInFolder(item)
local addItemToFolder = true
local currentItemAttribute = item:GetAttribute("TemporaryIndexAttribute")
for _, existingItem in specificValueMatchFolder:GetChildren() do
local existingItemAttribute = existingItem:GetAttribute("TemporaryIndexAttribute")
if currentItemAttribute == existingItemAttribute then
-- In this case, the exact item already exists in the folder
addItemToFolder = false
break
end
end
if addItemToFolder == true then
itemsAddedToFolder += 1
local clonedItem = item:Clone()
clonedItem.Parent = specificValueMatchFolder
end
end
checkIfItemAlreadyExistsInFolder(originalItem)
checkIfItemAlreadyExistsInFolder(otherItem)
if itemsAddedToFolder > 0 then
totalMatches += 1
end
end
end
end
end
for _, item in MaterialVariants.AllObjects do
if item:GetAttribute("TemporaryIndexAttribute") then
item:SetAttribute("TemporaryIndexAttribute", nil)
end
end
warn("Search for all matching items has concluded")
print("Total matches found: "..tostring(totalMatches))
end
searchForMatchingItems()
If you want a somewhat brief rundown of how it organizes the matching items, the code will create a main folder called MaterialVariants: Duplicate Matching Folder
so you know where to find everything. By default, it is placed into the ServerStorage
, but you can choose where it goes in the “Settings” table.
Inside of those folders will be the names of the properties that you want to check for matches of. So if you want to find all MaterialVariants
that could have overlaps with one or more of the following:
- Name
- ColorMap
- MetalnessMap
- NormalMap
- RoughnessMap
Then it will create a separate folder for each of those property name inside of the MaterialVariants: Duplicate Matching Folder
. Then, when the code finds two items with a matching value for a given property, it will create a new folder inside of the property name folder that has the name of the matching value. It’ll clone those items and then place it into that final folder. This way, once everything is done, you can go through and see how many duplicate matches there were of a specific value for each property.
As an example, if you had two material variants with the following properties:
#1
- Name: “EpicMaterialVariant”
- ColorMap: “rbxassetid://1”
- MetalnessMap: (blank)
- NormalMap: “rbxassetid://1337”
- RoughnessMap: “rbxassetid://2000”
#2
- Name: “EpicMaterialVariant”
- ColorMap: “rbxassetid://25”
- MetalnessMap: “rbxassetid://12345”
- NormalMap: “rbxassetid://1337”
- RoughnessMap: (blank)
These two had exact matches for:
So, inside of ServerStorage
→ MaterialVariants: Duplicate Matching Folder
, 2 of the property name folders will be updated.
-
Inside of the folder called “Name”, a new folder will be created that is called “EpicMaterialVariant”, and the two matching items will be cloned and then added there.
-
Inside of the folder called “NormalMap”, a new folder will be created that is called “rbxassetid://1337”, and the two matching items will be cloned and then added there.
And for the sake of the example, let’s say that there happened to be another MaterialVariant
that had the same value for the NormalMap
as the previous two items did. In that case, it would clone this item into the folder but it would not create an additional clone of the items that were already added into the folder.
- This helps make sure that the folder will contain the exact quantity of unique items that all had the exact same value for a specific property. (Note: The code is structured in a way where even if you have two identical items with the exact same values for each property, it will be able to distinguish those as unique items, so both of those items would be added to the folder(s) rather than just one.)
There’s a ton of different things that I’ve explained here all at once, so if you need clarification for anything in particular, let me know and I’ll try to make it easier to understand