Component bundles#
A bundle names one reusable component set:
local playerBundle = world:newBundle("Player", {
required = {tecs.ecs.Transform2D, Health},
with = {
[tecs.gfx.Tint] = function()
return tecs.gfx.Tint(1, 1, 1, 1)
end,
[tecs.gfx.Renderable2D] = true,
},
})
local player = playerBundle:spawn(
tecs.ecs.Transform2D(100, 200),
Health(100)
)required sets the positional arguments to spawn. with creates the rest for every entity.
Required components#
Declaration order controls spawn order:
local enemyBundle = world:newBundle("Enemy", {
required = {tecs.ecs.Transform2D, Health, Damage},
})
local enemy = enemyBundle:spawn(
tecs.ecs.Transform2D(100, 200),
Health(50),
Damage(10)
)Every argument must match its declared component. Move any value that varies per spawn into required.
Bundle defaults#
Each with value must hold a factory or true. A factory runs once per spawn and returns a fresh instance:
local bulletBundle = world:newBundle("Bullet", {
required = {tecs.ecs.Transform2D},
with = {
[Velocity] = function()
return Velocity(100, 0)
end,
[Damage] = function()
return Damage(25)
end,
},
})true uses the component's default value. It suits tags and components whose declared defaults already have the right value:
local propBundle = world:newBundle("Prop", {
required = {tecs.ecs.Transform2D},
with = {
[tecs.gfx.Renderable2D] = true,
[Static] = true,
},
})A spawn cannot override a component from with. Put that component in required when callers need to supply it.
The definition may name each component once across required and with. Registration rejects duplicates, invalid with values, and duplicate bundle names.
Staged spawning#
The bundle object and the world registry call the same spawn path:
local first = playerBundle:spawn(
tecs.ecs.Transform2D(0, 0),
Health(100)
)
local second = world:spawnBundle(
"Player",
tecs.ecs.Transform2D(20, 0),
Health(100)
)Bundle spawns follow world:spawn timing. They reserve an ID immediately and stage placement until the next pipeline barrier. The returned ID works immediately for later staged operations:
for _archetype, _length in query:iter() do
local id = playerBundle:spawn(
tecs.ecs.Transform2D(0, 0),
Health(100)
)
world:set(id, Selected)
endPlain bundles cache their resolved signature, including transitive requirements and the active state tag. They enqueue raw values in a retained, fixed-stride queue and publish directly into columns. Ordinary plain spawns and scalar mutations use the same flattened transaction storage. Pending edits coalesce before publication; relationships and durable keys retain their required indexing and validation paths. A state change resolves the next bundle burst without retagging earlier reservations.
Registry lookup#
world:getBundle(name) returns one bundle or nil. world:getBundles() returns a fresh name-to-bundle map, so changing the map does not change the registry.