Add some examples

This commit is contained in:
Ukendio 2025-07-26 02:37:51 +02:00
parent 3777585677
commit 1c2dee57d3
5 changed files with 348 additions and 5 deletions

View file

@ -123,6 +123,17 @@ function jecs.pair_first(
``` ```
Returns the first element (the relation part) of a pair ID. Returns the first element (the relation part) of a pair ID.
**Example:**
```luau
local Likes = world:component()
local alice = world:entity()
local bob = world:entity()
local pair_id = pair(Likes, alice)
local relation = jecs.pair_first(pair_id)
print(relation == Likes) -- true
```
## pair_second() ## pair_second()
```luau ```luau
function jecs.pair_second( function jecs.pair_second(

196
docs/api/observers.md Executable file
View file

@ -0,0 +1,196 @@
# Observers
The observers addon extends the World with signal-based reactivity and query-based observers. This addon provides a more ergonomic way to handle component lifecycle events and query changes.
## Installation
The observers addon is included with jecs and can be imported directly:
```luau
local jecs = require(path/to/jecs)
local observers_add = require(path/to/jecs/addons/observers)
local world = observers_add(jecs.world())
```
## Methods
### added
Registers a callback that is invoked when a component is added to any entity.
```luau
function World:added<T>(
component: Id<T>,
callback: (entity: Entity, id: Id<T>, value: T?) -> ()
): () -> () -- Returns an unsubscribe function
```
**Parameters:**
- `component` - The component ID to listen for additions
- `callback` - Function called when component is added, receives entity, component ID, and value
**Returns:** An unsubscribe function that removes the listener when called
**Example:**
```luau
local Health = world:component() :: jecs.Entity<number>
local unsubscribe = world:added(Health, function(entity, id, value)
print("Health component added to entity", entity, "with value", value)
end)
-- Later, to stop listening:
unsubscribe()
```
### removed
Registers a callback that is invoked when a component is removed from any entity.
```luau
function World:removed<T>(
component: Id<T>,
callback: (entity: Entity, id: Id<T>) -> ()
): () -> () -- Returns an unsubscribe function
```
**Parameters:**
- `component` - The component ID to listen for removals
- `callback` - Function called when component is removed, receives entity and component ID
**Returns:** An unsubscribe function that removes the listener when called
**Example:**
```luau
local Health = world:component() :: jecs.Entity<number>
local unsubscribe = world:removed(Health, function(entity, id)
print("Health component removed from entity", entity)
end)
```
### changed
Registers a callback that is invoked when a component's value is changed on any entity.
```luau
function World:changed<T>(
component: Id<T>,
callback: (entity: Entity, id: Id<T>, value: T) -> ()
): () -> () -- Returns an unsubscribe function
```
**Parameters:**
- `component` - The component ID to listen for changes
- `callback` - Function called when component value changes, receives entity, component ID, and new value
**Returns:** An unsubscribe function that removes the listener when called
**Example:**
```luau
local Health = world:component() :: jecs.Entity<number>
local unsubscribe = world:changed(Health, function(entity, id, value)
print("Health changed to", value, "for entity", entity)
end)
```
### observer
Creates a query-based observer that triggers when entities match or stop matching a query.
```luau
function World:observer<T...>(
query: Query<T...>,
callback: ((entity: Entity, id: Id, value: any?) -> ())?
): () -> () -> Entity -- Returns an iterator function
```
**Parameters:**
- `query` - The query to observe for changes
- `callback` - Optional function called when entities match the query
**Returns:** An iterator function that returns entities that matched the query since last iteration
**Example:**
```luau
local Position = world:component() :: jecs.Id<Vector3>
local Velocity = world:component() :: jecs.Id<Vector3>
local moving_entities = world:observer(
world:query(Position, Velocity),
function(entity, id, value)
print("Entity", entity, "started moving")
end
)
-- In your game loop:
for entity in moving_entities() do
-- Process newly moving entities
end
```
### monitor
Creates a query-based monitor that triggers when entities are added to or removed from a query.
```luau
function World:monitor<T...>(
query: Query<T...>,
callback: ((entity: Entity, id: Id, value: any?) -> ())?
): () -> () -> Entity -- Returns an iterator function
```
**Parameters:**
- `query` - The query to monitor for additions/removals
- `callback` - Optional function called when entities are added or removed from the query
**Returns:** An iterator function that returns entities that were added or removed since last iteration
**Example:**
```luau
local Health = world:component() :: jecs.Id<number>
local health_changes = world:monitor(
world:query(Health),
function(entity, id, value)
print("Health component changed for entity", entity)
end
)
-- In your game loop:
for entity in health_changes() do
-- Process entities with health changes
end
```
## Usage Patterns
### Component Lifecycle Tracking
```luau
local Player = world:component()
local Health = world:component() :: jecs.Id<number>
-- Track when players are created
world:added(Player, function(entity, id, instance)
instance:SetAttribute("entityid", entity)
end)
world:removed(Player, function(entity, id)
world:add(entity, Destroy) -- process its deletion later!
end)
```
## Performance Considerations
- **Signal listeners** are called immediately when components are added/removed/changed
- **Query observers** cache the query for better performance
- **Multiple listeners** for the same component are supported and called in registration order
- **Unsubscribe functions** should be called when listeners are no longer needed to prevent memory leaks
- **Observer iterators** should be called regularly to clear the internal buffer
## Integration with Built-in Hooks
The observers addon integrates with the built-in component hooks (`OnAdd`, `OnRemove`, `OnChange`). If a component already has these hooks configured, the observers addon will preserve them and call both the original hook and any registered signal listeners.

View file

@ -120,9 +120,26 @@ This function is meant for people who want to really customize their query behav
::: :::
## iter ## iter
In most cases, you can iterate over queries directly using `for entity, ... in query do`. The `:iter()` method is mainly useful if you are on the old solver, to get types for the returned values.
If you are on the old solver, to get types for the returned values, requires usage of `:iter` to get an explicit returntype of an iterator function.
```luau ```luau
function Query:iter(): () -> (Entity, ...) function Query:iter(): () -> (Entity, ...)
``` ```
Example:
::: code-group
```luau [luau]
local query = world:query(Position, Velocity)
-- Direct iteration (recommended)
for entity, position, velocity in query do
-- Process entity
end
-- Using explicit iterator (when needed for the old solver)
local iterator = query:iter()
for entity, position, velocity in iterator do
-- Process entity
end
```

View file

@ -6,7 +6,7 @@ A World contains entities which have components. The World is queryable and can
## new ## new
`World` utilizes a class, meaning JECS allows you to create multiple worlds. `World` utilizes a class, meaning jecs allows you to create multiple worlds.
```luau ```luau
function World.new(): World function World.new(): World
@ -55,7 +55,7 @@ const entity = world.entity();
## component ## component
Creates a new component. Do note components are entities as well, meaning JECS allows you to add other components onto them. Creates a new component. Do note components are entities as well, meaning jecs allows you to add other components onto them.
These are meant to be added onto other entities through `add` and `set` These are meant to be added onto other entities through `add` and `set`
@ -545,6 +545,122 @@ Enforces a check for entities to be created within a desired range.
```luau ```luau
function World:range( function World:range(
range_begin: number -- The starting point, range_begin: number -- The starting point,
range_begin: number? -- The end point (optional) range_end: number? -- The end point (optional)
) )
``` ```
Example:
::: code-group
```luau [luau]
world:range(1000, 5000) -- Entities will be created with IDs 1000-5000
local entity = world:entity()
print(entity) -- Will be >= 1000 and < 5000
```
```ts [typescript]
world.range(1000, 5000) // Entities will be created with IDs 1000-5000
const entity = world.entity()
print(entity) // Will be >= 1000 and < 5000
```
:::
## parent
Gets the parent entity of the specified entity using the built-in `ChildOf` relationship.
```luau
function World:parent(
entity: Entity
): Entity? -- Returns the parent entity or nil if no parent
```
Example:
::: code-group
```luau [luau]
local parent = world:entity()
local child = world:entity()
world:add(child, pair(jecs.ChildOf, parent))
local retrieved_parent = world:parent(child)
print(retrieved_parent == parent) -- true
```
```ts [typescript]
const parent = world.entity()
const child = world.entity()
world.add(child, pair(jecs.ChildOf, parent))
const retrievedParent = world.parent(child)
print(retrievedParent === parent) // true
```
:::
## contains
Checks if an entity exists and is alive in the world.
```luau
function World:contains(
entity: Entity
): boolean
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
print(world:contains(entity)) -- true
world:delete(entity)
print(world:contains(entity)) -- false
```
```ts [typescript]
const entity = world.entity()
print(world.contains(entity)) // true
world.delete(entity)
print(world.contains(entity)) // false
```
:::
## exists
Alias for `contains`. Checks if an entity exists and is alive in the world.
```luau
function World:exists(
entity: Entity
): boolean
```
## cleanup
Cleans up deleted entities and their associated data. This is automatically called by jecs, but can be called manually if needed.
```luau
function World:cleanup(): void
```
Example:
::: code-group
```luau [luau]
local entity = world:entity()
world:delete(entity)
-- Cleanup is usually automatic, but can be called manually
world:cleanup()
```
```ts [typescript]
const entity = world.entity()
world.delete(entity)
// Cleanup is usually automatic, but can be called manually
world.cleanup()
```
:::

View file

@ -15,6 +15,9 @@ hero:
- theme: alt - theme: alt
text: API References text: API References
link: /api/jecs.md link: /api/jecs.md
- theme: alt
text: Observers
link: /api/observers.md
features: features:
- title: Stupidly Fast - title: Stupidly Fast