Skip to main content

instanceTrackerUtil

A simple utility module for working with instances and their properties.

NOTE

This utility works in conjuction with the Property class.

-- Example type writer effect
local name = Property.new("newText")

instanceTrackerUtil.setProps(textLabel, {
	Name = instanceTrackerUtil.bind(name, function(name) 
		local goalLength = string.len(name)
		local timeToTake = goalLength / 30
		local accumulated = 0

		return function(updatedName)
			if timeToTake < accumulated then return nil end

			accumulated += RunService.Heartbeat:Wait()
			return string.sub(name, 1, math.floor((accumulated / timeToTake) * goalLength))
		end
	end)
})

Types

TransformerBinder

interface TransformerBinder {
propertyProperty,
transformer(
newValueany,
oldValueany?
) → any,
__propertyTransformertrue,
}

Functions

track

instanceTrackerUtil.track(
instanceInstance,
cleanupCallback((instanceInstance) → ())?
) → ()

Starts tracking the given instance so that the utility can easily cleanup once this instance is untracked. The cleanupCallback is called instanceTrackerUtil.untrack is called on the instance.

instanceTrackerUtil.track(screenGui.Frame, function(frame)
	frame:TweenPosition(UDim2.fromScale(1, 0))
end)

bind

instanceTrackerUtil.bind(
propertyProperty.Property,
transformer(
newValueany,
oldValueany?
) → any
) → TransformerBinder

Returns a transformer binder, to be used by instanceTrackerUtil.setProps, the value of property is wrapped onto transformer.

bindToInstanceProperty

instanceTrackerUtil.bindToInstanceProperty(
instanceInstance,
instancePropertystring,
transformer(
newValueany,
oldValueany?
) → any
) → TransformerBinder

Returns a transformer binder, to be used by instanceTrackerUtil.setProps, the value of property of instance is wrapped onto transformer.

local name = Property.new("newText")

instanceTrackerUtil.setProps(textLabel, {
	Text = instanceTrackerUtil.bindToInstanceProperty(Workspace.Baseplate, "Transparency", function(newTransparency, oldTransparency) 
		return tostring(newTransparency)
	end)
})

Workspace.Baseplate.Transparency = 1

task.defer(function()
	print(textLabel.Text) --> "1"
end)

bindToInstanceAttribute

instanceTrackerUtil.bindToInstanceAttribute(
instanceInstance,
attributestring,
transformer(
newValueany,
oldValueany?
) → any
) → TransformerBinder

