mirror of
https://github.com/Ukendio/jecs.git
synced 2025-08-04 03:09:18 +00:00
Add some examples
This commit is contained in:
parent
3777585677
commit
1c2dee57d3
5 changed files with 348 additions and 5 deletions
|
@ -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
196
docs/api/observers.md
Executable 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.
|
|
@ -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
|
||||||
|
```
|
|
@ -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()
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue