| title | description |
|---|---|
Properties and attributes |
How to use scripts to manipulate object properties and attributes. |
Making experiences interactive often means manipulating object properties and attributes:
-
Properties are part of the object class. For example, the
Class.BasePart.Anchoredproperty controls physics for the part. In a track and field experience, you might want to anchor a discus or javelin the instant it lands so that players have a visual indicator of how far it traveled. -
Attributes are essentially custom properties that you define. For example, the Plant reference project uses attributes to set the purchase price for seeds and the maximum plant size that a pot can hold.
Before you begin retrieving and manipulating objects, you must have an understanding of replication order.
The Roblox Engine doesn't guarantee the order in which objects are replicated from the server to the client, which makes the Class.Instance:WaitForChild() method essential for accessing objects in client scripts, particularly objects in the Class.Workspace. Still, some aspects of the process are predictable:
-
The client loads the contents of
Class.ReplicatedFirst, such as a loading screen, assets, and scripts. -
Class.LocalScript|LocalScripts(andClass.Script|Scriptswith aClass.Script.RunContext|RunContextofEnum.RunContext.Client|Client) inReplicatedFirstrun. These scripts can safely get objects fromReplicatedFirstwithout usingWaitForChild():-- Safe local ReplicatedFirst = game:GetService("ReplicatedFirst") local LoadingScreen = require(ReplicatedFirst.LoadingScreen)
These scripts can't safely get objects from other services, because they might not have loaded yet:
-- Not safe local ReplicatedStorage = game:GetService("ReplicatedStorage") local PickupManager = require(ReplicatedStorage.PickupManager)
You can use
WaitForChild()in these scripts to get objects from other services, but doing so negates the benefits of usingReplicatedFirst. -
The client continues loading the rest of the experience.
-
When it finishes, the
Class.DataModel.Loaded|game.Loadedevent fires andClass.DataModel:IsLoaded()|game:IsLoaded()returns true. -
LocalScriptsinPlayers.Player.PlayerScripts(copied fromStarterPlayerScripts) run, as well as clientScriptsinClass.ReplicatedStorage. These scripts can safely get objects fromReplicatedStoragewithout usingWaitForChild(). -
The player's
Class.Player.Character|Charactermodel spawns in the experience. -
LocalScriptsinWorkspace.Character(copied fromStarterCharacterScripts) run.
If your experience uses instance streaming (Class.Workspace.StreamingEnabled), some or most objects might not have loaded into the workspace, so using WaitForChild() to access workspace objects becomes an even more important safety measure. In particular, see Stream in and Per-model streaming controls for additional information on loading and tuning streaming behavior.
The first step to modifying object properties and attributes is to get a reference to the object. The simplest solution is to make the script a child of the object in the Explorer and use script.Parent to reference the object.
local sign = script.ParentThe more universal solution is to get the object from a service using methods like Class.Instance:FindFirstChild() or Class.Instance:WaitForChild().
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local signsFolder = ReplicatedStorage:WaitForChild("Signs")
local sign = signsFolder:WaitForChild("InteractiveSign")Properties are straightforward to access — just use a . after the object reference — although if you're working with a model, you might need to choose an individual part rather than the model itself.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local chair = ReplicatedStorage:WaitForChild("Chair")
chair.LeftArmRest.Size = Vector3.new(10, 1, 10)Although you can create attributes programmatically, the more common solution is to create them with default values in the Studio user interface. Then you can use scripts to modify their values in response to player actions.
For information on creating attributes in Studio, see Instance attributes.
To modify an attribute's value, call Class.Instance:SetAttribute() with a name and value.
local cabbage = script.Parent
cabbage:SetAttribute("Harvestable", true)If the attribute doesn't already exist, this method creates it.
To get the value of one existing attribute, call Class.Instance:GetAttribute() on the instance.
local cabbage = script.Parent
cabbage:SetAttribute("Harvestable", true)
local isHarvestable = cabbage:GetAttribute("Harvestable")
print(isHarvestable) --> trueSimilarly, you can get all attributes by calling Class.Instance:GetAttributes(). This method returns a dictionary of key-value pairs.
local cabbage = script.Parent
local cabbageAttributes = cabbage:GetAttributes()
print(cabbageAttributes.GrowthRate) --> 2
for k, v in cabbageAttributes do
print(k, v)
endTo delete an attribute, set its value to nil.
local cabbage = script.Parent
cabbage:SetAttribute("GrowthRate", nil)There are several ways to listen for changes to properties and attributes:
- The
Class.Instance.AttributeChangedevent listens for changes to any attribute and passes the name of the changed attribute as a parameter. - The
Class.Instance:GetPropertyChangedSignal()method lets you listen for changes to one property and passes no parameters. - The
Class.Instance:GetAttributeChangedSignal()method lets you listen for changes to one attribute and passes no parameters.
Due to the minimal information that these events and methods pass as parameters, all of them are a good fit for anonymous functions, particularly Class.Instance:GetPropertyChangedSignal() and Class.Instance:GetAttributeChangedSignal(). To learn more about anonymous functions and working with events, see Events.
local cabbage = script.Parent
-- Local functions
local function onAnyPropertyChange(property)
-- Ignore changes to attributes
if property ~= "Attributes" then
print(property) --> Name
print(cabbage[property]) --> Cabbage1
end
end
local function onAnyAttributeChange(attribute)
print(attribute) --> Grow, GrowthRate
print(cabbage:GetAttribute(attribute)) --> false, 3
end
-- Listen for changes and connect to local functions
cabbage.Changed:Connect(onAnyPropertyChange)
cabbage.AttributeChanged:Connect(onAnyAttributeChange)
-- Listen for changes and connect to anonymous functions
cabbage:GetPropertyChangedSignal("Name"):Connect(function()
print(cabbage.Name) --> Cabbage1
end)
cabbage:GetAttributeChangedSignal("GrowthRate"):Connect(function()
print(cabbage:GetAttribute("GrowthRate")) --> 3
end)
-- Fires Changed and GetPropertyChangedSignal()
cabbage.Name = "Cabbage1"
-- Fires Changed and AttributeChanged
cabbage:SetAttribute("Grow", false)
-- Fires Changed, AttributeChanged, and GetAttributeChangedSignal()
cabbage:SetAttribute("GrowthRate", 3)


