Expand the How To series

This commit is contained in:
Ukendio 2026-02-05 00:32:59 +01:00
parent eacc343551
commit 33f7c08025
5 changed files with 103 additions and 84 deletions

View file

@ -43,6 +43,12 @@ world:add(entity, Dead) -- Adds the tag
print(world:has(entity, Dead)) -- true
-- jecs.is_tag(world, id) returns true if the id is a tag (no data).
print(jecs.is_tag(world, Dead)) -- true
local Position = world:component() :: jecs.Id<number>
print(jecs.is_tag(world, Position)) -- false
-- Tags are removed using world:remove(entity, component)
world:remove(entity, Dead)

View file

View file

@ -2,7 +2,7 @@
Relationships makes it possible to describe entity graphs natively in ECS.
Adding/removing relationships is similar to adding/removing regular components,
with as difference that instead of a single component id, a relationship adds
with the difference that instead of a single component id, a relationship adds
a pair of two things to an entity. In this pair, the first element represents
the relationship (e.g. "Eats"), and the second element represents the relationship
target (e.g. "Apples").
@ -36,9 +36,6 @@ world:add(alice, pair(Likes, bob))
-- Test if entity has a relationship pair
print(world:has(bob, pair(Eats, Apples))) -- true
-- Test if entity has a relationship wildcard
print(world:has(bob, pair(Eats, jecs.Wildcard))) -- true
--[[
Querying for relationship targets
@ -77,26 +74,6 @@ for child in world:query(pair(ChildOf, parent)) do
print(`Entity {child} is a child of parent {parent}`)
end
--[[
Querying with wildcards and getting targets
When you query with a wildcard, you can use world:target() to get the
actual target entity. This is useful when you want to find all entities
with a relationship, regardless of the target.
]]
-- Find all entities that eat something (any target)
for entity in world:query(pair(Eats, jecs.Wildcard)) do
local food = world:target(entity, Eats) -- Get the actual target
print(`Entity {entity} eats {food}`)
end
-- Find all entities that like someone (any target)
for entity in world:query(pair(Likes, jecs.Wildcard)) do
local target = world:target(entity, Likes)
print(`Entity {entity} likes {target}`)
end
--[[
Combining relationship queries with regular components
@ -118,39 +95,6 @@ for entity, pos, health in world:query(Position, Health, pair(ChildOf, parent))
print(`Child {entity} has position {pos} and health {health}`)
end
--[[
Querying for entities with multiple relationship targets
An entity can have multiple relationships with the same relationship type
but different targets. For example, bob might like both alice and charlie.
When querying with a wildcard, you'll get the entity once, but world:target()
will return the first matching target. If you need all targets, you'll need
to use a different approach (see the targets example for advanced usage).
]]
local charlie = world:entity()
world:add(bob, pair(Likes, charlie))
-- This query will return bob once, even though bob likes both alice and charlie
for entity in world:query(pair(Likes, jecs.Wildcard)) do
local target = world:target(entity, Likes)
print(`Entity {entity} likes {target}`) -- Will show one target per entity
end
--[[
Querying for all relationships with a specific target
You can also query for all entities that have any relationship with a
specific target using a wildcard for the relationship part.
]]
-- Find all entities that have any relationship with alice as the target
for entity in world:query(pair(jecs.Wildcard, alice)) do
-- Note: This is less common and may have performance implications
print(`Entity {entity} has some relationship with alice`)
end
--[[
Relationship pairs, just like regular component, can be associated with data.
]]
@ -178,30 +122,4 @@ for entity, eats_data in world:query(pair(Eats, Apples)) do
print(`Entity {entity} eats apples: amount = {eats_data.amount}`)
end
--[[
When querying for relationship pairs, it is often useful to be able to find
all instances for a given relationship or target. To accomplish this, a game
can use wildcard expressions.
Wildcards may used for the relationship or target part of a pair:
pair(Likes, jecs.Wildcard) -- Matches all Likes relationships
pair(jecs.Wildcard, Alice) -- Matches all relationships with Alice as target
Using world:target() is the recommended way to get the target in a wildcard
query. However, if you're in a very hot path and need maximum performance,
you can access the relationship column directly (see advanced examples).
]]
for entity in world:query(pair(Eats, jecs.Wildcard)) do
local nth = 0
local food = world:target(entity, Eats, nth)
while food do
local eats_data = world:get(entity, pair(Eats, food))
assert(eats_data) -- This coerces the type to be non-nilable for the type checker
print(`Entity {entity} eats {food}: amount = {eats_data.amount}`)
nth += 1
food = world:target(entity, Eats, nth)
end
end
-- For wildcard queries and world:target (0-based index), see 042_target.luau and 043_wildcards.luau.

37
how_to/042_target.luau Normal file
View file

@ -0,0 +1,37 @@
--[[
world:target(entity, relation, index?) returns the target of a relationship
on an entity. The index is 0-based. If no target exists at that index, it
returns nil.
Use target when you have queried with a wildcard (e.g. pair(Eats, jecs.Wildcard))
and need the actual target entity for each result. Without an index, the
default is 0 (the first target).
]]
local jecs = require("@jecs")
local pair = jecs.pair
local world = jecs.world()
local Eats = world:entity()
local Apples = world:entity()
local Oranges = world:entity()
local bob = world:entity()
world:add(bob, pair(Eats, Apples))
world:add(bob, pair(Eats, Oranges))
-- First target is at index 0
local first = world:target(bob, Eats, 0)
print(first == Apples) -- true
-- Second target is at index 1
local second = world:target(bob, Eats, 1)
print(second == Oranges) -- true
-- No third target: index 2 returns nil
local third = world:target(bob, Eats, 2)
print(third == nil) -- true
-- Omitting the index is the same as index 0
local default = world:target(bob, Eats)
print(default == Apples) -- true

58
how_to/043_wildcards.luau Normal file
View file

@ -0,0 +1,58 @@
--[[
Wildcards let you query relationships without specifying the exact target or
relationship. jecs.Wildcard matches any entity in that slot.
- pair(relation, jecs.Wildcard) matches that relationship with any target.
- pair(jecs.Wildcard, target) matches any relationship with that target.
Use world:target(entity, relation, index) to get the actual target when
querying with a wildcard. The index is 0-based (see 042_target.luau).
]]
local jecs = require("@jecs")
local pair = jecs.pair
local world = jecs.world()
local Eats = world:component() :: jecs.Id<{ amount: number }>
local Likes = world:entity()
local Apples = world:entity()
local alice = world:entity()
local bob = world:entity()
world:add(bob, pair(Eats, Apples))
world:set(bob, pair(Eats, Apples), { amount = 1 })
world:add(bob, pair(Likes, alice))
-- world:has with wildcard
print(world:has(bob, pair(Eats, jecs.Wildcard))) -- true
-- Query with wildcard: all entities that eat something
for entity in world:query(pair(Eats, jecs.Wildcard)) do
local food = world:target(entity, Eats)
print(`Entity {entity} eats {food}`)
end
-- Query with wildcard: all entities that like someone
for entity in world:query(pair(Likes, jecs.Wildcard)) do
local target = world:target(entity, Likes)
print(`Entity {entity} likes {target}`)
end
-- Multiple targets: index is 0-based. Iterate until nil.
local charlie = world:entity()
world:add(bob, pair(Likes, charlie))
for entity in world:query(pair(Likes, jecs.Wildcard)) do
local nth = 0
local target = world:target(entity, Likes, nth)
while target do
print(`Entity {entity} likes {target}`)
nth += 1
target = world:target(entity, Likes, nth)
end
end
-- pair(jecs.Wildcard, target): all relationships that have this target
for entity in world:query(pair(jecs.Wildcard, alice)) do
print(`Entity {entity} has some relationship with alice`)
end