Repository: mlange-42/arche
Pull request #100 (2023-02-17 22:14:33)

Clean up API
* Rename `ChangeEvent` to `EntityEvent`
* Rename `World.RegisterListener` to `World.SetListener`, allow overwriting the listener
* Limit world construction to `NewWorld(...Config)`
* Remove `World.ID()` and `Query.ID()`
* Remove `World.Assign`, rename `World.AssignN` to `World.Assign`
* All methods of `BitMask` use pointer receiver
* Method `Matches` for `MaskPair` uses pointer receiver for 20% match speedup
* Remove type `Mask`, use `BitMask` directly instead, and rename it to `Mask`
* Use slice instead of array for component indexing in archetypes
* Rename `MaskPair` to `MaskFilter`


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc630fb..a4de62f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 * Optimization of adding/removing components, with 2-3x speedup and vast reduction of (number of) allocations (#93)
 * More examples as user documentation (#83, #95)
 * Speed up component world access by use of nil pointer check instead of bitmask (#96)
+* General API cleanup with renaming of several types and methods (#100)
 
 ## [[v0.3.1]](https://github.com/mlange-42/arche/compare/v0.3.0...v0.3.1)
 
diff --git a/README.md b/README.md
index d20987d..9e49b65 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ go get github.com/mlange-42/arche
 
 ## Features
 
-* Minimal core API. See the [API docs](https://pkg.go.dev/github.com/mlange-42/arche).
+* Simple core API. See the [API docs](https://pkg.go.dev/github.com/mlange-42/arche).
 * Optional rich filtering and generic query API.
 * Fast iteration and component access via queries (≈2.5ns iterate + get).
 * Fast random access for components of arbitrary entities. Useful for hierarchies.
diff --git a/benchmark/profile/exchange/main.go b/benchmark/profile/exchange/main.go
index 5b4efe1..08d5edf 100644
--- a/benchmark/profile/exchange/main.go
+++ b/benchmark/profile/exchange/main.go
@@ -42,9 +42,9 @@ func main() {
 
 func run(rounds, iters, numEntities int) {
 	for i := 0; i < rounds; i++ {
-		world := ecs.NewConfig().
-			WithCapacityIncrement(1024).
-			Build()
+		world := ecs.NewWorld(
+			ecs.NewConfig().WithCapacityIncrement(1024),
+		)
 
 		posID := ecs.ComponentID[position](&world)
 		velID := ecs.ComponentID[velocity](&world)
diff --git a/benchmark/profile/iter/main.go b/benchmark/profile/iter/main.go
index 61dafce..bf7bd2d 100644
--- a/benchmark/profile/iter/main.go
+++ b/benchmark/profile/iter/main.go
@@ -37,9 +37,9 @@ func main() {
 
 func run(rounds, iters, entities int) {
 	for i := 0; i < rounds; i++ {
-		world := ecs.NewConfig().
-			WithCapacityIncrement(1024).
-			Build()
+		world := ecs.NewWorld(
+			ecs.NewConfig().WithCapacityIncrement(1024),
+		)
 
 		posID := ecs.ComponentID[position](&world)
 		rotID := ecs.ComponentID[rotation](&world)
diff --git a/benchmark/profile/world/main.go b/benchmark/profile/world/main.go
index fa2571d..41aa00a 100644
--- a/benchmark/profile/world/main.go
+++ b/benchmark/profile/world/main.go
@@ -37,9 +37,9 @@ func main() {
 
 func run(rounds, iters, entityCount int) {
 	for i := 0; i < rounds; i++ {
-		world := ecs.NewConfig().
-			WithCapacityIncrement(1024).
-			Build()
+		world := ecs.NewWorld(
+			ecs.NewConfig().WithCapacityIncrement(1024),
+		)
 
 		posID := ecs.ComponentID[position](&world)
 		rotID := ecs.ComponentID[rotation](&world)
diff --git a/doc.go b/doc.go
index 956119d..c590b60 100644
--- a/doc.go
+++ b/doc.go
@@ -4,4 +4,5 @@
 //   - Core API -- [github.com/mlange-42/arche/ecs]
 //   - Generic queries -- [github.com/mlange-42/arche/generic]
 //   - Advanced filters -- [github.com/mlange-42/arche/filter]
+//   - Usage examples -- [github.com/mlange-42/arche/examples]
 package arche
diff --git a/ecs/archetype.go b/ecs/archetype.go
index bb0969b..dcc051a 100644
--- a/ecs/archetype.go
+++ b/ecs/archetype.go
@@ -9,11 +9,11 @@ import (
 
 // archetype represents an ECS archetype
 type archetype struct {
-	Mask BitMask
+	Mask Mask
 	Ids  []ID
 	// Indirection to avoid a fixed-size array of storages
 	// Increases access time by 50-100%
-	references [MaskTotalBits]*storage
+	references []*storage
 	entities   genericStorage[Entity]
 	components []storage
 	toAdd      []*archetype
@@ -22,11 +22,12 @@ type archetype struct {
 
 // Init initializes an archetype
 func (a *archetype) Init(capacityIncrement int, forStorage bool, components ...componentType) {
-	var mask BitMask
+	var mask Mask
 	if len(components) > 0 {
 		a.Ids = make([]ID, len(components))
 	}
-	comps := make([]storage, len(components))
+	a.components = make([]storage, len(components))
+	a.references = make([]*storage, MaskTotalBits)
 
 	prev := -1
 	for i, c := range components {
@@ -37,13 +38,12 @@ func (a *archetype) Init(capacityIncrement int, forStorage bool, components ...c
 
 		mask.Set(c.ID, true)
 		a.Ids[i] = c.ID
-		comps[i] = storage{}
-		comps[i].Init(c.Type, capacityIncrement, forStorage)
-		a.references[c.ID] = &comps[i]
+		a.components[i] = storage{}
+		a.components[i].Init(c.Type, capacityIncrement, forStorage)
+		a.references[c.ID] = &a.components[i]
 	}
 
 	a.Mask = mask
-	a.components = comps
 	a.entities = genericStorage[Entity]{}
 	a.toAdd = make([]*archetype, MaskTotalBits)
 	a.toRemove = make([]*archetype, MaskTotalBits)
@@ -93,7 +93,7 @@ func (a *archetype) Add(entity Entity, components ...Component) uint32 {
 	}
 	idx := a.entities.Add(entity)
 	for _, c := range components {
-		a.references[c.ID].Add(c.Component)
+		a.references[c.ID].Add(c.Comp)
 	}
 	return idx
 }
diff --git a/ecs/bitmask.go b/ecs/bitmask.go
index 6f5e0cc..d0592a8 100644
--- a/ecs/bitmask.go
+++ b/ecs/bitmask.go
@@ -8,27 +8,37 @@ import "math/bits"
 const MaskTotalBits = 128
 const wordSize = 64
 
-// BitMask is a 128 bit bitmask.
-type BitMask struct {
+// Mask is a 128 bit bitmask.
+// It is also a [Filter] for including certain components (see [All] and [Mask.Without]).
+type Mask struct {
 	Lo uint64
 	Hi uint64
 }
 
-// NewBitMask creates a new bitmask from a list of IDs.
+// All creates a new Mask from a list of IDs.
 //
-// If any ID is bigger or equal [MaskTotalBits], it'll not be added to the mask.
-func NewBitMask(ids ...ID) BitMask {
-	var mask BitMask
+// If any [ID] is bigger or equal [MaskTotalBits], it'll not be added to the mask.
+func All(ids ...ID) Mask {
+	var mask Mask
 	for _, id := range ids {
 		mask.Set(id, true)
 	}
 	return mask
 }
 
-// Get reports if bit index defined by ID is true or false.
+// Without creates a [MaskFilter] which filters for including the mask's components,
+// and excludes the components given as arguments.
+func (b Mask) Without(comps ...ID) MaskFilter {
+	return MaskFilter{
+		Include: b,
+		Exclude: All(comps...),
+	}
+}
+
+// Get reports if bit index defined by [ID] is true or false.
 //
 // The return will be always false for bit >= [MaskTotalBits].
-func (b BitMask) Get(bit ID) bool {
+func (b *Mask) Get(bit ID) bool {
 	if bit < wordSize {
 		mask := uint64(1 << bit)
 		return b.Lo&mask == mask
@@ -40,7 +50,7 @@ func (b BitMask) Get(bit ID) bool {
 // Set sets the state of bit index to true or false.
 //
 // This function has no effect for bit >= [MaskTotalBits].
-func (b *BitMask) Set(bit ID, value bool) {
+func (b *Mask) Set(bit ID, value bool) {
 	if bit < wordSize {
 		if value {
 			b.Lo |= uint64(1 << bit)
@@ -56,27 +66,27 @@ func (b *BitMask) Set(bit ID, value bool) {
 }
 
 // IsZero returns whether no bits are set in the bitmask.
-func (b BitMask) IsZero() bool {
+func (b *Mask) IsZero() bool {
 	return b.Lo == 0 && b.Hi == 0
 }
 
 // Reset changes the state of all bits to false.
-func (b *BitMask) Reset() {
+func (b *Mask) Reset() {
 	b.Lo = 0
 	b.Hi = 0
 }
 
 // Contains reports if other mask is a subset of this mask.
-func (b BitMask) Contains(other BitMask) bool {
+func (b *Mask) Contains(other Mask) bool {
 	return b.Lo&other.Lo == other.Lo && b.Hi&other.Hi == other.Hi
 }
 
 // ContainsAny reports if any bit of other mask is in this mask.
-func (b BitMask) ContainsAny(other BitMask) bool {
+func (b *Mask) ContainsAny(other Mask) bool {
 	return b.Lo&other.Lo != 0 || b.Hi&other.Hi != 0
 }
 
 // TotalBitsSet returns how many bits are set in this mask.
-func (b BitMask) TotalBitsSet() int {
+func (b *Mask) TotalBitsSet() int {
 	return bits.OnesCount64(b.Hi) + bits.OnesCount64(b.Lo)
 }
diff --git a/ecs/bitmask64.go b/ecs/bitmask64.go
deleted file mode 100644
index 2ce5f19..0000000
--- a/ecs/bitmask64.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package ecs
-
-// bitMask64 is there just for performance comparison with the new 128 bit BitMask.
-type bitMask64 uint64
-
-func newBitMask64(ids ...ID) bitMask64 {
-	var mask bitMask64
-	for _, id := range ids {
-		mask.Set(id, true)
-	}
-	return mask
-}
-func (e bitMask64) Get(bit ID) bool {
-	mask := bitMask64(1 << bit)
-	return e&mask == mask
-}
-
-func (e *bitMask64) Set(bit ID, value bool) {
-	if value {
-		*e |= bitMask64(1 << bit)
-	} else {
-		*e &= bitMask64(^(1 << bit))
-	}
-}
diff --git a/ecs/config.go b/ecs/config.go
index 3d3f397..07ec7c8 100644
--- a/ecs/config.go
+++ b/ecs/config.go
@@ -5,7 +5,7 @@ type Config struct {
 	CapacityIncrement int
 }
 
-// NewConfig creates a new default [World] config.
+// NewConfig creates a new default [World] configuration.
 func NewConfig() Config {
 	return Config{
 		CapacityIncrement: 128,
@@ -18,8 +18,3 @@ func (c Config) WithCapacityIncrement(inc int) Config {
 	c.CapacityIncrement = inc
 	return c
 }
-
-// Build creates a [World] from this Config.
-func (c Config) Build() World {
-	return FromConfig(c)
-}
diff --git a/ecs/entity.go b/ecs/entity.go
index e22c168..592ade8 100644
--- a/ecs/entity.go
+++ b/ecs/entity.go
@@ -11,7 +11,7 @@ var entityIndexSize = reflect.TypeOf(entityIndex{}).Size()
 // Entity identifier.
 // Holds an entity ID and it's generation for recycling.
 //
-// Entities should only be created via the [World], using [World.NewEntity].
+// Entities should only be created via the [World], using [World.NewEntity] or [World.NewEntityWith].
 type Entity struct {
 	id  eid
 	gen uint16
diff --git a/ecs/event.go b/ecs/event.go
index 24da3ad..590ed3a 100644
--- a/ecs/event.go
+++ b/ecs/event.go
@@ -1,13 +1,13 @@
 package ecs
 
-// ChangeEvent contains information about component changes.
+// EntityEvent contains information about component changes.
 //
-// To receive change events, register a function func(e ChangeEvent) with [World.RegisterListener].
-type ChangeEvent struct {
+// To receive change events, register a function func(e EntityEvent) with [World.SetListener].
+type EntityEvent struct {
 	// The entity that was changed.
 	Entity Entity
 	// The old and new component masks.
-	OldMask, NewMask BitMask
+	OldMask, NewMask Mask
 	// Components added, removed, and after the change.
 	Added, Removed, Current []ID
 	// Whether the entity itself was added (> 0), removed (< 0), or only changed (= 0).
@@ -15,11 +15,11 @@ type ChangeEvent struct {
 }
 
 // EntityAdded reports whether the entity was newly added.
-func (e *ChangeEvent) EntityAdded() bool {
+func (e *EntityEvent) EntityAdded() bool {
 	return e.AddedRemoved > 0
 }
 
 // EntityRemoved reports whether the entity was removed.
-func (e *ChangeEvent) EntityRemoved() bool {
+func (e *EntityEvent) EntityRemoved() bool {
 	return e.AddedRemoved < 0
 }
diff --git a/ecs/query.go b/ecs/query.go
index 638d183..6230b83 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -4,60 +4,41 @@ import (
 	"unsafe"
 )
 
-// Filter is the interface for logic filters
+// Filter is the interface for logic filters.
+// Filters are required to query entities using [World.Query].
+//
+// See [Mask] and [MaskFilter] for basic filters.
+// For type-safe generics queries, see package [github.com/mlange-42/arche/generic].
+// For advanced filtering, see package [github.com/mlange-42/arche/filter].
 type Filter interface {
 	// Matches the filter against a bitmask, i.e. a component composition.
-	Matches(bits BitMask) bool
-}
-
-// Mask for a combination of components.
-type Mask struct {
-	BitMask BitMask
-}
-
-// All creates a new Mask from a list of IDs.
-//
-// If any ID is bigger or equal [MaskTotalBits], it'll not be added to the mask.
-func All(ids ...ID) Mask {
-	var mask BitMask
-	for _, id := range ids {
-		mask.Set(id, true)
-	}
-	return Mask{mask}
-}
-
-// Matches matches a filter against a bitmask
-func (f Mask) Matches(bits BitMask) bool {
-	return bits.Contains(f.BitMask)
+	Matches(bits Mask) bool
 }
 
-// Without excludes the given components.
-func (f Mask) Without(comps ...ID) MaskPair {
-	return MaskPair{
-		Mask:    f,
-		Exclude: All(comps...),
-	}
+// MaskFilter is a [Filter] for including and excluding certain components.
+// See [All] and [Mask.Without].
+type MaskFilter struct {
+	Include Mask
+	Exclude Mask
 }
 
-// MaskPair is a filter for including an excluding components
-type MaskPair struct {
-	Mask    Mask
-	Exclude Mask
+// Matches matches a filter against a mask.
+func (f *MaskFilter) Matches(bits Mask) bool {
+	return bits.Contains(f.Include) &&
+		(f.Exclude.IsZero() || !bits.ContainsAny(f.Exclude))
 }
 
-// Matches matches a filter against a mask
-func (f MaskPair) Matches(bits BitMask) bool {
-	ex := f.Exclude.BitMask
-	return bits.Contains(f.Mask.BitMask) && (ex.IsZero() || !bits.ContainsAny(ex))
+// Matches matches a filter against a bitmask.
+func (b Mask) Matches(bits Mask) bool {
+	return bits.Contains(b)
 }
 
-// Query is an advanced iterator to iterate entities.
+// Query is an iterator to iterate entities, filtered by a [Filter].
 //
 // Create queries through the [World] using [World.Query].
 //
 // See also the generic alternatives [github.com/mlange-42/arche/generic.Query1],
 // [github.com/mlange-42/arche/generic.Query2], etc.
-//
 // For advanced filtering, see package [github.com/mlange-42/arche/filter]
 type Query struct {
 	queryIter
@@ -82,10 +63,10 @@ func (q *Query) Next() bool {
 		return true
 	}
 	// outline to allow inlining of the fast path
-	return q.slowNext()
+	return q.nextArchetype()
 }
 
-func (q *Query) slowNext() bool {
+func (q *Query) nextArchetype() bool {
 	index, archetype, ok := q.world.nextArchetype(q.filter, q.index)
 	q.index = index
 	if ok {
@@ -121,18 +102,10 @@ func (q *queryIter) Entity() Entity {
 // Mask returns the archetype [BitMask] for the [Entity] at the iterator's current position.
 //
 // Can be used for fast checks of the entity composition, e.g. using a [Filter].
-func (q *queryIter) Mask() BitMask {
+func (q *queryIter) Mask() Mask {
 	return q.archetype.Archetype.Mask
 }
 
-// IDs returns the archetype's component IDs for the [Entity] at the iterator's current position.
-//
-// Makes a copy of the slice for immutability, so there is a certain overhead involved.
-func (q *queryIter) IDs() []ID {
-	var ids []ID
-	return append(ids, q.archetype.Archetype.Ids...)
-}
-
 // Close closes the Query and unlocks the world.
 //
 // Automatically called when iteration finishes.
diff --git a/ecs/types.go b/ecs/types.go
index 3b0443c..cb23b7b 100644
--- a/ecs/types.go
+++ b/ecs/types.go
@@ -2,16 +2,19 @@ package ecs
 
 import "reflect"
 
-// Eid is the entity identifier/index type
+// Eid is the entity identifier/index type.
 type eid uint32
 
-// ID is the component identifier type
+// ID is the component identifier type.
 type ID = uint8
 
-// Component is a Component ID/Component pointer pair
+// Component is a component ID/pointer pair.
+//
+// It is a helper for [World.Assign] and [World.NewEntityWith].
+// It is not related to how components are implemented in Arche.
 type Component struct {
-	ID
-	Component interface{}
+	ID   ID
+	Comp interface{}
 }
 
 // componentType is a component ID with a data type
diff --git a/ecs/world.go b/ecs/world.go
index 6c0e4c1..61cf82f 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -7,13 +7,13 @@ import (
 	"github.com/mlange-42/arche/ecs/stats"
 )
 
-// ComponentID returns the ID for a component type via generics. Registers the type if it is not already registered.
+// ComponentID returns the [ID] for a component type via generics. Registers the type if it is not already registered.
 func ComponentID[T any](w *World) ID {
 	tp := reflect.TypeOf((*T)(nil)).Elem()
 	return w.componentID(tp)
 }
 
-// TypeID returns the ID for a component type. Registers the type if it is not already registered.
+// TypeID returns the [ID] for a component type. Registers the type if it is not already registered.
 func TypeID(w *World, tp reflect.Type) ID {
 	return w.componentID(tp)
 }
@@ -26,17 +26,29 @@ type World struct {
 	entityPool entityPool
 	bitPool    bitPool
 	registry   componentRegistry
-	locks      BitMask
-	listener   func(e ChangeEvent)
+	locks      Mask
+	listener   func(e EntityEvent)
 }
 
-// NewWorld creates a new [World]
-func NewWorld() World {
-	return FromConfig(NewConfig())
+// NewWorld creates a new [World] from an optional [Config].
+//
+// Uses the default [Config] if called without an argument.
+// Accepts zero or one arguments.
+func NewWorld(config ...Config) World {
+	if len(config) > 1 {
+		panic("can't use more than one Config")
+	}
+	if len(config) == 1 {
+		return fromConfig(config[0])
+	}
+	return fromConfig(NewConfig())
 }
 
-// FromConfig creates a new [World] from a [Config]
-func FromConfig(conf Config) World {
+// fromConfig creates a new [World] from a [Config].
+func fromConfig(conf Config) World {
+	if conf.CapacityIncrement < 1 {
+		panic("invalid CapacityIncrement in config, must be > 0")
+	}
 	entities := make([]entityIndex, 1, conf.CapacityIncrement)
 	entities[0] = entityIndex{arch: nil, index: 0}
 	w := World{
@@ -46,10 +58,10 @@ func FromConfig(conf Config) World {
 		bitPool:    newBitPool(),
 		registry:   newComponentRegistry(),
 		archetypes: pagedArr32[archetype]{},
-		locks:      BitMask{},
+		locks:      Mask{},
 		listener:   nil,
 	}
-	w.createArchetype(BitMask{}, false)
+	w.createArchetype(Mask{}, false)
 	return w
 }
 
@@ -59,7 +71,7 @@ func FromConfig(conf Config) World {
 // Panics when called on a locked world.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
+// See also the generic variants under [github.com/mlange-42/arche/generic.Map1], etc.
 func (w *World) NewEntity(comps ...ID) Entity {
 	w.checkLocked()
 
@@ -84,7 +96,7 @@ func (w *World) NewEntity(comps ...ID) Entity {
 	}
 
 	if w.listener != nil {
-		w.listener(ChangeEvent{entity, BitMask{}, arch.Mask, comps, nil, arch.Ids, 1})
+		w.listener(EntityEvent{entity, Mask{}, arch.Mask, comps, nil, arch.Ids, 1})
 	}
 	return entity
 }
@@ -92,10 +104,13 @@ func (w *World) NewEntity(comps ...ID) Entity {
 // NewEntityWith returns a new or recycled [Entity].
 // The given component values are assigned to the entity.
 //
+// The components in the `Comp` field of [Component] must be pointers.
+// The passed pointers are no valid references to the assigned memory!
+//
 // Panics when called on a locked world.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
+// See also the generic variants under [github.com/mlange-42/arche/generic.Map1], etc.
 func (w *World) NewEntityWith(comps ...Component) Entity {
 	w.checkLocked()
 
@@ -126,11 +141,11 @@ func (w *World) NewEntityWith(comps ...Component) Entity {
 	}
 
 	for _, c := range comps {
-		w.copyTo(entity, c.ID, c.Component)
+		w.copyTo(entity, c.ID, c.Comp)
 	}
 
 	if w.listener != nil {
-		w.listener(ChangeEvent{entity, BitMask{}, arch.Mask, ids, nil, arch.Ids, 1})
+		w.listener(EntityEvent{entity, Mask{}, arch.Mask, ids, nil, arch.Ids, 1})
 	}
 	return entity
 }
@@ -146,7 +161,7 @@ func (w *World) RemoveEntity(entity Entity) {
 	oldArch := index.arch
 
 	if w.listener != nil {
-		w.listener(ChangeEvent{entity, oldArch.Mask, oldArch.Mask, nil, nil, oldArch.Ids, -1})
+		w.listener(EntityEvent{entity, oldArch.Mask, oldArch.Mask, nil, nil, oldArch.Ids, -1})
 	}
 
 	swapped := oldArch.Remove(index.index)
@@ -212,44 +227,50 @@ func (w *World) Has(entity Entity, comp ID) bool {
 // Mask returns the archetype [BitMask] for the given [Entity].
 //
 // Can be used for fast checks of the entity composition, e.g. using a [Filter].
-func (w *World) Mask(entity Entity) BitMask {
+func (w *World) Mask(entity Entity) Mask {
 	return w.entities[entity.id].arch.Mask
 }
 
-// IDs returns the archetype's component IDs for the given [Entity].
-//
-// Makes a copy of the slice for immutability, so there is a certain overhead involved.
-func (w *World) IDs(entity Entity) []ID {
-	var ids []ID
-	return append(ids, w.entities[entity.id].arch.Ids...)
-}
-
 // Add adds components to an [Entity].
 //
 // Panics when called with component that can't be added because they are already present.
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
+// See also the generic variants under [github.com/mlange-42/arche/generic.Map1], etc.
 func (w *World) Add(entity Entity, comps ...ID) {
 	w.Exchange(entity, comps, nil)
 }
 
-// Assign assigns a component to an [Entity], using a given pointer for the content.
-// See also [World.AssignN].
+// Assign assigns multiple components to an [Entity], using pointers for the content.
 //
-// The passed component must be a pointer.
-// Returns a pointer to the assigned memory.
-// The passed in pointer is not a valid reference to that memory!
+// The components in the `Comp` field of [Component] must be pointers.
+// The passed pointers are no valid references to the assigned memory!
 //
-// Panics when called with a component that can't be added because it is already present.
+// Panics when called with components that can't be added because they are already present.
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
-func (w *World) Assign(entity Entity, id ID, comp interface{}) unsafe.Pointer {
-	w.Exchange(entity, []ID{id}, nil)
-	return w.copyTo(entity, id, comp)
+// See also the generic variants under [github.com/mlange-42/arche/generic.Map1], etc.
+func (w *World) Assign(entity Entity, comps ...Component) {
+	len := len(comps)
+	if len == 0 {
+		panic("no components given to assign")
+	}
+	if len == 1 {
+		c := comps[0]
+		w.Exchange(entity, []ID{c.ID}, nil)
+		w.copyTo(entity, c.ID, c.Comp)
+		return
+	}
+	ids := make([]ID, len)
+	for i, c := range comps {
+		ids[i] = c.ID
+	}
+	w.Exchange(entity, ids, nil)
+	for _, c := range comps {
+		w.copyTo(entity, c.ID, c.Comp)
+	}
 }
 
 // Set overwrites a component for an [Entity], using a given pointer for the content.
@@ -268,45 +289,25 @@ func (w *World) Set(entity Entity, id ID, comp interface{}) unsafe.Pointer {
 	return w.copyTo(entity, id, comp)
 }
 
-// AssignN assigns multiple components to an [Entity], using pointers for the content.
-// See also [World.Assign].
-//
-// The passed components must be pointers.
-// The passed in pointers are no valid references to the assigned memory!
-//
-// Panics when called with components that can't be added because they are already present.
-// Panics when called on a locked world or for an already removed entity.
-// Do not use during [Query] iteration!
-//
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
-func (w *World) AssignN(entity Entity, comps ...Component) {
-	ids := make([]ID, len(comps))
-	for i, c := range comps {
-		ids[i] = c.ID
-	}
-	w.Exchange(entity, ids, nil)
-	for _, c := range comps {
-		w.copyTo(entity, c.ID, c.Component)
-	}
-}
-
 // Remove removes components from an entity.
 //
 // Panics when called with components that can't be removed because they are not present.
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
 //
-// See also the generic variants under [github.com/mlange-42/arche/generic.Mutate1], etc.
+// See also the generic variants under [github.com/mlange-42/arche/generic.Map1], etc.
 func (w *World) Remove(entity Entity, comps ...ID) {
 	w.Exchange(entity, nil, comps)
 }
 
-// Exchange adds and removes components in one pass
+// Exchange adds and removes components in one pass.
 //
 // Panics when called with components that can't be added or removed because
 // they are already present/not present, respectively.
 // Panics when called on a locked world or for an already removed entity.
 // Do not use during [Query] iteration!
+//
+// See also the generic variants under [github.com/mlange-42/arche/generic.Exchange].
 func (w *World) Exchange(entity Entity, add []ID, rem []ID) {
 	w.checkLocked()
 
@@ -354,7 +355,7 @@ func (w *World) Exchange(entity Entity, add []ID, rem []ID) {
 	w.entities[entity.id] = entityIndex{arch: arch, index: newIndex}
 
 	if w.listener != nil {
-		w.listener(ChangeEvent{entity, oldMask, arch.Mask, add, rem, arch.Ids, 0})
+		w.listener(EntityEvent{entity, oldMask, arch.Mask, add, rem, arch.Ids, 0})
 	}
 }
 
@@ -363,16 +364,15 @@ func (w *World) IsLocked() bool {
 	return !w.locks.IsZero()
 }
 
-// RegisterListener registers a func(e ChangeEvent) to the world.
-// The listener function is immediately called on every entity change.
+// SetListener sets a listener callback func(e EntityEvent) for the world.
+// The listener is immediately called on every [ecs.Entity] change.
+// Replaces the current listener. Call with `nil` to remove a listener.
 //
 // Events notified are entity creation, removal and changes to the component composition.
-//
-// Panics if there is already a listener registered.
-func (w *World) RegisterListener(listener func(e ChangeEvent)) {
-	if w.listener != nil {
-		panic("the world already has a change listener registered")
-	}
+// Events are emitted immediately after the change is applied.
+// Except for removal of an entity, where the event is emitted before removal.
+// This allows for inspection of the to-be-removed Entity.
+func (w *World) SetListener(listener func(e EntityEvent)) {
 	w.listener = listener
 }
 
@@ -445,14 +445,14 @@ func (w *World) findOrCreateArchetype(start *archetype, add []ID, rem []ID) *arc
 	return curr
 }
 
-func (w *World) findOrCreateArchetypeSlow(mask BitMask, forStorage bool) (*archetype, bool) {
+func (w *World) findOrCreateArchetypeSlow(mask Mask, forStorage bool) (*archetype, bool) {
 	if arch, ok := w.findArchetype(mask); ok {
 		return arch, false
 	}
 	return w.createArchetype(mask, forStorage), true
 }
 
-func (w *World) findArchetype(mask BitMask) (*archetype, bool) {
+func (w *World) findArchetype(mask Mask) (*archetype, bool) {
 	length := w.archetypes.Len()
 	for i := 0; i < length; i++ {
 		arch := w.archetypes.Get(i)
@@ -463,7 +463,7 @@ func (w *World) findArchetype(mask BitMask) (*archetype, bool) {
 	return nil, false
 }
 
-func (w *World) createArchetype(mask BitMask, forStorage bool) *archetype {
+func (w *World) createArchetype(mask Mask, forStorage bool) *archetype {
 	count := int(mask.TotalBitsSet())
 	types := make([]componentType, count)
 
diff --git a/examples/base/main.go b/examples/base/main.go
index 2bc061e..c02a1c8 100644
--- a/examples/base/main.go
+++ b/examples/base/main.go
@@ -54,7 +54,7 @@ func main() {
 	// Time loop.
 	for t := 0; t < 1000; t++ {
 		// Get a fresh query iterator.
-		query := world.Query(filter)
+		query := world.Query(&filter)
 		// Iterate it
 		for query.Next() {
 			// Component access through the Query.
diff --git a/examples/change_listener/main.go b/examples/change_listener/main.go
index 1be87e4..028f3b0 100644
--- a/examples/change_listener/main.go
+++ b/examples/change_listener/main.go
@@ -1,4 +1,5 @@
-// Demonstrates the use of a ChangeEvent listener.
+// Demonstrates the use of a EntityEvent listener,
+// notifying on changes to the component composition of entities.
 package main
 
 import (
@@ -25,7 +26,7 @@ type Rotation struct {
 }
 
 // Listen is called on entity changes.
-func Listen(evt ecs.ChangeEvent) {
+func Listen(evt ecs.EntityEvent) {
 	// Just prints out what the event is about.
 	// This could also be a method of a type that manages events.
 	// Could use e.g. filters to distribute events to interested/registered systems.
@@ -48,7 +49,7 @@ func main() {
 	rotID := ecs.ComponentID[Rotation](&world)
 
 	// Register a listener function.
-	world.RegisterListener(Listen)
+	world.SetListener(Listen)
 
 	// Create/manipulate/delete entities and observe the listener's output
 	e0 := world.NewEntity(posID)
diff --git a/examples/doc.go b/examples/doc.go
index fab5a4e..f612f96 100644
--- a/examples/doc.go
+++ b/examples/doc.go
@@ -1,4 +1,4 @@
 // Package examples contains examples for Arche.
 //
-// See package [github.com/mlange-42/arche/ecs] for Arche's API.
+// See the top level module [github.com/mlange-42/arche] for an overview.
 package examples
diff --git a/examples/locked_world/main.go b/examples/locked_world/main.go
index a70d555..9824535 100644
--- a/examples/locked_world/main.go
+++ b/examples/locked_world/main.go
@@ -1,5 +1,5 @@
 // Demonstrates how to create, remove or alter entities,
-// despite the locked World during query iteration.
+// despite the World is locked during query iteration.
 package main
 
 import (
diff --git a/examples/random_access/main.go b/examples/random_access/main.go
index 200b845..b30e94d 100644
--- a/examples/random_access/main.go
+++ b/examples/random_access/main.go
@@ -1,4 +1,4 @@
-// Demonstrates generic access to arbitrary entities.
+// Demonstrates generic access to arbitrary/random entities.
 package main
 
 import (
diff --git a/examples/world_stats/main.go b/examples/world_stats/main.go
index 34b05e9..ee89d1d 100644
--- a/examples/world_stats/main.go
+++ b/examples/world_stats/main.go
@@ -1,4 +1,4 @@
-// Demonstrates the core API that uses component IDs for access.
+// Demonstrates using world and archetype statistics.
 package main
 
 import (
@@ -27,10 +27,10 @@ type Rotation struct {
 
 func main() {
 	// Create a World.
-	world :=
+	world := ecs.NewWorld(
 		ecs.NewConfig().
-			WithCapacityIncrement(4096).
-			Build()
+			WithCapacityIncrement(4096),
+	)
 
 	ids := []ecs.ID{
 		ecs.ComponentID[Position](&world),
diff --git a/filter/filter.go b/filter/filter.go
index 2465da8..6f39bb1 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -4,11 +4,8 @@ import (
 	"github.com/mlange-42/arche/ecs"
 )
 
-// Mask is a mask for a combination of components.
-type Mask = ecs.Mask
-
 // ALL matches entities that have all the given components.
-type ALL Mask
+type ALL ecs.Mask
 
 // All matches entities that have all the given components.
 //
@@ -23,12 +20,12 @@ func (f ALL) Not() NoneOF {
 }
 
 // Matches matches a filter against a bitmask
-func (f ALL) Matches(bits ecs.BitMask) bool {
-	return bits.Contains(f.BitMask)
+func (f ALL) Matches(bits ecs.Mask) bool {
+	return bits.Contains(ecs.Mask(f))
 }
 
 // ANY matches entities that have any of the given components.
-type ANY Mask
+type ANY ecs.Mask
 
 // Any matches entities that have any of the given components.
 func Any(comps ...ecs.ID) ANY {
@@ -41,12 +38,12 @@ func (f ANY) Not() AnyNOT {
 }
 
 // Matches matches a filter against a bitmask
-func (f ANY) Matches(bits ecs.BitMask) bool {
-	return bits.ContainsAny(f.BitMask)
+func (f ANY) Matches(bits ecs.Mask) bool {
+	return bits.ContainsAny(ecs.Mask(f))
 }
 
 // NoneOF matches entities that are missing all the given components.
-type NoneOF Mask
+type NoneOF ecs.Mask
 
 // NoneOf matches entities that are missing all the given components.
 func NoneOf(comps ...ecs.ID) NoneOF {
@@ -54,12 +51,12 @@ func NoneOf(comps ...ecs.ID) NoneOF {
 }
 
 // Matches matches a filter against a bitmask
-func (f NoneOF) Matches(bits ecs.BitMask) bool {
-	return !bits.ContainsAny(f.BitMask)
+func (f NoneOF) Matches(bits ecs.Mask) bool {
+	return !bits.ContainsAny(ecs.Mask(f))
 }
 
 // AnyNOT matches entities that are missing any of the given components.
-type AnyNOT Mask
+type AnyNOT ecs.Mask
 
 // AnyNot matches entities that are missing any of the given components.
 func AnyNot(comps ...ecs.ID) AnyNOT {
@@ -67,8 +64,8 @@ func AnyNot(comps ...ecs.ID) AnyNOT {
 }
 
 // Matches matches a filter against a bitmask
-func (f AnyNOT) Matches(bits ecs.BitMask) bool {
-	return !bits.Contains(f.BitMask)
+func (f AnyNOT) Matches(bits ecs.Mask) bool {
+	return !bits.Contains(ecs.Mask(f))
 }
 
 // AND combines two filters using AND.
@@ -83,7 +80,7 @@ func And(l, r ecs.Filter) *AND {
 }
 
 // Matches matches a filter against a bitmask
-func (f *AND) Matches(bits ecs.BitMask) bool {
+func (f *AND) Matches(bits ecs.Mask) bool {
 	return f.L.Matches(bits) && f.R.Matches(bits)
 }
 
@@ -99,7 +96,7 @@ func Or(l, r ecs.Filter) *OR {
 }
 
 // Matches matches a filter against a bitmask
-func (f *OR) Matches(bits ecs.BitMask) bool {
+func (f *OR) Matches(bits ecs.Mask) bool {
 	return f.L.Matches(bits) || f.R.Matches(bits)
 }
 
@@ -115,7 +112,7 @@ func XOr(l, r ecs.Filter) *XOR {
 }
 
 // Matches matches a filter against a bitmask
-func (f *XOR) Matches(bits ecs.BitMask) bool {
+func (f *XOR) Matches(bits ecs.Mask) bool {
 	return f.L.Matches(bits) != f.R.Matches(bits)
 }
 
@@ -130,6 +127,6 @@ func Not(f ecs.Filter) *NOT {
 }
 
 // Matches matches a filter against a bitmask
-func (f *NOT) Matches(bits ecs.BitMask) bool {
+func (f *NOT) Matches(bits ecs.Mask) bool {
 	return !f.f.Matches(bits)
 }
diff --git a/generic/compiled.go b/generic/compiled.go
index f81b845..06cb52e 100644
--- a/generic/compiled.go
+++ b/generic/compiled.go
@@ -5,8 +5,7 @@ import (
 )
 
 type compiledQuery struct {
-	mask     ecs.Mask
-	exclude  ecs.Mask
+	filter   ecs.MaskFilter
 	Ids      []ecs.ID
 	compiled bool
 }
@@ -16,16 +15,11 @@ func (q *compiledQuery) Compile(w *ecs.World, include, optional, exclude []Comp)
 		return
 	}
 	q.Ids = toIds(w, include)
-	q.mask = toMaskOptional(w, q.Ids, optional)
-	q.exclude = toMask(w, exclude)
-	q.compiled = true
-}
-
-func (q *compiledQuery) Filter() ecs.MaskPair {
-	return ecs.MaskPair{
-		Mask:    q.mask,
-		Exclude: q.exclude,
+	q.filter = ecs.MaskFilter{
+		Include: toMaskOptional(w, q.Ids, optional),
+		Exclude: toMask(w, exclude),
 	}
+	q.compiled = true
 }
 
 func (q *compiledQuery) Reset() {
diff --git a/generic/generate/main.go b/generic/generate/main.go
index 0376d7a..8bb09b2 100644
--- a/generic/generate/main.go
+++ b/generic/generate/main.go
@@ -73,7 +73,7 @@ func generateMaps() {
 				returnAll += ", "
 				arguments += ", "
 			}
-			components += fmt.Sprintf("ecs.Component{ID: m.ids[%d], Component: %s},\n", j, strings.ToLower(typeLetters[j]))
+			components += fmt.Sprintf("ecs.Component{ID: m.ids[%d], Comp: %s},\n", j, strings.ToLower(typeLetters[j]))
 		}
 
 		data := query{
diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt
index 31addc3..4c55308 100644
--- a/generic/generate/map.go.txt
+++ b/generic/generate/map.go.txt
@@ -48,9 +48,9 @@ func (m *Map{{ .Index }}{{ .Types }}) Add(entity ecs.Entity) ({{ .TypesReturn }}
 
 // Assign the Map{{ .Index }}'s components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map{{ .Index }}{{ .Types }}) Assign(entity ecs.Entity, {{ .Arguments }}) ({{ .TypesReturn }}) {
-	m.world.AssignN(entity,
+	m.world.Assign(entity,
 		{{ .Components }}
 	)
 	return {{ .ReturnAll }}
diff --git a/generic/generate/query.go.txt b/generic/generate/query.go.txt
index 9d60731..1a25f63 100644
--- a/generic/generate/query.go.txt
+++ b/generic/generate/query.go.txt
@@ -49,7 +49,7 @@ func (q *Filter{{ .Index }}{{ .Types }}) Without(mask ...Comp) *Filter{{ .Index
 func (q *Filter{{ .Index }}{{ .Types }}) Query(w *ecs.World) Query{{ .Index }}{{ .Types }} {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query{{ .Index }}{{ .Types }}(query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -58,9 +58,9 @@ func (q *Filter{{ .Index }}{{ .Types }}) Query(w *ecs.World) Query{{ .Index }}{{
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter{{ .Index }}.Query].
-func (q *Filter{{ .Index }}{{ .Types }}) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter{{ .Index }}{{ .Types }}) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query{{ .Index }} is a generic query iterator for {{ .NumberStr }} components.
diff --git a/generic/map_generated.go b/generic/map_generated.go
index f94f19b..ce980f6 100644
--- a/generic/map_generated.go
+++ b/generic/map_generated.go
@@ -26,7 +26,7 @@ func (m *Map1[A]) Get(entity ecs.Entity) *A {
 	return (*A)(m.world.Get(entity, m.ids[0]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate1's components.
+// NewEntity creates a new [ecs.Entity] with the Map1's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map1[A]) NewEntity() (ecs.Entity, *A) {
@@ -39,7 +39,7 @@ func (m *Map1[A]) NewEntity() (ecs.Entity, *A) {
 // See also [ecs.World.NewEntityWith].
 func (m *Map1[A]) NewEntityWith(a *A) (ecs.Entity, *A) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
+		ecs.Component{ID: m.ids[0], Comp: a},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0]))
 }
@@ -54,10 +54,10 @@ func (m *Map1[A]) Add(entity ecs.Entity) *A {
 
 // Assign the Map1's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map1[A]) Assign(entity ecs.Entity, a *A) *A {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0]))
 }
@@ -89,7 +89,7 @@ func (m *Map2[A, B]) Get(entity ecs.Entity) (*A, *B) {
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate2's components.
+// NewEntity creates a new [ecs.Entity] with the Map2's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map2[A, B]) NewEntity() (ecs.Entity, *A, *B) {
@@ -102,8 +102,8 @@ func (m *Map2[A, B]) NewEntity() (ecs.Entity, *A, *B) {
 // See also [ecs.World.NewEntityWith].
 func (m *Map2[A, B]) NewEntityWith(a *A, b *B) (ecs.Entity, *A, *B) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
 }
@@ -118,11 +118,11 @@ func (m *Map2[A, B]) Add(entity ecs.Entity) (*A, *B) {
 
 // Assign the Map2's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map2[A, B]) Assign(entity ecs.Entity, a *A, b *B) (*A, *B) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
 }
@@ -154,7 +154,7 @@ func (m *Map3[A, B, C]) Get(entity ecs.Entity) (*A, *B, *C) {
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate3's components.
+// NewEntity creates a new [ecs.Entity] with the Map3's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map3[A, B, C]) NewEntity() (ecs.Entity, *A, *B, *C) {
@@ -167,9 +167,9 @@ func (m *Map3[A, B, C]) NewEntity() (ecs.Entity, *A, *B, *C) {
 // See also [ecs.World.NewEntityWith].
 func (m *Map3[A, B, C]) NewEntityWith(a *A, b *B, c *C) (ecs.Entity, *A, *B, *C) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2]))
 }
@@ -184,12 +184,12 @@ func (m *Map3[A, B, C]) Add(entity ecs.Entity) (*A, *B, *C) {
 
 // Assign the Map3's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map3[A, B, C]) Assign(entity ecs.Entity, a *A, b *B, c *C) (*A, *B, *C) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2]))
 }
@@ -221,7 +221,7 @@ func (m *Map4[A, B, C, D]) Get(entity ecs.Entity) (*A, *B, *C, *D) {
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate4's components.
+// NewEntity creates a new [ecs.Entity] with the Map4's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map4[A, B, C, D]) NewEntity() (ecs.Entity, *A, *B, *C, *D) {
@@ -234,10 +234,10 @@ func (m *Map4[A, B, C, D]) NewEntity() (ecs.Entity, *A, *B, *C, *D) {
 // See also [ecs.World.NewEntityWith].
 func (m *Map4[A, B, C, D]) NewEntityWith(a *A, b *B, c *C, d *D) (ecs.Entity, *A, *B, *C, *D) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3]))
 }
@@ -252,13 +252,13 @@ func (m *Map4[A, B, C, D]) Add(entity ecs.Entity) (*A, *B, *C, *D) {
 
 // Assign the Map4's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map4[A, B, C, D]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D) (*A, *B, *C, *D) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3]))
 }
@@ -290,7 +290,7 @@ func (m *Map5[A, B, C, D, E]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E) {
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate5's components.
+// NewEntity creates a new [ecs.Entity] with the Map5's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map5[A, B, C, D, E]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E) {
@@ -303,11 +303,11 @@ func (m *Map5[A, B, C, D, E]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E) {
 // See also [ecs.World.NewEntityWith].
 func (m *Map5[A, B, C, D, E]) NewEntityWith(a *A, b *B, c *C, d *D, e *E) (ecs.Entity, *A, *B, *C, *D, *E) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4]))
 }
@@ -322,14 +322,14 @@ func (m *Map5[A, B, C, D, E]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E) {
 
 // Assign the Map5's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map5[A, B, C, D, E]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E) (*A, *B, *C, *D, *E) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4]))
 }
@@ -361,7 +361,7 @@ func (m *Map6[A, B, C, D, E, F]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E, *F)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate6's components.
+// NewEntity creates a new [ecs.Entity] with the Map6's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map6[A, B, C, D, E, F]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F) {
@@ -374,12 +374,12 @@ func (m *Map6[A, B, C, D, E, F]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F
 // See also [ecs.World.NewEntityWith].
 func (m *Map6[A, B, C, D, E, F]) NewEntityWith(a *A, b *B, c *C, d *D, e *E, f *F) (ecs.Entity, *A, *B, *C, *D, *E, *F) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5]))
 }
@@ -394,15 +394,15 @@ func (m *Map6[A, B, C, D, E, F]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E, *F)
 
 // Assign the Map6's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map6[A, B, C, D, E, F]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F) (*A, *B, *C, *D, *E, *F) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5]))
 }
@@ -434,7 +434,7 @@ func (m *Map7[A, B, C, D, E, F, G]) Get(entity ecs.Entity) (*A, *B, *C, *D, *E,
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate7's components.
+// NewEntity creates a new [ecs.Entity] with the Map7's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map7[A, B, C, D, E, F, G]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F, *G) {
@@ -447,13 +447,13 @@ func (m *Map7[A, B, C, D, E, F, G]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E,
 // See also [ecs.World.NewEntityWith].
 func (m *Map7[A, B, C, D, E, F, G]) NewEntityWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G) (ecs.Entity, *A, *B, *C, *D, *E, *F, *G) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
-		ecs.Component{ID: m.ids[6], Component: g},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
+		ecs.Component{ID: m.ids[6], Comp: g},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6]))
 }
@@ -468,16 +468,16 @@ func (m *Map7[A, B, C, D, E, F, G]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E,
 
 // Assign the Map7's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map7[A, B, C, D, E, F, G]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G) (*A, *B, *C, *D, *E, *F, *G) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
-		ecs.Component{ID: m.ids[6], Component: g},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
+		ecs.Component{ID: m.ids[6], Comp: g},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6]))
 }
@@ -509,7 +509,7 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Get(entity ecs.Entity) (*A, *B, *C, *D, *
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6])), (*H)(m.world.Get(entity, m.ids[7]))
 }
 
-// NewEntity creates a new [ecs.Entity] with the Mutate8's components.
+// NewEntity creates a new [ecs.Entity] with the Map8's components.
 //
 // See also [ecs.World.NewEntity].
 func (m *Map8[A, B, C, D, E, F, G, H]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F, *G, *H) {
@@ -522,14 +522,14 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewEntity() (ecs.Entity, *A, *B, *C, *D,
 // See also [ecs.World.NewEntityWith].
 func (m *Map8[A, B, C, D, E, F, G, H]) NewEntityWith(a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H) (ecs.Entity, *A, *B, *C, *D, *E, *F, *G, *H) {
 	entity := m.world.NewEntityWith(
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
-		ecs.Component{ID: m.ids[6], Component: g},
-		ecs.Component{ID: m.ids[7], Component: h},
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
+		ecs.Component{ID: m.ids[6], Comp: g},
+		ecs.Component{ID: m.ids[7], Comp: h},
 	)
 	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6])), (*H)(m.world.Get(entity, m.ids[7]))
 }
@@ -544,17 +544,17 @@ func (m *Map8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity) (*A, *B, *C, *D, *
 
 // Assign the Map8's components to the given entity, using the supplied values.
 //
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
+// See also [ecs.World.Assign].
 func (m *Map8[A, B, C, D, E, F, G, H]) Assign(entity ecs.Entity, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H) (*A, *B, *C, *D, *E, *F, *G, *H) {
-	m.world.AssignN(entity,
-		ecs.Component{ID: m.ids[0], Component: a},
-		ecs.Component{ID: m.ids[1], Component: b},
-		ecs.Component{ID: m.ids[2], Component: c},
-		ecs.Component{ID: m.ids[3], Component: d},
-		ecs.Component{ID: m.ids[4], Component: e},
-		ecs.Component{ID: m.ids[5], Component: f},
-		ecs.Component{ID: m.ids[6], Component: g},
-		ecs.Component{ID: m.ids[7], Component: h},
+	m.world.Assign(entity,
+		ecs.Component{ID: m.ids[0], Comp: a},
+		ecs.Component{ID: m.ids[1], Comp: b},
+		ecs.Component{ID: m.ids[2], Comp: c},
+		ecs.Component{ID: m.ids[3], Comp: d},
+		ecs.Component{ID: m.ids[4], Comp: e},
+		ecs.Component{ID: m.ids[5], Comp: f},
+		ecs.Component{ID: m.ids[6], Comp: g},
+		ecs.Component{ID: m.ids[7], Comp: h},
 	)
 	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1])), (*C)(m.world.Get(entity, m.ids[2])), (*D)(m.world.Get(entity, m.ids[3])), (*E)(m.world.Get(entity, m.ids[4])), (*F)(m.world.Get(entity, m.ids[5])), (*G)(m.world.Get(entity, m.ids[6])), (*H)(m.world.Get(entity, m.ids[7]))
 }
diff --git a/generic/query_generated.go b/generic/query_generated.go
index b6c73b7..a4402d8 100644
--- a/generic/query_generated.go
+++ b/generic/query_generated.go
@@ -43,7 +43,7 @@ func (q *Filter0) Without(mask ...Comp) *Filter0 {
 func (q *Filter0) Query(w *ecs.World) Query0 {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query0(query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -52,9 +52,9 @@ func (q *Filter0) Query(w *ecs.World) Query0 {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter0.Query].
-func (q *Filter0) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter0) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query0 is a generic query iterator for zero components.
@@ -110,7 +110,7 @@ func (q *Filter1[A]) Without(mask ...Comp) *Filter1[A] {
 func (q *Filter1[A]) Query(w *ecs.World) Query1[A] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query1[A](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -119,9 +119,9 @@ func (q *Filter1[A]) Query(w *ecs.World) Query1[A] {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter1.Query].
-func (q *Filter1[A]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter1[A]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query1 is a generic query iterator for one components.
@@ -129,7 +129,9 @@ func (q *Filter1[A]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter1] and [Filter1.Query]
 type Query1[A any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query1.Entity] to get the current Entity.
 func (q *Query1[A]) Get() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
@@ -182,7 +184,7 @@ func (q *Filter2[A, B]) Without(mask ...Comp) *Filter2[A, B] {
 func (q *Filter2[A, B]) Query(w *ecs.World) Query2[A, B] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query2[A, B](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -191,9 +193,9 @@ func (q *Filter2[A, B]) Query(w *ecs.World) Query2[A, B] {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter2.Query].
-func (q *Filter2[A, B]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter2[A, B]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query2 is a generic query iterator for two components.
@@ -201,7 +203,9 @@ func (q *Filter2[A, B]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter2] and [Filter2.Query]
 type Query2[A any, B any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query2.Entity] to get the current Entity.
 func (q *Query2[A, B]) Get() (*A, *B) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1]))
 }
@@ -254,7 +258,7 @@ func (q *Filter3[A, B, C]) Without(mask ...Comp) *Filter3[A, B, C] {
 func (q *Filter3[A, B, C]) Query(w *ecs.World) Query3[A, B, C] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query3[A, B, C](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -263,9 +267,9 @@ func (q *Filter3[A, B, C]) Query(w *ecs.World) Query3[A, B, C] {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter3.Query].
-func (q *Filter3[A, B, C]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter3[A, B, C]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query3 is a generic query iterator for three components.
@@ -273,7 +277,9 @@ func (q *Filter3[A, B, C]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter3] and [Filter3.Query]
 type Query3[A any, B any, C any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query3.Entity] to get the current Entity.
 func (q *Query3[A, B, C]) Get() (*A, *B, *C) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2]))
 }
@@ -326,7 +332,7 @@ func (q *Filter4[A, B, C, D]) Without(mask ...Comp) *Filter4[A, B, C, D] {
 func (q *Filter4[A, B, C, D]) Query(w *ecs.World) Query4[A, B, C, D] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query4[A, B, C, D](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -335,9 +341,9 @@ func (q *Filter4[A, B, C, D]) Query(w *ecs.World) Query4[A, B, C, D] {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter4.Query].
-func (q *Filter4[A, B, C, D]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter4[A, B, C, D]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query4 is a generic query iterator for four components.
@@ -345,7 +351,9 @@ func (q *Filter4[A, B, C, D]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter4] and [Filter4.Query]
 type Query4[A any, B any, C any, D any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query4.Entity] to get the current Entity.
 func (q *Query4[A, B, C, D]) Get() (*A, *B, *C, *D) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2])), (*D)(q.Query.Get(q.ids[3]))
 }
@@ -398,7 +406,7 @@ func (q *Filter5[A, B, C, D, E]) Without(mask ...Comp) *Filter5[A, B, C, D, E] {
 func (q *Filter5[A, B, C, D, E]) Query(w *ecs.World) Query5[A, B, C, D, E] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query5[A, B, C, D, E](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -407,9 +415,9 @@ func (q *Filter5[A, B, C, D, E]) Query(w *ecs.World) Query5[A, B, C, D, E] {
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter5.Query].
-func (q *Filter5[A, B, C, D, E]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter5[A, B, C, D, E]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query5 is a generic query iterator for five components.
@@ -417,7 +425,9 @@ func (q *Filter5[A, B, C, D, E]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter5] and [Filter5.Query]
 type Query5[A any, B any, C any, D any, E any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query5.Entity] to get the current Entity.
 func (q *Query5[A, B, C, D, E]) Get() (*A, *B, *C, *D, *E) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2])), (*D)(q.Query.Get(q.ids[3])), (*E)(q.Query.Get(q.ids[4]))
 }
@@ -470,7 +480,7 @@ func (q *Filter6[A, B, C, D, E, F]) Without(mask ...Comp) *Filter6[A, B, C, D, E
 func (q *Filter6[A, B, C, D, E, F]) Query(w *ecs.World) Query6[A, B, C, D, E, F] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query6[A, B, C, D, E, F](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -479,9 +489,9 @@ func (q *Filter6[A, B, C, D, E, F]) Query(w *ecs.World) Query6[A, B, C, D, E, F]
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter6.Query].
-func (q *Filter6[A, B, C, D, E, F]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter6[A, B, C, D, E, F]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query6 is a generic query iterator for six components.
@@ -489,7 +499,9 @@ func (q *Filter6[A, B, C, D, E, F]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter6] and [Filter6.Query]
 type Query6[A any, B any, C any, D any, E any, F any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query6.Entity] to get the current Entity.
 func (q *Query6[A, B, C, D, E, F]) Get() (*A, *B, *C, *D, *E, *F) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2])), (*D)(q.Query.Get(q.ids[3])), (*E)(q.Query.Get(q.ids[4])), (*F)(q.Query.Get(q.ids[5]))
 }
@@ -542,7 +554,7 @@ func (q *Filter7[A, B, C, D, E, F, G]) Without(mask ...Comp) *Filter7[A, B, C, D
 func (q *Filter7[A, B, C, D, E, F, G]) Query(w *ecs.World) Query7[A, B, C, D, E, F, G] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query7[A, B, C, D, E, F, G](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -551,9 +563,9 @@ func (q *Filter7[A, B, C, D, E, F, G]) Query(w *ecs.World) Query7[A, B, C, D, E,
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter7.Query].
-func (q *Filter7[A, B, C, D, E, F, G]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter7[A, B, C, D, E, F, G]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query7 is a generic query iterator for seven components.
@@ -561,7 +573,9 @@ func (q *Filter7[A, B, C, D, E, F, G]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter7] and [Filter7.Query]
 type Query7[A any, B any, C any, D any, E any, F any, G any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query7.Entity] to get the current Entity.
 func (q *Query7[A, B, C, D, E, F, G]) Get() (*A, *B, *C, *D, *E, *F, *G) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2])), (*D)(q.Query.Get(q.ids[3])), (*E)(q.Query.Get(q.ids[4])), (*F)(q.Query.Get(q.ids[5])), (*G)(q.Query.Get(q.ids[6]))
 }
@@ -614,7 +628,7 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) Without(mask ...Comp) *Filter8[A, B, C
 func (q *Filter8[A, B, C, D, E, F, G, H]) Query(w *ecs.World) Query8[A, B, C, D, E, F, G, H] {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
 	return Query8[A, B, C, D, E, F, G, H](query{
-		w.Query(q.compiled.Filter()),
+		w.Query(&q.compiled.filter),
 		q.compiled.Ids,
 	})
 }
@@ -623,9 +637,9 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) Query(w *ecs.World) Query8[A, B, C, D,
 //
 // Can be passed to [ecs.World.Query].
 // For the intended generic use, however, generate a generic query with [Filter8.Query].
-func (q *Filter8[A, B, C, D, E, F, G, H]) Filter(w *ecs.World) ecs.MaskPair {
+func (q *Filter8[A, B, C, D, E, F, G, H]) Filter(w *ecs.World) ecs.MaskFilter {
 	q.compiled.Compile(w, q.include, q.optional, q.exclude)
-	return q.compiled.Filter()
+	return q.compiled.filter
 }
 
 // Query8 is a generic query iterator for eight components.
@@ -633,7 +647,9 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) Filter(w *ecs.World) ecs.MaskPair {
 // Create it with [NewFilter8] and [Filter8.Query]
 type Query8[A any, B any, C any, D any, E any, F any, G any, H any] query
 
-// Get returns the [ecs.Entity] and all queried components for the current query iterator position.
+// Get returns all queried components for the current query iterator position.
+//
+// Use [Query8.Entity] to get the current Entity.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get() (*A, *B, *C, *D, *E, *F, *G, *H) {
 	return (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2])), (*D)(q.Query.Get(q.ids[3])), (*E)(q.Query.Get(q.ids[4])), (*F)(q.Query.Get(q.ids[5])), (*G)(q.Query.Get(q.ids[6])), (*H)(q.Query.Get(q.ids[7]))
 }
diff --git a/generic/util.go b/generic/util.go
index 6227c9c..2832135 100644
--- a/generic/util.go
+++ b/generic/util.go
@@ -36,17 +36,17 @@ func toIds(w *ecs.World, types []Comp) []ecs.ID {
 }
 
 func toMask(w *ecs.World, types []Comp) ecs.Mask {
-	mask := ecs.BitMask{}
+	mask := ecs.Mask{}
 	for _, t := range types {
 		mask.Set(ecs.TypeID(w, t), true)
 	}
-	return ecs.Mask{BitMask: mask}
+	return mask
 }
 
 func toMaskOptional(w *ecs.World, include []ecs.ID, optional []Comp) ecs.Mask {
-	mask := ecs.NewBitMask(include...)
+	mask := ecs.All(include...)
 	for _, t := range optional {
 		mask.Set(ecs.TypeID(w, t), false)
 	}
-	return ecs.Mask{BitMask: mask}
+	return mask
 }


Test patch:
diff --git a/benchmark/iterate/arche_test.go b/benchmark/iterate/arche_test.go
index 86e5bf2..c881ad0 100644
--- a/benchmark/iterate/arche_test.go
+++ b/benchmark/iterate/arche_test.go
@@ -54,7 +54,9 @@ func runArcheGet(b *testing.B, count int) {
 		}
 		b.StopTimer()
 		query.Close()
-		break
+		if true {
+			break
+		}
 	}
 }
 
@@ -209,8 +211,8 @@ func runArcheQuery1kArch(b *testing.B, count int) {
 		query := world.Query(ecs.All(6))
 		b.StartTimer()
 		for query.Next() {
-			pos := (*c.Position)(query.Get(6))
-			pos.X = 1
+			pos := (*c.TestStruct6)(query.Get(6))
+			pos.Val = 1
 		}
 	}
 }
@@ -243,8 +245,8 @@ func runArcheFilter1kArch(b *testing.B, count int) {
 		query := world.Query(filter.All(6))
 		b.StartTimer()
 		for query.Next() {
-			pos := (*c.Position)(query.Get(6))
-			pos.X = 1
+			pos := (*c.TestStruct6)(query.Get(6))
+			pos.Val = 1
 		}
 	}
 }
diff --git a/benchmark/iterate/ggecs_test.go b/benchmark/iterate/ggecs_test.go
index 07c600f..02705cb 100644
--- a/benchmark/iterate/ggecs_test.go
+++ b/benchmark/iterate/ggecs_test.go
@@ -102,8 +102,8 @@ func runGameEngineEcs1kArch(b *testing.B, count int) {
 
 	for i := 0; i < b.N; i++ {
 		for _, e := range filter.Entities() {
-			pos := (*c.Position)(world.Component(e, 6))
-			pos.X = 1
+			pos := (*c.TestStruct6)(world.Component(e, 6))
+			pos.Val = 1
 		}
 	}
 }
diff --git a/benchmark/move/arche_test.go b/benchmark/move/arche_test.go
index a1f2d8e..f83fe4b 100644
--- a/benchmark/move/arche_test.go
+++ b/benchmark/move/arche_test.go
@@ -11,7 +11,9 @@ import (
 func runArcheMove(b *testing.B, count int, add, rem []g.Comp) {
 	for i := 0; i < b.N; i++ {
 		b.StopTimer()
-		world := ecs.NewConfig().WithCapacityIncrement(1024).Build()
+		world := ecs.NewWorld(
+			ecs.NewConfig().WithCapacityIncrement(1024),
+		)
 		c.RegisterAll(&world)
 
 		addIDs := make([]ecs.ID, len(add))
diff --git a/ecs/archetype_test.go b/ecs/archetype_test.go
index a0da114..c3605ca 100644
--- a/ecs/archetype_test.go
+++ b/ecs/archetype_test.go
@@ -18,14 +18,14 @@ func TestArchetype(t *testing.T) {
 
 	arch.Add(
 		newEntity(0),
-		Component{ID: 0, Component: &position{1, 2}},
-		Component{ID: 1, Component: &rotation{3}},
+		Component{ID: 0, Comp: &position{1, 2}},
+		Component{ID: 1, Comp: &rotation{3}},
 	)
 
 	arch.Add(
 		newEntity(1),
-		Component{ID: 0, Component: &position{4, 5}},
-		Component{ID: 1, Component: &rotation{6}},
+		Component{ID: 0, Comp: &position{4, 5}},
+		Component{ID: 1, Comp: &rotation{6}},
 	)
 
 	assert.Equal(t, 2, int(arch.entities.Len()))
@@ -63,7 +63,7 @@ func TestArchetype(t *testing.T) {
 	assert.Panics(t, func() {
 		arch.Add(
 			newEntity(1),
-			Component{ID: 0, Component: &position{4, 5}},
+			Component{ID: 0, Comp: &position{4, 5}},
 		)
 	})
 }
diff --git a/ecs/bitmask_test.go b/ecs/bitmask_test.go
index 3a0e256..134eeb0 100644
--- a/ecs/bitmask_test.go
+++ b/ecs/bitmask_test.go
@@ -8,7 +8,7 @@ import (
 )
 
 func TestBitMask(t *testing.T) {
-	mask := NewBitMask(ID(1), ID(2), ID(13), ID(27))
+	mask := All(ID(1), ID(2), ID(13), ID(27))
 
 	assert.Equal(t, 4, mask.TotalBitsSet())
 
@@ -26,8 +26,8 @@ func TestBitMask(t *testing.T) {
 	assert.True(t, mask.Get(0))
 	assert.False(t, mask.Get(1))
 
-	other1 := NewBitMask(ID(1), ID(2), ID(32))
-	other2 := NewBitMask(ID(0), ID(2))
+	other1 := All(ID(1), ID(2), ID(32))
+	other2 := All(ID(0), ID(2))
 
 	assert.False(t, mask.Contains(other1))
 	assert.True(t, mask.Contains(other2))
@@ -35,9 +35,9 @@ func TestBitMask(t *testing.T) {
 	mask.Reset()
 	assert.Equal(t, 0, mask.TotalBitsSet())
 
-	mask = NewBitMask(ID(1), ID(2), ID(13), ID(27))
-	other1 = NewBitMask(ID(1), ID(32))
-	other2 = NewBitMask(ID(0), ID(32))
+	mask = All(ID(1), ID(2), ID(13), ID(27))
+	other1 = All(ID(1), ID(32))
+	other2 = All(ID(0), ID(32))
 
 	assert.True(t, mask.ContainsAny(other1))
 	assert.False(t, mask.ContainsAny(other2))
@@ -45,11 +45,11 @@ func TestBitMask(t *testing.T) {
 
 func TestBitMask128(t *testing.T) {
 	for i := 0; i < MaskTotalBits; i++ {
-		mask := NewBitMask(ID(i))
+		mask := All(ID(i))
 		assert.Equal(t, 1, mask.TotalBitsSet())
 		assert.True(t, mask.Get(ID(i)))
 	}
-	mask := BitMask{}
+	mask := Mask{}
 	assert.Equal(t, 0, mask.TotalBitsSet())
 
 	for i := 0; i < MaskTotalBits; i++ {
@@ -58,13 +58,36 @@ func TestBitMask128(t *testing.T) {
 		assert.True(t, mask.Get(ID(i)))
 	}
 
-	mask = NewBitMask(ID(1), ID(2), ID(13), ID(27), ID(63), ID(64), ID(65))
+	mask = All(ID(1), ID(2), ID(13), ID(27), ID(63), ID(64), ID(65))
 
-	assert.True(t, mask.Contains(NewBitMask(ID(1), ID(2), ID(63), ID(64))))
-	assert.False(t, mask.Contains(NewBitMask(ID(1), ID(2), ID(63), ID(90))))
+	assert.True(t, mask.Contains(All(ID(1), ID(2), ID(63), ID(64))))
+	assert.False(t, mask.Contains(All(ID(1), ID(2), ID(63), ID(90))))
 
-	assert.True(t, mask.ContainsAny(NewBitMask(ID(6), ID(65), ID(111))))
-	assert.False(t, mask.ContainsAny(NewBitMask(ID(6), ID(66), ID(90))))
+	assert.True(t, mask.ContainsAny(All(ID(6), ID(65), ID(111))))
+	assert.False(t, mask.ContainsAny(All(ID(6), ID(66), ID(90))))
+}
+
+// bitMask64 is there just for performance comparison with the new 128 bit Mask.
+type bitMask64 uint64
+
+func newBitMask64(ids ...ID) bitMask64 {
+	var mask bitMask64
+	for _, id := range ids {
+		mask.Set(id, true)
+	}
+	return mask
+}
+func (e bitMask64) Get(bit ID) bool {
+	mask := bitMask64(1 << bit)
+	return e&mask == mask
+}
+
+func (e *bitMask64) Set(bit ID, value bool) {
+	if value {
+		*e |= bitMask64(1 << bit)
+	} else {
+		*e &= bitMask64(^(1 << bit))
+	}
 }
 
 func TestBitMask64(t *testing.T) {
@@ -89,15 +112,18 @@ func BenchmarkBitmask64Get(b *testing.B) {
 	idx := ID(rand.Intn(wordSize))
 	b.StartTimer()
 
+	var v bool
 	for i := 0; i < b.N; i++ {
-		v := mask.Get(idx)
-		_ = v
+		v = mask.Get(idx)
 	}
+	b.StopTimer()
+	v = !v
+	_ = v
 }
 
 func BenchmarkBitmask128Get(b *testing.B) {
 	b.StopTimer()
-	mask := NewBitMask()
+	mask := All()
 	for i := 0; i < MaskTotalBits; i++ {
 		if rand.Float64() < 0.5 {
 			mask.Set(ID(i), true)
@@ -106,8 +132,86 @@ func BenchmarkBitmask128Get(b *testing.B) {
 	idx := ID(rand.Intn(MaskTotalBits))
 	b.StartTimer()
 
+	var v bool
+	for i := 0; i < b.N; i++ {
+		v = mask.Get(idx)
+	}
+
+	b.StopTimer()
+	v = !v
+	_ = v
+}
+
+func BenchmarkMaskFilter(b *testing.B) {
+	b.StopTimer()
+	mask := All(0, 1, 2).Without()
+	bits := All(0, 1, 2)
+	b.StartTimer()
+	var v bool
+	for i := 0; i < b.N; i++ {
+		v = mask.Matches(bits)
+	}
+	b.StopTimer()
+	v = !v
+	_ = v
+}
+
+type maskFilterPointer struct {
+	Mask    Mask
+	Exclude Mask
+}
+
+// Matches matches a filter against a mask.
+func (f maskFilterPointer) Matches(bits Mask) bool {
+	return bits.Contains(f.Mask) &&
+		(f.Exclude.IsZero() || !bits.ContainsAny(f.Exclude))
+}
+
+func BenchmarkMaskFilterNoPointer(b *testing.B) {
+	b.StopTimer()
+	mask := maskFilterPointer{All(0, 1, 2), All()}
+	bits := All(0, 1, 2)
+	b.StartTimer()
+	var v bool
+	for i := 0; i < b.N; i++ {
+		v = mask.Matches(bits)
+	}
+	b.StopTimer()
+	v = !v
+	_ = v
+}
+
+func BenchmarkMask(b *testing.B) {
+	b.StopTimer()
+	mask := All(0, 1, 2)
+	bits := All(0, 1, 2)
+	b.StartTimer()
+	var v bool
 	for i := 0; i < b.N; i++ {
-		v := mask.Get(idx)
-		_ = v
+		v = mask.Matches(bits)
 	}
+	b.StopTimer()
+	v = !v
+	_ = v
+}
+
+type maskPointer Mask
+
+// Matches matches a filter against a mask.
+func (f *maskPointer) Matches(bits Mask) bool {
+	return bits.Contains(Mask(*f))
+}
+
+func BenchmarkMaskPointer(b *testing.B) {
+	b.StopTimer()
+	mask := maskPointer(All(0, 1, 2))
+	bits := All(0, 1, 2)
+	b.StartTimer()
+	var v bool
+	for i := 0; i < b.N; i++ {
+		v = mask.Matches(bits)
+	}
+	b.StopTimer()
+	v = !v
+	_ = v
 }
diff --git a/ecs/config_test.go b/ecs/config_test.go
index d23eac9..c8d1e70 100644
--- a/ecs/config_test.go
+++ b/ecs/config_test.go
@@ -10,5 +10,5 @@ func TestConfig(t *testing.T) {
 	c := NewConfig()
 	c = c.WithCapacityIncrement(16)
 	assert.Equal(t, 16, c.CapacityIncrement)
-	_ = c.Build()
+	_ = NewWorld(c)
 }
diff --git a/ecs/event_test.go b/ecs/event_test.go
index eb81559..a4d9766 100644
--- a/ecs/event_test.go
+++ b/ecs/event_test.go
@@ -6,18 +6,18 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestChangeEvent(t *testing.T) {
-	e := ChangeEvent{AddedRemoved: 0}
+func TestEntityEvent(t *testing.T) {
+	e := EntityEvent{AddedRemoved: 0}
 
 	assert.False(t, e.EntityAdded())
 	assert.False(t, e.EntityRemoved())
 
-	e = ChangeEvent{AddedRemoved: 1}
+	e = EntityEvent{AddedRemoved: 1}
 
 	assert.True(t, e.EntityAdded())
 	assert.False(t, e.EntityRemoved())
 
-	e = ChangeEvent{AddedRemoved: -1}
+	e = EntityEvent{AddedRemoved: -1}
 
 	assert.False(t, e.EntityAdded())
 	assert.True(t, e.EntityRemoved())
diff --git a/ecs/query_test.go b/ecs/query_test.go
index 5a60b01..aeadef9 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -8,11 +8,11 @@ import (
 
 func TestMask(t *testing.T) {
 	filter := All(0, 2, 4)
-	other := NewBitMask(0, 1, 2)
+	other := All(0, 1, 2)
 
 	assert.False(t, filter.Matches(other))
 
-	other = NewBitMask(0, 1, 2, 3, 4)
+	other = All(0, 1, 2, 3, 4)
 	assert.True(t, filter.Matches(other))
 }
 
@@ -42,7 +42,6 @@ func TestQuery(t *testing.T) {
 		pos := (*position)(q.Get(posID))
 		rot := (*rotation)(q.Get(rotID))
 		assert.Equal(t, w.Mask(ent), q.Mask())
-		assert.Equal(t, w.IDs(ent), q.IDs())
 		_ = ent
 		_ = pos
 		_ = rot
@@ -76,7 +75,8 @@ func TestQuery(t *testing.T) {
 
 	assert.Panics(t, func() { q.Next() })
 
-	q = w.Query(All(rotID).Without(posID))
+	filter := All(rotID).Without(posID)
+	q = w.Query(&filter)
 
 	cnt = 0
 	for q.Next() {
@@ -85,7 +85,8 @@ func TestQuery(t *testing.T) {
 	}
 	assert.Equal(t, 2, cnt)
 
-	q = w.Query(All(rotID).Without(posID, velID))
+	filter = All(rotID).Without(posID, velID)
+	q = w.Query(&filter)
 
 	cnt = 0
 	for q.Next() {
diff --git a/ecs/world_test.go b/ecs/world_test.go
index 641898e..1b5b497 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -8,6 +8,13 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
+func TestWorldConfig(t *testing.T) {
+	_ = NewWorld(NewConfig())
+
+	assert.Panics(t, func() { _ = NewWorld(Config{}) })
+	assert.Panics(t, func() { _ = NewWorld(Config{}, Config{}) })
+}
+
 func TestWorldEntites(t *testing.T) {
 	w := NewWorld()
 
@@ -41,7 +48,7 @@ func TestWorldEntites(t *testing.T) {
 }
 
 func TestWorldNewEntites(t *testing.T) {
-	w := NewConfig().WithCapacityIncrement(32).Build()
+	w := NewWorld(NewConfig().WithCapacityIncrement(32))
 
 	posID := ComponentID[position](&w)
 	velID := ComponentID[velocity](&w)
@@ -56,10 +63,10 @@ func TestWorldNewEntites(t *testing.T) {
 	)
 	e3 := w.NewEntityWith()
 
-	assert.Equal(t, NewBitMask(), w.Mask(e0))
-	assert.Equal(t, NewBitMask(posID, velID, rotID), w.Mask(e1))
-	assert.Equal(t, NewBitMask(posID, velID, rotID), w.Mask(e2))
-	assert.Equal(t, NewBitMask(), w.Mask(e3))
+	assert.Equal(t, All(), w.Mask(e0))
+	assert.Equal(t, All(posID, velID, rotID), w.Mask(e1))
+	assert.Equal(t, All(posID, velID, rotID), w.Mask(e2))
+	assert.Equal(t, All(), w.Mask(e3))
 
 	pos := (*position)(w.Get(e2, posID))
 	vel := (*velocity)(w.Get(e2, velID))
@@ -116,15 +123,15 @@ func TestWorldComponents(t *testing.T) {
 	w.Add(e2, posID, rotID)
 	assert.Equal(t, 3, w.archetypes.Len())
 
-	assert.Equal(t, All(posID).BitMask, w.Mask(e0))
-	assert.Equal(t, All(posID, rotID).BitMask, w.Mask(e1))
+	assert.Equal(t, All(posID), w.Mask(e0))
+	assert.Equal(t, All(posID, rotID), w.Mask(e1))
 
 	w.Remove(e2, posID)
 
-	maskNone := NewBitMask()
-	maskPos := NewBitMask(posID)
-	maskRot := NewBitMask(rotID)
-	maskPosRot := NewBitMask(posID, rotID)
+	maskNone := All()
+	maskPos := All(posID)
+	maskRot := All(rotID)
+	maskPosRot := All(posID, rotID)
 
 	archNone, ok := w.findArchetype(maskNone)
 	assert.True(t, ok)
@@ -239,18 +246,21 @@ func TestWorldAssignSet(t *testing.T) {
 	e0 := w.NewEntity()
 	e1 := w.NewEntity()
 
-	pos := (*position)(w.Assign(e0, posID, &position{2, 3}))
+	assert.Panics(t, func() { w.Assign(e0) })
+
+	w.Assign(e0, Component{posID, &position{2, 3}})
+	pos := (*position)(w.Get(e0, posID))
 	assert.Equal(t, 2, pos.X)
 	pos.X = 5
 
 	pos = (*position)(w.Get(e0, posID))
 	assert.Equal(t, 5, pos.X)
 
-	assert.Panics(t, func() { _ = (*position)(w.Assign(e0, posID, &position{2, 3})) })
+	assert.Panics(t, func() { w.Assign(e0, Component{posID, &position{2, 3}}) })
 	assert.Panics(t, func() { _ = (*position)(w.copyTo(e1, posID, &position{2, 3})) })
 
 	e2 := w.NewEntity()
-	w.AssignN(e2,
+	w.Assign(e2,
 		Component{posID, &position{4, 5}},
 		Component{velID, &velocity{1, 2}},
 		Component{rotID, &rotation{3}},
@@ -269,6 +279,10 @@ func TestWorldAssignSet(t *testing.T) {
 	_ = (*position)(w.Set(e2, posID, &position{7, 8}))
 	pos = (*position)(w.Get(e2, posID))
 	assert.Equal(t, 7, pos.X)
+
+	*pos = position{8, 9}
+	pos = (*position)(w.Get(e2, posID))
+	assert.Equal(t, 8, pos.X)
 }
 func TestWorldGetComponents(t *testing.T) {
 	w := NewWorld()
@@ -421,15 +435,14 @@ func TestArchetypeGraph(t *testing.T) {
 }
 
 func TestWorldListener(t *testing.T) {
-	events := []ChangeEvent{}
-	listen := func(e ChangeEvent) {
+	events := []EntityEvent{}
+	listen := func(e EntityEvent) {
 		events = append(events, e)
 	}
 
 	w := NewWorld()
 
-	w.RegisterListener(listen)
-	assert.Panics(t, func() { w.RegisterListener(listen) })
+	w.SetListener(listen)
 
 	posID := ComponentID[position](&w)
 	velID := ComponentID[velocity](&w)
@@ -437,21 +450,21 @@ func TestWorldListener(t *testing.T) {
 
 	e0 := w.NewEntity()
 	assert.Equal(t, 1, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity: e0, AddedRemoved: 1,
 	}, events[len(events)-1])
 
 	w.RemoveEntity(e0)
 	assert.Equal(t, 2, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity: e0, AddedRemoved: -1,
 	}, events[len(events)-1])
 
 	e0 = w.NewEntity(posID, velID)
 	assert.Equal(t, 3, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity:       e0,
-		NewMask:      NewBitMask(posID, velID),
+		NewMask:      All(posID, velID),
 		Added:        []ID{posID, velID},
 		Current:      []ID{posID, velID},
 		AddedRemoved: 1,
@@ -459,19 +472,19 @@ func TestWorldListener(t *testing.T) {
 
 	w.RemoveEntity(e0)
 	assert.Equal(t, 4, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity:       e0,
-		OldMask:      NewBitMask(posID, velID),
-		NewMask:      NewBitMask(posID, velID),
+		OldMask:      All(posID, velID),
+		NewMask:      All(posID, velID),
 		Current:      []ID{posID, velID},
 		AddedRemoved: -1,
 	}, events[len(events)-1])
 
 	e0 = w.NewEntityWith(Component{posID, &position{}}, Component{velID, &velocity{}})
 	assert.Equal(t, 5, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity:       e0,
-		NewMask:      NewBitMask(posID, velID),
+		NewMask:      All(posID, velID),
 		Added:        []ID{posID, velID},
 		Current:      []ID{posID, velID},
 		AddedRemoved: 1,
@@ -479,10 +492,10 @@ func TestWorldListener(t *testing.T) {
 
 	w.Add(e0, rotID)
 	assert.Equal(t, 6, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity:       e0,
-		OldMask:      NewBitMask(posID, velID),
-		NewMask:      NewBitMask(posID, velID, rotID),
+		OldMask:      All(posID, velID),
+		NewMask:      All(posID, velID, rotID),
 		Added:        []ID{rotID},
 		Current:      []ID{posID, velID, rotID},
 		AddedRemoved: 0,
@@ -490,10 +503,10 @@ func TestWorldListener(t *testing.T) {
 
 	w.Remove(e0, posID)
 	assert.Equal(t, 7, len(events))
-	assert.Equal(t, ChangeEvent{
+	assert.Equal(t, EntityEvent{
 		Entity:       e0,
-		OldMask:      NewBitMask(posID, velID, rotID),
-		NewMask:      NewBitMask(velID, rotID),
+		OldMask:      All(posID, velID, rotID),
+		NewMask:      All(velID, rotID),
 		Removed:      []ID{posID},
 		Current:      []ID{velID, rotID},
 		AddedRemoved: 0,
@@ -528,7 +541,7 @@ func Test1000Archetypes(t *testing.T) {
 	ids[9] = ComponentID[testStruct9](&w)
 
 	for i := 0; i < 1024; i++ {
-		mask := BitMask{uint64(i), 0}
+		mask := Mask{uint64(i), 0}
 		add := make([]ID, 0, 10)
 		for j := 0; j < 10; j++ {
 			id := ID(j)
@@ -552,7 +565,7 @@ func Test1000Archetypes(t *testing.T) {
 
 func TestTypeSizes(t *testing.T) {
 	printTypeSize[Entity]()
-	printTypeSize[BitMask]()
+	printTypeSize[Mask]()
 	printTypeSize[World]()
 	printTypeSizeName[pagedArr32[archetype]]("PagedArr32")
 	printTypeSize[archetype]()
diff --git a/filter/filter_test.go b/filter/filter_test.go
index b00a87f..60bb177 100644
--- a/filter/filter_test.go
+++ b/filter/filter_test.go
@@ -185,8 +185,8 @@ func TestFilter(t *testing.T) {
 	assert.Equal(t, 2, cnt)
 }
 
-func match(f ecs.Filter, m Mask) bool {
-	return f.Matches(m.BitMask)
+func match(f ecs.Filter, m ecs.Mask) bool {
+	return f.Matches(m)
 }
 
 func TestInterface(t *testing.T) {
@@ -207,7 +207,7 @@ func BenchmarkFilterStackOr(b *testing.B) {
 	b.StartTimer()
 
 	for i := 0; i < b.N; i++ {
-		_ = filter.Matches(mask.BitMask)
+		_ = filter.Matches(ecs.Mask(mask))
 	}
 }
 
@@ -222,7 +222,7 @@ func BenchmarkFilterStack5And(b *testing.B) {
 	b.StartTimer()
 
 	for i := 0; i < b.N; i++ {
-		_ = filter.Matches(mask.BitMask)
+		_ = filter.Matches(ecs.Mask(mask))
 	}
 }
 
@@ -234,7 +234,7 @@ func BenchmarkFilterHeapOr(b *testing.B) {
 	b.StartTimer()
 
 	for i := 0; i < b.N; i++ {
-		_ = filter.Matches(mask.BitMask)
+		_ = filter.Matches(ecs.Mask(mask))
 	}
 }
 
@@ -246,6 +246,6 @@ func BenchmarkFilterHeap5And(b *testing.B) {
 	b.StartTimer()
 
 	for i := 0; i < b.N; i++ {
-		_ = filter.Matches(mask.BitMask)
+		_ = filter.Matches(ecs.Mask(mask))
 	}
 }
diff --git a/generic/query_test.go b/generic/query_test.go
index afd79ac..f691438 100644
--- a/generic/query_test.go
+++ b/generic/query_test.go
@@ -16,18 +16,18 @@ func TestQueryOptionalNot(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e0, 1, &testStruct1{1})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{1}})
 
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e1, 1, &testStruct1{2})
-	w.Assign(e1, 2, &testStruct2{1, 1})
-	w.Assign(e1, 8, &testStruct8{})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{1, 1}})
+	w.Assign(e1, ecs.Component{ID: 8, Comp: &testStruct8{}})
 
-	w.Assign(e2, 0, &testStruct0{3})
-	w.Assign(e2, 1, &testStruct1{3})
-	w.Assign(e2, 2, &testStruct2{1, 1})
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{1, 1}})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	query2 := NewFilter2[testStruct0, testStruct1]().Query(&w)
 	cnt := 0
@@ -81,13 +81,13 @@ func TestQuery0(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e0, 8, &testStruct8{})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{}})
 
-	w.Assign(e1, 0, &testStruct0{2})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e2, 0, &testStruct0{2})
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -114,14 +114,14 @@ func TestQuery1(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
 
-	w.Assign(e1, 0, &testStruct0{2})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 0, &testStruct0{0})
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{0}})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -149,17 +149,17 @@ func TestQuery2(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{3})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{3}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{4})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{4}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	filter :=
 		NewFilter2[testStruct0, testStruct1]().
@@ -191,21 +191,21 @@ func TestQuery3(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{3})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{3}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{4})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{4}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
-	w.Assign(e2, 2, &testStruct2{5, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{5, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -235,25 +235,25 @@ func TestQuery4(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{2})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{3}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
-	w.Assign(e2, 2, &testStruct2{4, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
 
-	w.Assign(e0, 3, &testStruct3{4, 0, 0})
-	w.Assign(e1, 3, &testStruct3{5, 0, 0})
-	w.Assign(e2, 3, &testStruct3{5, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -284,29 +284,29 @@ func TestQuery5(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{2})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{3}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
-	w.Assign(e2, 2, &testStruct2{4, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
 
-	w.Assign(e0, 3, &testStruct3{4, 0, 0})
-	w.Assign(e1, 3, &testStruct3{5, 0, 0})
-	w.Assign(e2, 3, &testStruct3{5, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
 
-	w.Assign(e0, 4, &testStruct4{5, 0, 0, 0})
-	w.Assign(e1, 4, &testStruct4{6, 0, 0, 0})
-	w.Assign(e2, 4, &testStruct4{6, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -338,33 +338,33 @@ func TestQuery6(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{2})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{3}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
-	w.Assign(e2, 2, &testStruct2{4, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
 
-	w.Assign(e0, 3, &testStruct3{4, 0, 0})
-	w.Assign(e1, 3, &testStruct3{5, 0, 0})
-	w.Assign(e2, 3, &testStruct3{5, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
 
-	w.Assign(e0, 4, &testStruct4{5, 0, 0, 0})
-	w.Assign(e1, 4, &testStruct4{6, 0, 0, 0})
-	w.Assign(e2, 4, &testStruct4{6, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
 
-	w.Assign(e0, 5, &testStruct5{6, 0, 0, 0, 0})
-	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
-	w.Assign(e2, 5, &testStruct5{7, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -397,37 +397,37 @@ func TestQuery7(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
-	w.Assign(e2, 0, &testStruct0{2})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
+	w.Assign(e2, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
-	w.Assign(e2, 1, &testStruct1{3})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
+	w.Assign(e2, ecs.Component{ID: 1, Comp: &testStruct1{3}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
-	w.Assign(e2, 2, &testStruct2{4, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
+	w.Assign(e2, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
 
-	w.Assign(e0, 3, &testStruct3{4, 0, 0})
-	w.Assign(e1, 3, &testStruct3{5, 0, 0})
-	w.Assign(e2, 3, &testStruct3{5, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
 
-	w.Assign(e0, 4, &testStruct4{5, 0, 0, 0})
-	w.Assign(e1, 4, &testStruct4{6, 0, 0, 0})
-	w.Assign(e2, 4, &testStruct4{6, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
 
-	w.Assign(e0, 5, &testStruct5{6, 0, 0, 0, 0})
-	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
-	w.Assign(e2, 5, &testStruct5{7, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}})
 
-	w.Assign(e0, 6, &testStruct6{7, 0, 0, 0, 0, 0})
-	w.Assign(e1, 6, &testStruct6{8, 0, 0, 0, 0, 0})
-	w.Assign(e2, 6, &testStruct6{8, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}})
+	w.Assign(e2, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -464,33 +464,33 @@ func TestQuery8(t *testing.T) {
 	e1 := w.NewEntity()
 	e2 := w.NewEntity()
 
-	w.Assign(e0, 0, &testStruct0{1})
-	w.Assign(e1, 0, &testStruct0{2})
+	w.Assign(e0, ecs.Component{ID: 0, Comp: &testStruct0{1}})
+	w.Assign(e1, ecs.Component{ID: 0, Comp: &testStruct0{2}})
 
-	w.Assign(e0, 1, &testStruct1{2})
-	w.Assign(e1, 1, &testStruct1{3})
+	w.Assign(e0, ecs.Component{ID: 1, Comp: &testStruct1{2}})
+	w.Assign(e1, ecs.Component{ID: 1, Comp: &testStruct1{3}})
 
-	w.Assign(e0, 2, &testStruct2{3, 0})
-	w.Assign(e1, 2, &testStruct2{4, 0})
+	w.Assign(e0, ecs.Component{ID: 2, Comp: &testStruct2{3, 0}})
+	w.Assign(e1, ecs.Component{ID: 2, Comp: &testStruct2{4, 0}})
 
-	w.Assign(e0, 3, &testStruct3{4, 0, 0})
-	w.Assign(e1, 3, &testStruct3{5, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 3, Comp: &testStruct3{4, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 3, Comp: &testStruct3{5, 0, 0}})
 
-	w.Assign(e0, 4, &testStruct4{5, 0, 0, 0})
-	w.Assign(e1, 4, &testStruct4{6, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 4, Comp: &testStruct4{5, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 4, Comp: &testStruct4{6, 0, 0, 0}})
 
-	w.Assign(e0, 5, &testStruct5{6, 0, 0, 0, 0})
-	w.Assign(e1, 5, &testStruct5{7, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 5, Comp: &testStruct5{6, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 5, Comp: &testStruct5{7, 0, 0, 0, 0}})
 
-	w.Assign(e0, 6, &testStruct6{7, 0, 0, 0, 0, 0})
-	w.Assign(e1, 6, &testStruct6{8, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 6, Comp: &testStruct6{7, 0, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 6, Comp: &testStruct6{8, 0, 0, 0, 0, 0}})
 
-	w.Assign(e0, 7, &testStruct7{8, 0, 0, 0, 0, 0, 0})
-	w.Assign(e1, 7, &testStruct7{9, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 7, Comp: &testStruct7{8, 0, 0, 0, 0, 0, 0}})
+	w.Assign(e1, ecs.Component{ID: 7, Comp: &testStruct7{9, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e0, 8, &testStruct8{9, 0, 0, 0, 0, 0, 0, 0})
+	w.Assign(e0, ecs.Component{ID: 8, Comp: &testStruct8{9, 0, 0, 0, 0, 0, 0, 0}})
 
-	w.Assign(e2, 9, &testStruct9{})
+	w.Assign(e2, ecs.Component{ID: 9, Comp: &testStruct9{}})
 
 	cnt := 0
 	filter :=
@@ -535,9 +535,11 @@ func TestQueryGeneric(t *testing.T) {
 	q := query.Query(&world)
 	cnt := 0
 	for q.Next() {
-		s1, s2 := q.Get()
-		_ = s1
-		_ = s2
+		s1, _ := q.Get()
+		*s1 = testStruct2{int32(cnt), int32(cnt)}
+
+		s1, _ = q.Get()
+		assert.Equal(t, &testStruct2{int32(cnt), int32(cnt)}, s1)
 		cnt++
 	}
 	assert.Equal(t, count, cnt)