Matter
Matter. It's what everything is made out of.
Types
ConnectionObject
type
ConnectionObject =
{
Disconnect:
(
(
)
→
(
)
)
?
,
Destroy:
(
(
)
-
>
(
)
)
?
,
disconnect:
(
(
)
→
(
)
)
?
,
destroy:
(
(
)
→
(
)
)
?
}
|
(
)
→
(
)
A connection object returned by a custom event must be either a table with any of the following methods, or a cleanup function.
CustomEvent
interface
CustomEvent {
}
A custom event must have any of these 3 methods.
Properties
World
Matter.World:
World
Loop
Matter.Loop:
Loop
Debugger
Matter.Debugger:
Debugger
None
Matter.None:
None
A value should be interpreted as nil when merging dictionaries.
Matter.None
is used by Component:patch
.
Functions
useHookState
Matter.
useHookState
(
discriminator?:
any
,
--
A unique value to additionally key by
cleanupCallback:
(
storage:
{
}
)
→
boolean?
--
A function to run when the storage for this hook is cleaned up
) →
{
}
tip
Don't use this function directly in your systems.
This function is used for implementing your own topologically-aware functions. It should not be used in your
systems directly. You should use this function to implement your own utilities, similar to useEvent
and
useThrottle
.
useHookState
does one thing: it returns a table. An empty, pristine table. Here's the cool thing though:
it always returns the same table, based on the script and line where your function (the function calling
useHookState
) was called.
Uniqueness
If your function is called multiple times from the same line, perhaps within a loop, the default behavior of
useHookState
is to uniquely identify these by call count, and will return a unique table for each call.
However, you can override this behavior: you can choose to key by any other value. This means that in addition to script and line number, the storage will also only return the same table if the unique value (otherwise known as the "discriminator") is the same.
Cleaning up
As a second optional parameter, you can pass a function that is automatically invoked when your storage is about
to be cleaned up. This happens when your function (and by extension, useHookState
) ceases to be called again
next frame (keyed by script, line number, and discriminator).
Your cleanup callback is passed the storage table that's about to be cleaned up. You can then perform cleanup work, like disconnecting events.
Or, you could return true
, and abort cleaning up altogether. If you abort cleanup, your storage will stick
around another frame (even if your function wasn't called again). This can be used when you know that the user will
(or might) eventually call your function again, even if they didn't this frame. (For example, caching a value for
a number of seconds).
If cleanup is aborted, your cleanup function will continue to be called every frame, until you don't abort cleanup, or the user actually calls your function again.
Example: useThrottle
This is the entire implementation of the built-in useThrottle
function:
local function cleanup(storage)
return os.clock() < storage.expiry
end
local function useThrottle(seconds, discriminator)
local storage = useHookState(discriminator, cleanup)
if storage.time == nil or os.clock() - storage.time >= seconds then
storage.time = os.clock()
storage.expiry = os.clock() + seconds
return true
end
return false
end
A lot of talk for something so simple, right?
component
Matter.
component
(
name?:
string
,
--
Optional name for debugging purposes
defaultData?:
{
}
--
Default data that will be merged with data passed to the component when created
) →
Component
--
Your new type of component
Creates a new type of component. Call the component as a function to create an instance of that component.
If defaultData
is specified, it will be merged with data passed to the component when the component instance is
created. Note that this is not fallback data: if you later remove a field from a component instance that is
specified in the default data, it won't fall back to the value specified in default data.
-- Component:
local MyComponent = Matter.component("My component")
-- component instance:
local myComponentInstance = MyComponent({
some = "data"
})
log
Matter.
log
(
...:
any
) →
(
)
Topologically-aware function
This function is only usable if called within the context of Loop:begin
.
Logs some text. Readable in the Matter debugger.
useDeltaTime
Matter.
useDeltaTime
(
) →
number
Topologically-aware function
This function is only usable if called within the context of Loop:begin
.
Returns the os.clock()
time delta between the start of this and last frame.
useEvent
Matter.
useEvent
(
instance:
Instance
|
{
[
string
]
:
CustomEvent
}
|
CustomEvent
,
--
The instance or the custom event, or a table that has the event you want to connect to
event:
string
|
RBXScriptSignal
|
CustomEvent
--
The name of, or the actual event that you want to connect to
) →
(
)
→
(
number
,
...any
)
Topologically-aware function
This function is only usable if called within the context of Loop:begin
.
Collects events that fire during the frame and allows iteration over event arguments.
for _, player in ipairs(Players:GetPlayers()) do
for i, character in useEvent(player, "CharacterAdded") do
world:spawn(
Components.Target(),
Components.Model({
model = character,
})
)
end
end
Returns an iterator function that returns an ever-increasing number, starting at 1, followed by any event arguments from the specified event.
Events are returned in the order that they were fired.
caution
useEvent
keys storage uniquely identified by the script and line number useEvent
was called from, and the
first parameter (instance). If the second parameter, event
, is not equal to the event passed in for this unique
storage last frame, the old event is disconnected from and the new one is connected in its place.
Tl;dr: on a given line, you should hard-code a single event to connect to. Do not dynamically change the event with a variable. Dynamically changing the first parameter (instance) is fine.
for _, instance in pairs(someTable) do
for i, arg1, arg2 in useEvent(instance, "Touched") do -- This is ok
end
end
for _, instance in pairs(someTable) do
local event = getEventSomehow()
for i, arg1, arg2 in useEvent(instance, event) do -- PANIC! This is NOT OK
end
end
If useEvent
ceases to be called on the same line with the same instance and event, the event connection is
disconnected from automatically.
You can also pass the actual event object instead of its name as the second parameter:
useEvent(instance, instance.Touched)
useEvent(instance, instance:GetPropertyChangedSignal("Name"))
useEvent
supports custom events as well, so you can pass in an object with a Connect
, connect
or an on
method.
The object returned by any event must either be a cleanup function, or a table with a Disconnect
or a Destroy
method so that useEvent
can later clean the event up when needed. See ConnectionObject for more information.
useThrottle
Matter.
useThrottle
(
seconds:
number
,
--
The number of seconds to throttle for
discriminator?:
any
--
A unique value to additionally key by
) →
boolean
--
returns true every x seconds, otherwise false
Topologically-aware function
This function is only usable if called within the context of Loop:begin
.
Utility for easy time-based throttling.
Accepts a duration, and returns true
if it has been that long since the last time this function returned true
.
Always returns true
the first time.
This function returns unique results keyed by script and line number. Additionally, uniqueness can be keyed by a unique value, which is passed as a second parameter. This is useful when iterating over a query result, as you can throttle doing something to each entity individually.
if useThrottle(1) then -- Keyed by script and line number only
print("only prints every second")
end
for id, enemy in world:query(Enemy) do
if useThrottle(5, id) then -- Keyed by script, line number, and the entity id
print("Recalculate target...")
end
end