Charm uses a fine-grained reactivity model to manage game state. The core building blocks are:
- Signals: Reactive state containers that hold values. You interact with them via a getter and a setter.
- Effects: Code blocks that re-run automatically whenever the signals they access change.
- Computed Signals: Memoized functions that derive new values from other signals. They only re-calculate when their dependencies change.
Example: Reactive Todo List
This example demonstrates creating a reactive UI where a list of todos is observed, and each todo item is represented by a TextLabel that updates its text and visibility based on a search query.
local getTodos, setTodos = signal({} :: { string })
local getQuery, setQuery = signal("")
-- observe reacts to changes in the getTodos signal
observe(getTodos, function(todo: string, index: number)
local instance = Instance.new("TextLabel")
-- getText is a computed signal that only re-runs if the specific todo at this index changes
local getText = computed(function()
return getTodos()[index] or ""
end)
-- effect re-runs whenever getText() or getQuery() changes
effect(function()
instance.Text = getText()
instance.Visible = string.match(getText(), getQuery()) ~= nil
end)
instance.LayoutOrder = index
instance.Size = UDim2.new(1, 0, 0, 40)
instance.Parent = screenGui
-- The return function acts as a cleanup callback when the item is removed from the list
return function()
instance:Destroy()
end
end)
setTodos({ "Buy milk", "Buy eggs", "Play Roblox" })
setQuery("Buy")
local getTodos, setTodos = signal({} :: { string })
local getQuery, setQuery = signal("")
observe(getTodos, function(todo: string, index: number)
local instance = Instance.new("TextLabel")
local getText = computed(function()
return getTodos()[index] or ""
end)
effect(function()
instance.Text = getText()
instance.Visible = string.match(getText(), getQuery()) ~= nil
end)
instance.LayoutOrder = index
instance.Size = UDim2.new(1, 0, 0, 40)
instance.Parent = screenGui
return function()
instance:Destroy()
end
end)
setTodos({ "Buy milk", "Buy eggs", "Play Roblox" })
setQuery("Buy")