Returns a transformer binder, to be used by instanceTrackerUtil.setProps, the attribute (attribute)ofinstanceis wrapped ontotransformer`.

local name = Property.new("newText")

instanceTrackerUtil.setProps(textLabel, {
	Text = instanceTrackerUtil.bindToInstanceAttribute(Workspace.Baseplate, "SomeAttribute", function(newAttributeValue, oldAttributeValue) 
		return  newAttributeValue
	end)
})

Workspace.Baseplate:SetAttribute("SomeAttribute", "TestText")
task.defer(function()
	print(textLabel.Text) --> "TestText"
end)

setProps

instanceTrackerUtil.setProps(
instanceInstance,
props{[string]any}
) → Connection

Initializes the given instance with props. If properties are specified in props, the instance property will be bind to the *value of the property.

Returns a cleanup function which when called, will destroy all properties bound to the instance.

local textProp = Property.new("someText")
local nameProp = Property.new("test")

instanceTrackerUtil.setProps(textLabel, {
	Text = textProp,

	TextStrokeTransparency = 1,

	-- Bind the "Name" property of the textlabel to a callback
	-- which will be called every time the "name" property's value
	-- changes (and is called initially), you can return a new value
	-- from the callback using the given property value, which'll be
	-- applied to the instance's property itself.
	Name = instanceTrackerUtil.bind(nameProp, function(name) 
		return name .. "_Cool"
	end)
})

print(textLabel.Name) --> "test_Cool"
nameProp:set("nope")
print(textLabel.Name) --> "nope_Cool"
text:set("eyes")
print(textLabel.Text) --> "eyes"

You can also treat the bound function as a higher order function and return another function from it which will be called indefinitely until it returns an explicit nil value or the prop reupdates. The function is just passed the latest value of the property and the old value of the property, and it's return value is used as the new value of the property.

NOTE

The property is updated to the non-nil return value of the function, however it is bulk updated so no on update signals are fired (which effectively prevents the higher order function from being unnecessarily recalled).

  • You can use this to implement cool animations, for e.g a type writer effect:
-- Example type writer effect
local nameProp = Property.new("newText")

instanceTrackerUtil.setProps(textLabel, {
	Name = instanceTrackerUtil.bind(nameProp, function(name) 
		local goalLength = string.len(name)
		local timeToTake = goalLength / 30
		local accumulated = 0

		return function(updatedName)
			-- We are done with the time writer effect, let's return a nil value to stop
			-- this function from being called.
			if timeToTake < accumulated then return nil end

			accumulated += RunService.Heartbeat:Wait()
			return string.sub(name, 1, math.floor((accumulated / timeToTake) * goalLength))
		end
	end)
})	
  • Tweening:
local GOAL_TRANSPARENCY = 1

local transparencyProp = Property.new(0)

local function lerp(n, g, a)
	return n + (g - n) * a
end

local function close(n, g)
	return (n - g) < 0.001
end

instanceTrackerUtil.setProps(Workspace.Baseplate, {
	Transparency = instanceTrackerUtil.bind(transparencyProp, function() 
		return function(updatedTransparency)
			if close(updatedTransparency, GOAL_TRANSPARENCY) then return nil end

			return lerp(updatedTransparency, GOAL_TRANSPARENCY, RunService.Heartbeat:Wait() * 2)
		end
	end)
})	

untrack

instanceTrackerUtil.untrack(instanceInstance) → ()

Cleans up the given instance and untracks it, if it was previously being tracked. If a cleanup callback for the given instance exists, it will be called. Additionally, all property objects used for the properties of the descendants of the given instance will have their connections cleaned up.

local myPart = workspace.MyPart

local brickColor = Property.new(BrickColor.Red())
local transparency = Property.new(0)

-- Bind the "BrickColor" property of myPart to the above property:
instanceTrackerUtil.setProps(myPart, {BrickColor = brickColor})

-- Bind the "Transparency" property of myPart.SomeOtherPart to the above property:
instanceTrackerUtil.setProps(myPart.SomeOtherPart, {Transparency = transparency})

-- Track myPart: 
instanceTrackerUtil.track(myPart, function()
	-- the transparency and brickColor property will soon have their connections cleaned up, so allow
	-- them to be set to their default values (myPart's brick color will now turn white
	-- and myPart.SomeOtherPart's transparency will now be set to 0)
	brickColor:set(BrickColor.White())
	transparency:set(0)
end)

task.wait(5)

instanceTrackerUtil.untrack(myPart)
Show raw api
{
    "functions": [
        {
            "name": "track",
            "desc": " \nStarts tracking the given instance so that the utility can easily cleanup once this instance is untracked. The \n`cleanupCallback` is called `instanceTrackerUtil.untrack` is called on the instance.\n\n```lua\ninstanceTrackerUtil.track(screenGui.Frame, function(frame)\n\tframe:TweenPosition(UDim2.fromScale(1, 0))\nend)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                },
                {
                    "name": "cleanupCallback",
                    "desc": "",
                    "lua_type": "((instance: Instance) -> ())?\n"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 100,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        },
        {
            "name": "bind",
            "desc": " \nReturns a transformer binder, to be used by `instanceTrackerUtil.setProps`,\nthe value of `property` is wrapped onto `transformer`.",
            "params": [
                {
                    "name": "property",
                    "desc": "",
                    "lua_type": "Property.Property"
                },
                {
                    "name": "transformer",
                    "desc": "",
                    "lua_type": "(newValue: any, oldValue: any?) -> any\n"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TransformerBinder"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 118,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        },
        {
            "name": "bindToInstanceProperty",
            "desc": " \n\nReturns a transformer binder, to be used by `instanceTrackerUtil.setProps`,\nthe value of `property` of `instance` is wrapped onto `transformer`.\n\n```lua\nlocal name = Property.new(\"newText\")\n\ninstanceTrackerUtil.setProps(textLabel, {\n\tText = instanceTrackerUtil.bindToInstanceProperty(Workspace.Baseplate, \"Transparency\", function(newTransparency, oldTransparency) \n\t\treturn tostring(newTransparency)\n\tend)\n})\n\nWorkspace.Baseplate.Transparency = 1\n\ntask.defer(function()\n\tprint(textLabel.Text) --> \"1\"\nend)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                },
                {
                    "name": "instanceProperty",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "transformer",
                    "desc": "",
                    "lua_type": "(newValue: any, oldValue: any?) -> any\n"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TransformerBinder"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 152,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        },
        {
            "name": "bindToInstanceAttribute",
            "desc": " \n\nReturns a transformer binder, to be used by `instanceTrackerUtil.setProps`, \nthe attribute (`attribute`)` of `instance` is wrapped onto `transformer`.\n\n```lua\nlocal name = Property.new(\"newText\")\n\ninstanceTrackerUtil.setProps(textLabel, {\n\tText = instanceTrackerUtil.bindToInstanceAttribute(Workspace.Baseplate, \"SomeAttribute\", function(newAttributeValue, oldAttributeValue) \n\t\treturn  newAttributeValue\n\tend)\n})\n\nWorkspace.Baseplate:SetAttribute(\"SomeAttribute\", \"TestText\")\ntask.defer(function()\n\tprint(textLabel.Text) --> \"TestText\"\nend)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                },
                {
                    "name": "attribute",
                    "desc": "",
                    "lua_type": "string"
                },
                {
                    "name": "transformer",
                    "desc": "",
                    "lua_type": "(newValue: any, oldValue: any?) -> any\n"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TransformerBinder"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 189,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        },
        {
            "name": "setProps",
            "desc": " \n\nInitializes the given instance with `props`. If properties are specified in `props`, the\ninstance property will be bind to the **value of the property*.\n\nReturns a cleanup function which when called, will destroy all properties bound to the instance.\n\n```lua\nlocal textProp = Property.new(\"someText\")\nlocal nameProp = Property.new(\"test\")\n\ninstanceTrackerUtil.setProps(textLabel, {\n\tText = textProp,\n\n\tTextStrokeTransparency = 1,\n\n\t-- Bind the \"Name\" property of the textlabel to a callback\n\t-- which will be called every time the \"name\" property's value\n\t-- changes (and is called initially), you can return a new value\n\t-- from the callback using the given property value, which'll be\n\t-- applied to the instance's property itself.\n\tName = instanceTrackerUtil.bind(nameProp, function(name) \n\t\treturn name .. \"_Cool\"\n\tend)\n})\n\nprint(textLabel.Name) --> \"test_Cool\"\nnameProp:set(\"nope\")\nprint(textLabel.Name) --> \"nope_Cool\"\ntext:set(\"eyes\")\nprint(textLabel.Text) --> \"eyes\"\n```\n\nYou can also treat the bound function as a higher order function and return another function from it which will be called\n*indefinitely* **until it returns an explicit nil value** or the prop reupdates. The function is just passed the latest value\nof the property and the old value of the property, and it's return value is used as the new value of the property.\n\n:::note\nThe property is updated to the **non-nil** return value of the function, however it is bulk updated so no on update signals\nare fired (which effectively prevents the higher order function from being unnecessarily recalled).\n:::\n\n- You can use this to implement cool animations, for e.g a type writer effect:\n\n```lua\n-- Example type writer effect\nlocal nameProp = Property.new(\"newText\")\n\ninstanceTrackerUtil.setProps(textLabel, {\n\tName = instanceTrackerUtil.bind(nameProp, function(name) \n\t\tlocal goalLength = string.len(name)\n\t\tlocal timeToTake = goalLength / 30\n\t\tlocal accumulated = 0\n\n\t\treturn function(updatedName)\n\t\t\t-- We are done with the time writer effect, let's return a nil value to stop\n\t\t\t-- this function from being called.\n\t\t\tif timeToTake < accumulated then return nil end\n\n\t\t\taccumulated += RunService.Heartbeat:Wait()\n\t\t\treturn string.sub(name, 1, math.floor((accumulated / timeToTake) * goalLength))\n\t\tend\n\tend)\n})\t\n```\n\n- Tweening:\n\n```lua\nlocal GOAL_TRANSPARENCY = 1\n\nlocal transparencyProp = Property.new(0)\n\nlocal function lerp(n, g, a)\n\treturn n + (g - n) * a\nend\n\nlocal function close(n, g)\n\treturn (n - g) < 0.001\nend\n\ninstanceTrackerUtil.setProps(Workspace.Baseplate, {\n\tTransparency = instanceTrackerUtil.bind(transparencyProp, function() \n\t\treturn function(updatedTransparency)\n\t\t\tif close(updatedTransparency, GOAL_TRANSPARENCY) then return nil end\n\n\t\t\treturn lerp(updatedTransparency, GOAL_TRANSPARENCY, RunService.Heartbeat:Wait() * 2)\n\t\tend\n\tend)\n})\t\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                },
                {
                    "name": "props",
                    "desc": "",
                    "lua_type": "{ [string]: any }\n"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Connection"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 297,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        },
        {
            "name": "untrack",
            "desc": " \nCleans up the given instance and untracks it, if it was previously being tracked.\nIf a cleanup callback for the given instance exists, it will be called. Additionally, \nall property objects used for the properties of the descendants of the given instance\nwill have their connections cleaned up.\n\n```lua\nlocal myPart = workspace.MyPart\n\nlocal brickColor = Property.new(BrickColor.Red())\nlocal transparency = Property.new(0)\n\n-- Bind the \"BrickColor\" property of myPart to the above property:\ninstanceTrackerUtil.setProps(myPart, {BrickColor = brickColor})\n\n-- Bind the \"Transparency\" property of myPart.SomeOtherPart to the above property:\ninstanceTrackerUtil.setProps(myPart.SomeOtherPart, {Transparency = transparency})\n\n-- Track myPart: \ninstanceTrackerUtil.track(myPart, function()\n\t-- the transparency and brickColor property will soon have their connections cleaned up, so allow\n\t-- them to be set to their default values (myPart's brick color will now turn white\n\t-- and myPart.SomeOtherPart's transparency will now be set to 0)\n\tbrickColor:set(BrickColor.White())\n\ttransparency:set(0)\nend)\n\ntask.wait(5)\n\ninstanceTrackerUtil.untrack(myPart)\n```",
            "params": [
                {
                    "name": "instance",
                    "desc": "",
                    "lua_type": "Instance"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 427,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "TransformerBinder",
            "desc": " ",
            "fields": [
                {
                    "name": "property",
                    "lua_type": "Property,",
                    "desc": ""
                },
                {
                    "name": "transformer",
                    "lua_type": "(newValue: any, oldValue: any?) -> any,",
                    "desc": ""
                },
                {
                    "name": "__propertyTransformer",
                    "lua_type": "true,",
                    "desc": ""
                }
            ],
            "source": {
                "line": 39,
                "path": "src/instanceTrackerUtil/init.luau"
            }
        }
    ],
    "name": "instanceTrackerUtil",
    "desc": " \n\nA simple utility module for working with instances and their properties.\n\n:::note\nThis utility works in conjuction with the [Property] class.\n:::\n\n```lua\n-- Example type writer effect\nlocal name = Property.new(\"newText\")\n\ninstanceTrackerUtil.setProps(textLabel, {\n\tName = instanceTrackerUtil.bind(name, function(name) \n\t\tlocal goalLength = string.len(name)\n\t\tlocal timeToTake = goalLength / 30\n\t\tlocal accumulated = 0\n\n\t\treturn function(updatedName)\n\t\t\tif timeToTake < accumulated then return nil end\n\n\t\t\taccumulated += RunService.Heartbeat:Wait()\n\t\t\treturn string.sub(name, 1, math.floor((accumulated / timeToTake) * goalLength))\n\t\tend\n\tend)\n})\n```",
    "source": {
        "line": 30,
        "path": "src/instanceTrackerUtil/init.luau"
    }
}