Loop
The Loop class handles scheduling and looping (who would have guessed) over all of your game systems.
Yielding
Yielding is not allowed in systems. Doing so will result in the system thread being closed early, but it will not affect other systems.
Types
System
Either a plain function or a table defining the system.
SystemTable
interface
SystemTable {
system:
(
...any
)
→
(
)
--
The system function
event?:
string
--
The event the system runs on. A string, a key from the table you pass to Loop:begin
.
priority?:
number
--
Priority influences the position in the frame the system is scheduled to run at.
}
A table defining a system with possible options.
Systems are scheduled in order of priority
, meaning lower priority
runs first.
The default priority is 0
.
Functions
new
Creates a new loop. Loop.new
accepts as arguments the values that will be passed to all of your systems.
So typically, you want to pass the World in here, as well as maybe a table of global game state.
local world = World.new()
local gameState = {}
local loop = Loop.new(world, gameState)
scheduleSystems
Schedules a set of systems based on the constraints they define.
Systems may optionally declare:
- The name of the event they run on (e.g., RenderStepped, Stepped, Heartbeat)
- A numerical priority value
- Other systems that they must run after
If systems do not specify an event, they will run on the default
event.
Systems that share an event will run in order of their priority, which means that systems with a lower priority
value run first. The default priority is 0
.
Systems that have defined what systems they run after
can only be scheduled after all systems they depend on have
already been scheduled.
All else being equal, the order in which systems run is stable, meaning if you don't change your code, your systems will always run in the same order across machines.
INFO
It is possible for your systems to be in an unresolvable state. In which case, scheduleSystems
will error.
This can happen when your systems have circular or unresolvable dependency chains.
If a system has both a priority
and defines systems it runs after
, the system can only be scheduled if all of
the systems it depends on have a lower or equal priority.
Systems can never depend on systems that run on other events, because it is not guaranteed or required that events will fire every frame or will always fire in the same order.
CAUTION
scheduleSystems
has to perform nontrivial sorting work each time it's called, so you should avoid calling it multiple
times if possible.
scheduleSystem
Schedules a single system. This is an expensive function to call multiple times. Instead, try batch scheduling systems with Loop:scheduleSystems if possible.
evictSystem
Removes a previously-scheduled system from the Loop. Evicting a system also cleans up any storage from hooks. This is intended to be used for hot reloading. Dynamically loading and unloading systems for gameplay logic is not recommended.
replaceSystem
Replaces an older version of a system with a newer version of the system. Internal system storage (which is used by hooks) will be moved to be associated with the new system. This is intended to be used for hot reloading.
setWorlds
Loop:
setWorlds
(
) →
(
)
Sets the Worlds to be used by the Loop for deferring commands, and the Debugger for profiling.
begin
Connects to frame events and starts invoking your systems.
Pass a table of events you want to be able to run systems on, a map of name to event. Systems can use these names
to define what event they run on. By default, systems run on an event named "default"
. Custom events may be used
if they have a Connect
function.
loop:begin({
default = RunService.Heartbeat,
Heartbeat = RunService.Heartbeat,
RenderStepped = RunService.RenderStepped,
Stepped = RunService.Stepped,
})
Returns a table similar to the one you passed in, but the values are RBXScriptConnection
values (or whatever is
returned by :Connect
if you passed in a synthetic event).
addMiddleware
Loop:
addMiddleware
(
middleware:
(
nextFn:
(
)
→
(
)
,
eventName:
string
)
→
(
)
→
(
)
) →
(
)
Adds a user-defined middleware function that is called during each frame.
This allows you to run code before and after each frame, to perform initialization and cleanup work.
loop:addMiddleware(function(nextFn)
return function()
Plasma.start(plasmaNode, nextFn)
end
end)
You must pass addMiddleware
a function that itself returns a function that invokes nextFn
at some point.
The outer function is invoked only once. The inner function is invoked during each frame event.
INFO
Middleware added later "wraps" middleware that was added earlier. The innermost middleware function is the internal function that actually calls your systems.