Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
5

How to detect if a variable changes?

Asked by 8 years ago

Lets say I have a variable

local var = 0

And a function

function updateText(text)
    TextLabel.Text = text
end

Then I change the variable.

var = 35

How do I run the function updateText(var) when the variable changes? The variable will have to work for every value type (including tables).

I have thought of a few methods of doing this but some are pretty hacky. I would prefer to do this just inside the script and not using any roblox provided objects if possible

METHOD 1: Whenever I change the variable fire the function.

Aww that's no fun. I would like to not have to type that out everytime

METHOD 2: Fire a remoteEvent with an argument of what we want to change the variable to, then (assuming this is client side), in the serverside using the OnServerEvent event fire the remoteEvent again, in client side using the OnClientEvent event run the function and change the variable to the argument that we provided

Uh, hacky and we need to wait for Server and Client communications to happen and that will only work if we have a player in the game and it is client side.

METHOD 3: Use a value object and use the Changed event

What about table variables

Any help would be appreciated

0
Perhaps a Value object? funyun 958 — 8y
0
Oh I forgot to write that method in, thanks for reminding me. But I really would like to use only Lua for this YellowoTide 1992 — 8y
0
Edited YellowoTide 1992 — 8y

2 answers

Log in to vote
7
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
8 years ago

There isn't a direct way to watch a variable change.

This is mostly to make Lua understandable -- when you change a variable, only that variable changes.

You can still accomplish this, though. First: there is a reason you cannot do extra work when dealing simply with variable. It makes Lua easier to understand when you play by the normal rules. ROBLOX cheats all over the place with Lua's rules; if you think about it, you can probably come up with lots of different places where it cheats. That doesn't mean you should!

Getters and Setters

An idiomatic solution is getters and setters. Instead of using a variable directly, you use two functions. This lets you react to changes. For instance, we can limit a number to be between 0 and 1:

do 
    local x = 0
    -- local in a do means no one can directly use `x`
    -- EXCEPT by getX and setX
    -- (if you trust yourself to not use `x`, the `do/end` is unnecessary)

    function getX()
        return x
    end

    function setX(newX)
        if newX > 1 then
            x = 1
        elseif newX < 0 then
            x = 0
        else
            x = newX
        end
    end
end

setX(2)
print(getX()) -- 1

setX(0.5)
print(getX()) -- 0.5

Polling

An often sufficient method is to just repeatedly ask. If, for instance, you just need to show a value on the screen, this is probably enough:

while wait() do
    script.Parent.Text = value
end

If you want to do it in the background, you can spawn a new thread to do that.

Metatables

Lua has a feature called metatables. These redefine the way that basic operations work in Lua.

Using the __index and __newindex metamethods, we can take advantage of the getter and setter pattern, but make it look like property of an object (this is more or less how .Changed is implemented in ROBLOX objects)

It looks like this:

local t = {}
local value
function get(obj, property)
    assert(property == "value") -- only allow `t.value` to be gotten
    return value
end

function set(obj, property, newValue)
    assert(property == "value") -- only allow `t.value` to be set
    if newValue > 1 then
        value = 1
    elseif newValue < 0 then
        value = 0
    else
        value = newValue
    end
    -- (Do whatever extra stuff here)
end

setmetatable(t, {__index = get, __newindex = set} )

t.value = 5
print(t.value) -- 1
t.value = 0.5
print(t.value) -- 0.5

Metamethod + Environment Hack

Pure Lua 5.1 will let you do this more directly. Diito's answer tells you how to get this to work in ROBLOX.

First: do not do this. To an even greater extent than the above, it can be incredibly confusing and frustrating.


Global variables (as opposed to local, not those in _G -- although in vanilla Lua this is essentially the same) are actually properties stored in a table. That table is called the environment, and you can give it a metatable.

Applying the previous lets you do getters and setters on plain variables -- horrifying!

setmetatable( getfenv(), {__index = get, __newindex = set} ) -- get and set from before

value = 1
print(value) -- 1

value = -1.5
print(value) -- 0 ???

value = 0.5
print(value) -- 0.5
0
Thank you for answering, I haven't really found a reason to learn metatables but I guess they are useful for something after all! PS: Line 16 of the last code block YellowoTide 1992 — 8y
0
getters and setters is usually the better approach because it's clearer and simpler, though slightly less compact. BlueTaslem 18071 — 8y
Ad
Log in to vote
3
Answered by
Diitto 230 Moderation Voter
8 years ago

Following up BlueTaslem... Metamethod + Environment Hack [in a correct way if pro] :

local Environment = getfenv(1)
local Metatable = {}
Metatable.__index = {}
Metatable.__metatable = "This metatable is locked"

function Metatable:__newindex(self, index, value)
    Metatable.__index[index] = value
    updateText()
end

setfenv(1, setmetatable({}, Metatable))
0
Oh, setfenv is unlocked! I totally forgot about that BlueTaslem 18071 — 8y
0
(Still: don't do this!) BlueTaslem 18071 — 8y

Answer this question