Version 2.00 (1 March 2024)
Introduction
Hello, I just released a module that extends the functionality of Roblox’s Instance.new
constructor by replacing the parent
parameter with a properties: {[string]: any}
optional parameter, including an additional children: {Instance}
optional parameter and attributes: {[any]: any}
optional parameter.
This module also has built in functions that you can use to speed up development (usage and examples below).
List of familiar functions:
Instance:FindFirstAncestorWithFilter()
Instance:FindFirstChildWithFilter()
Instance:FindFirstDescendantWithFilter()
Instance:GetChildrenWithFilter()
Instance:GetDescendantsWithFilter()
Instance:WaitForChildWithFilter()
List of custom functions:
Instance:WaitForDescendantWithFilter()
Instance:FolderToDataTable()
Instance:DataTableToFolder()
Instance:DataTableToFolderUsingValueBases()
Instance:DataTableToFolderUsingAttributes()
Setup and Usage
Grab the model below and insert it in your game (I recommend ReplicatedStorage
). The model contains the Instance ModuleScript
that you can then override the global Instance
variable.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Instance = require(ReplicatedStorage.Instance) -- WaitForChild if you are using a LocalScript
Model Link and Examples
You can grab the latest module anytime here:
Here is an example of the full functionality of this module:
-- Require
local Instance = require("PATH_TO_MODULE")
-- Instance.new(className: string, properties: {[string]: any}?, children: {Instance}?, attributes: {[string]: any}?): Instance
-- Note: It is OK to pass a Parent key. Internally, parenting will be done after all other properties and children has been set
-- Additionally, any childrens passed in this function will automatically parent to the created Instance
-- Read: https://create.roblox.com/docs/reference/engine/classes/Instance#Parent -> Object Replication
local part: Part = Instance.new("Part", {
Name = "MyPart",
Anchored = true,
Transparency = 0.2,
BrickColor = BrickColor.random(),
Parent = workspace
}, {
Instance.new("StringValue"),
Instance.new("BoolValue"),
Instance.new("IntValue", {
Name = "MyIntValue",
Value = 200
})
}, {
SomeAttribute = "Value",
AnotherAttribute = true
})
-- Note: All filter functions passed will check for truthy/falsy values
-- Instance:FindFirstAncestorWithFilter(instance: Instance, filterFunction: (ancestor: Instance) -> any): Instance?
local someDeepAccessoryPart = nil -- path to some deep accessory part
local playerCharacterModel: Model = Instance:FindFirstAncestorWithFilter(someDeepAccessoryPart, function(ancestor)
return ancestor.ClassName == "Model" and game:GetService("Players"):GetPlayerFromCharacter(ancestor) ~= nil
end)
-- Instance:FindFirstChildWithFilter(instance: Instance, filterFunction: (child: Instance) -> any): Instance?
local playerToolWithHandle: Tool = Instance:FindFirstChildWithFilter(playerCharacterModel, function(child)
return child.ClassName == "Tool" and child:FindFirstChild("Handle") ~= nil
end)
-- Instance:FindFirstDescendantWithFilter(instance: Instance, filterFunction: (descendant: Instance) -> any): Instance?
local toolMainTrail: Trail = Instance:FindFirstDescendantWithFilter(playerToolWithHandle, function(descendant)
return descendant.ClassName == "Trail" and descendant.Name == "MainTrail"
end)
-- Instance:GetChildrenWithFilter(instance: Instance, filterFunction: (child: Instance) -> any): {Instance?}
local playerCharactersThatAreAlive: {Model} = Instance:GetChildrenWithFilter(workspace, function(child)
local player = game:GetService("Players"):GetPlayerFromCharacter(child)
local humanoid = if player ~= nil then Instance:FindFirstChildWithFilter(child, function(childFromCharacter: Humanoid) -- For autofill purposes
return childFromCharacter.ClassName == "Humanoid" and childFromCharacter:GetState() ~= Enum.HumanoidStateType.Dead
end) else nil
return humanoid ~= nil
end)
-- Instance:GetDescendantsWithFilter(instance: Instance, filterFunction: (descendant: Instance) -> any): {Instance?}
local function cleanupSounds()
local soundsWithNoSoundId: {Sound} = Instance:GetDescendantsWithFilter(workspace, function(descendant: Sound) -- For autofill purposes
return descendant.ClassName == "Sound" and descendant.SoundId == ""
end)
for _, sound in ipairs(soundsWithNoSoundId) do
sound:Destroy()
end
end
-- Instance:WaitForChildWithFilter(instance: Instance, filterFunction: (child: Instance) -> any, timeOut: number?): Instance?
-- Note: Replicates the original WaitForChild by using threads and connections with replicated timeOut behavior
-- Will yield indefinitely until conditions have been met and something gets returned
local attackRemoteEvent: RemoteEvent = Instance:WaitForChildWithFilter(game:GetService("ReplicatedStorage"), function(child)
return child.ClassName == "RemoteEvent" and child.Name == "Attack"
end)
-- Will yield until conditions have been met and something gets returned or 5 seconds have passed and will return nil
local attackSound: Sound = Instance:WaitForChildWithFilter(game:GetService("ReplicatedStorage"), function(child)
return child.ClassName == "Sound" and child.Name == "Attack"
end, 5)
-- Instance:WaitForDescendantWithFilter(instance: Instance, filterFunction: (descendant: Instance) -> any, timeOut: number?): Instance?
-- Note: Same functionality as Instance:WaitForChildWithFilter()
local superSecretPart: Part = Instance:WaitForDescendantWithFilter(workspace, function(descendant)
return descendant:IsA("BasePart") == true and descendant.Name == "SuperSecretPart"
end)
-- Instance:FolderToDataTable(instance: Folder | Instance): DataTable ({[any]: any})
-- Converts a folder (or instance) and it's descendants into a data table (DataTable)
-- Note: Compatible with both ValueBases and Attributes
-- Example mock data folder
local function newMockDataFolder(): Folder
return Instance.new("Folder", {
Name = "PlayerData",
Parent = script.Parent
}, {
Instance.new("IntValue", {
Name = "Level",
Value = 1
}),
Instance.new("IntValue", {
Name = "Experience",
Value = 0
}),
Instance.new("Folder", {
Name = "InventoryArrayUsingBaseValues"
}, {
Instance.new("StringValue", {
Name = "1",
Value = "Apple",
}),
Instance.new("StringValue", {
Name = "2",
Value = "Banana",
})
}),
Instance.new("Folder", {
Name = "InventoryArrayUsingAttributes"
}, {
Instance.new("Folder", {
Name = "Apple",
}, nil, {
Count = 5,
Description = "Yummy!"
}),
Instance.new("Folder", {
Name = "Banana",
}, nil, {
Count = 1,
Description = "Even more yummy!"
})
})
}, {
Locked = true,
DataStoreLoaded = false
})
end
local dataFolder: Folder = newMockDataFolder()
local dataTable: {[any]: any} = Instance:FolderToDataTable(dataFolder)
--[=[
local dataTable = {
["DataStoreLoaded"] = false,
["Experience"] = 0,
["InventoryArrayUsingAttributes"] = {
["Apple"] = {
["Count"] = 5,
["Description"] = "Yummy!"
},
["Banana"] = {
["Count"] = 1,
["Description"] = "Even more yummy!"
}
},
["InventoryArrayUsingBaseValues"] = {
[1] = "Apple",
[2] = "Banana"
},
["Level"] = 1,
["Locked"] = true
}
]=]
-- instance:DataTableToFolder(tableToConvert: DataTable, numericalKeyShouldCreateValueBase: boolean?, iteratorFunction: IteratorFunction?): Folder
-- Instance:DataTableToFolderUsingValueBases(tableToConvert: DataTable, iteratorFunction: IteratorFunction?): Folder
-- Instance:DataTableToFolderUsingAttributes(tableToConvert: DataTable, numericalKeyShouldCreateValueBase: boolean?, iteratorFunction: IteratorFunction?): Folder
-- Note 1: By default, iteratorFunction will use pairs. If you want to use ipairs,
-- simply pass the "ipairs" function as the iteratorFunction
-- Note 2: By default, numericalKeyShouldCreateValueBase = true, meaning if a key
-- is numerical, it will create a ValueBase instead of setting an Attribute (value)
-- Convert using attributes, then ValueBases if the attribute type is not supported
-- By default, any numerical keys will convert to ValueBases instead unless
-- numericalKeyShouldCreateValueBase is set to false
local dataFolder: Folder = Instance:DataTableToFolder({
ThisWillBecomeAFolder = {
ThisWillBecomeAnAttribute = true,
AlsoAnAttribute = "hooray!",
ButThisOneWillTurnIntoARayValue = Ray.new(),
ThisWillAlsoTurnIntoAFolder = {
"However",
"This is an array",
"Meaning by default",
"This will turn into",
"StringValues"
}
},
AndFinallyAnAttributeInTheDataFolder = 100
})
dataFolder.Name = "DataTableToFolder"
dataFolder.Parent = script.Parent
-- Convert using ValueBases
local dataFolder: Folder = Instance:DataTableToFolderUsingValueBases({
ThisWillBecomeAnIntValue = 1,
ThisWillbecomeANumberValue = 1.001,
ThisWillBecomeAFolder = {
etc = "etc"
}
})
dataFolder.Name = "DataTableToFolderUsingValueBases"
dataFolder.Parent = script.Parent
-- Convert using Attributes
-- Note: This will turn into a singular folder with attributes 1-4 instead
-- If numericalKeyShouldCreateValueBase is set to true, it will create
-- ValueBases (name as the key) instead of setting these as attributes
local dataFolder: Folder = Instance:DataTableToFolderUsingAttributes({
"This",
"Is",
"An",
"Array"
}, false, ipairs)
dataFolder.Name = "DataTableToFolderUsingAttributes"
dataFolder.Parent = script.Parent