https://github.com/YetAnotherClown/planck
An Agnostic Scheduler for ECS
https://github.com/YetAnotherClown/planck
ecs luau scheduler
Last synced: 28 days ago
JSON representation
An Agnostic Scheduler for ECS
- Host: GitHub
- URL: https://github.com/YetAnotherClown/planck
- Owner: YetAnotherClown
- License: mit
- Created: 2024-11-26T07:14:28.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2025-07-10T01:02:54.000Z (3 months ago)
- Last Synced: 2025-09-10T23:39:33.148Z (about 1 month ago)
- Topics: ecs, luau, scheduler
- Language: Luau
- Homepage: https://yetanotherclown.github.io/planck/
- Size: 2.93 MB
- Stars: 30
- Watchers: 1
- Forks: 11
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-roblox - Planck - An Agnostic Scheduler for ECS. (Libraries / Entity Component System)
README
# Planck, an ECS Scheduler

[](https://yetanotherclown.github.io/planck)
[](https://wally.run/package/yetanotherclown/planck)An Agnostic Scheduler, inspired by Bevy Schedules and Flecs Pipelines and Phases.
## Installation
You can install Planck with Wally
```toml
[dependencies]
Planck = "yetanotherclown/planck@0.2.0"
```## What is Planck?
Planck is a standalone scheduler, which allows you to execute code on specific events, with certain conditions, and in a particular order.
This scheduler is library agnostic, which means that it *doesn't matter* which ECS library your using or if you're even using an ECS.
You can use this with [Jecs], [Matter], [ECR], and other Luau ECS Libraries.## Does any of this really matter?
Yes, and no.
Your ECS code should be able to run in any order, without any conditions, and without concern for which event it's running on, as long as *it is* running.The order of execution, and conditions both serve to *optimize* your code. Some systems don't need to run every frame, which is why we have conditions.
And the actual order of execution is to reduce latency between changes and effects in your ECS world.Let's say we have `systemA` and `systemB`. `systemA` modifies data in our world which `systemB` depends on.
If `systemA` runs *after* `systemB`, then `systemB` will have to wait a whole frame for the modifications to be made.
This is called being *off-by-a-frame*, and this is why we care about the order of execution.## What's Next?
You may not completely understand what's written above. That's fine.
For now, you should read the [Official Documentation](https://yetanotherclown.github.io/planck) on how to get started with Planck. These concepts will be explained more in depth as you read.
## Quick Overview
While it's highly suggested you read the documentation, here is a quick overview of Planck's API.
### The Scheduler
This is the core of Planck, this is where you add your Systems and set your Phases, Pipelines, and Run Conditions.
```luau
local Planck = require("@packages/Planck")
local Scheduler = Planck.Schedulerlocal Jecs = require("@packages/Jecs")
local World = Jecs.Worldlocal world = World.new()
local state = {}local scheduler = Scheduler.new(world, state)
```### Systems
Systems are really simple, they are just functions which run on an event or in a loop.
```luau
local function systemA(world, state)
-- ...
endreturn systemA
```And to add it to our Scheduler,
```luau
-- ...local systemA = require("@shared/systems/systemA")
local scheduler = Scheduler.new(world, state)
:addSystem(systemA)
```### Phases
Phases are used to split up your frame into different sections, this allows us to schedule our systems to run at different moments of a given frame.
```luau
local Planck = require("@packages/Planck")
local Scheduler = Planck.Scheduler
local Phase = Planck.Phase-- ...
local systemA = require("@shared/systems/systemA")
local myPhase = Phase.new("myPhase")
local scheduler = Scheduler.new(world, state)
:insert(myPhase)
:addSystem(systemA, myPhase)
```Planck has lots of built-in Phases that should work for most cases,
→ [Built-in Phases](https://yetanotherclown.github.io/planck/docs/getting_started/phases#built-in-phases)### Pipelines
Pipelines are ordered groups of Phases, they make working with larger collections of Phases (which all run on the same event) easier.
```luau
local Phase = Planck.Phase
local Pipeline = Planck.Pipeline
local Scheduler = Planck.Schedulerlocal PreUpdate = Phase.new()
local Update = Phase.new()
local PostUpdate = Phase.new()local UpdatePipeline = Pipeline.new()
:insert(PreUpdate)
:insert(Update)
:insert(PostUpdate)local scheduler = scheduler.new(world)
:insert(UpdatePipeline, RunService, "Heartbeat")
```> [!TIP]
> The `UpdatePipeline` seen here, already exists in Planck! It's a built-in Pipeline that you can use without any setup.
> See all [Built-in Phases](https://yetanotherclown.github.io/planck/docs/getting_started/phases#built-in-phases).### Conditions
When we run all our systems every frame, there are a lot of systems that may not actually need to run. Run Conditions allow us to
run our Systems, Phases and Pipelines only sometimes.```luau
local function condition(world)
if someCondition then
return true
else
return false
end
endlocal scheduler = Scheduler.new(world)
:addRunCondition(systemA, condition)
:addRunCondition(somePhase, condition)
:addRunCondition(somePipeline, condition)
```Conditions can be useful, but you should use them carefully. It's suggested that you read our page on
[Conditions](https://yetanotherclown.github.io/planck/docs/design/conditions) to see some useful examples and learn when you should use them.## Inspiration
Planck's API design is heavily influenced by the [Bevy Engine](https://bevyengine.org/), with Schedules, RunConditions, and more.
Planck also draws inspiration from [Flecs](https://www.flecs.dev/) for Pipelines and Phases.We're combining the simple, and beloved API of Bevy with the concept of Pipelines and Phases.
[Jecs]: https://ukendio.github.io/jecs
[Matter]: https://matter-ecs.github.io/matter
[ECR]: https://centau.github.io/ecr/