Changed/GetPropertyChangedSignal - When and how to use them

Introduction

When connecting to property changes, there’s multiple ways to do so including:

  • Loops (While/Repeat/For)
  • Changed
  • GetPropertyChangedSignal

But what’s the best way and how can I use it to the best standard?
Inparticular, loops are used the majority of the time however this is not the method to go about this.
Loops are inefficient especially when you have many, yielding - therefore requiring to be coroutine wrapped - and even slow (Sometimes the value might’ve changed a couple of frames before the loop gets to that).

On the other hand, Changed/GetPropertyChangedSignal don’t need any coroutine wrapping neither are they slow also you can even :Disconnect() them to free up memory!

ValueBases and updating

ValueBases are used a lot in current day roblox development, they’re extremely helpful for sharing data or just keeping data there for use later on. Sometimes you might want to change a TextLabel or something UI related to show the current value - a lot of the time while loops are used for this and as I’ve said that is not the way to go. Lets say we wanted to show the amount of Coins we had in a TextLabel, a while loop would look like:

local plr = game:GetService("Players").LocalPlayer
local stats = plr:WaitForChild("leaderstats")

while wait() do
    script.Parent.Text = stats.Coins.Value 
end

As I’ve said before, this is really inefficient and you’re even updating it to the same thing it is a lot of the time - if you had many Values and these then it’d take up quite a bit of memory whereas using a Changed signal can reduce memory used:

local plr = game:GetService("Players").LocalPlayer
local stats = plr:WaitForChild("leaderstats")

stats.Coins.Changed:Connect(function(newval)
    script.Parent.Text = newval
end)

Why not GetPropertyChangedSignal(“Value”) though? Using GetPropertyChangedSignal is not a problem, it actually really shows what you’re doing, however Changed is specially adapted for ValueBases as Changed will only fire for when the Value is changed also GetPropertyChangedSignal does have a minor difference in speed compared to Changed - this isn’t too much of a problem though. The api-reference talks about Changed and ValueBases here:

Waiting for a ValueBase’s Value to change

In this case, a repeat loop is usually used - now this is an exception as you do need the yielding and you can choose what to yield until however in a majority of cases these are BoolValues and you’re just waiting for it to change from false to true. Now here you can use a :Wait() on Changed, example:

BoolValue.Changed:Wait()

Simples.

Moving away from ValueBases

Now in other instances, GetPropertyChangedSignal will prove more useful.
Lets say we wanted to change a Frame’s BackgroundColor3 to a BasePart’s color.
Since Changed on everything else except ValueBases has the parameter of what property changed, you’d have to do:

BasePart.Changed:Connect(function(property)
    if property == "Color" then
        Frame.BackgroundColor3 = BasePart.Color
    end
end)

Whereas, with GetPropertyChangedSignal you can easily just do:

BasePart:GetPropertyChangedSignal("Color"):Connect(function()
    Frame.BackgroundColor3 = BasePart.Color
end)

Sometimes you may need the Changed event to connect on many different properties however in this instance GetPropertyChangedSignal is the way to go.

Mistakes with GetPropertyChangedSignal

Occassionally, I’ve seen mistakes with GetPropertyChangedSignal due to it’s different syntax - one of which like:

Instance:GetPropertyChangedSignal("Property") do

end

Everyone makes mistakes, just remember GetPropertyChangedSignal acts like an event therefore you need to Connect/Wait on it.

Disconnecting

Going back to what I said about freeing up memory again, you can do so like this:

local c
c = Instance.Changed:Connect(function()
    print("Hello world!")
    c:Disconnect()
end)

Helpful links

Thanks for reading!

This was my first community tutorial, I hoped you enjoyed.
If you have anything to add, please be sure to say so in the replies!

Also correct me if I’m wrong on anything :wink:

14 Likes

Another usage which I utilise all the time is using getpropertychangedsignal to stop walkspeed exploits on the client side.

Why would you? Character physics are client-authoritative and your method can be bypassed or disabled very easily. WalkSpeed exploits aren’t actually WalkSpeed exploits: the property is merely used to help set a velocity for the character when moving. You can easily make yourself move faster without editing WalkSpeed.

Use the server for this. Fluctuating average velocities and differences from expectations can be flags that the character is exploiting, for example.

Yeah, my bad, that’s not a very secure method to do it, I was under the impression it was fine due to me not being able to change the walkspeed but as you pointed out you can always just modify velocities. Thanks for the info, I’ll be sure to use that instead.

A good tutorial. It’s important to cover the notion that systems should be heavily event-based and rely less on loops to get by. Loops should scarcely be used and that’s only in the case that you legitimately need them to perform a task, such as iterating over a table or having something run while a condition is valid.

There are a few comments I’d like to leave about some information raised in this thread.


The cause of this is that GetPropertyChangedSignal is a callback function that creates and returns a granular version of Changed that fires for a specific property. Changed, on the other hand, is already established for the instance as a member. You don’t create anything, you just connect to an existing signal.


Extra point: the wait method of a signal is a callback. When the signal is fired internally, the arguments passed to it will be returned to the Wait and the thread will resume. There are cases where you need a specific value so it’d be important to account for them.

local newValue = ValueBase.Changed:Wait()

This might especially be important if you want to set up a while loop that continually waits for the signal to fire with a specific value and you need the thread yielding rather than running pseudoasynchronously (or as a coroutine). Just remember that the values are returned as a tuple so you may need to either marshall them (catch them in a table) or establish variables for each return value you need.


It doesn’t. GetPropertyChangedSignal is a callback that returns a signal. When you run connect after GetPropertyChangedSignal, you’re connecting to what it returns. Notice GetPropertyChangedSignal. Getter functions, by nature, are callbacks intended to return something.


If you’re interested in clean syntax, you can push this content into a do block. :slight_smile:

-- Make sure to use clear variable names in actual work!
local c do
    c = Instance.Changed:Connect(function (p)
        print(("Change on \"%s\""):format(p))
        c:Disconnect()
    end)
end

That’s all! Cheers.

1 Like