Repository: mlange-42/arche
Pull request #84 (2023-02-15 16:57:32)

Generic mutation
Replaced the generic mutation functions by persistent structs to avoid ID lookup in code hotspots.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1483b42..d51d9b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@
 * Entities can be initialized with components, via ID as well as using generics (#76)
 * A listener function can be registered to the world, for notification on entity changes (#77)
 * Support for up to 128 distinct component types per world (was limited to 64 before) (#78)
+* Generic entity manipulation through types `Mutate1`, `Mutate2`, ... (#79)
 
 ### Other
 
diff --git a/README.md b/README.md
index 9c08231..d71ac72 100644
--- a/README.md
+++ b/README.md
@@ -64,10 +64,13 @@ func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
+	// Create a generic entity mutation helper.
+	mutator := generic.NewMutate2[Position, Velocity](&world)
+
 	// Create entities.
 	for i := 0; i < 1000; i++ {
 		// Create a new Entity with components.
-		_, pos, vel := generic.NewEntity2[Position, Velocity](&world)
+		_, pos, vel := mutator.NewEntity()
 
 		// Initialize component fields.
 		pos.X = rand.Float64() * 100
diff --git a/ecs/query.go b/ecs/query.go
index 11cb60a..28fdf2e 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -137,6 +137,14 @@ func (q *queryIter) Mask() BitMask {
 	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/test_types.go b/ecs/types_test.go
similarity index 100%
rename from ecs/test_types.go
rename to ecs/types_test.go
diff --git a/ecs/world.go b/ecs/world.go
index 4de5ac4..d6d8794 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -208,6 +208,14 @@ func (w *World) Mask(entity Entity) BitMask {
 	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.
diff --git a/examples/change_listener/main.go b/examples/change_listener/main.go
index a873ddb..1d55c5e 100644
--- a/examples/change_listener/main.go
+++ b/examples/change_listener/main.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/mlange-42/arche/ecs"
-	"github.com/mlange-42/arche/generic"
 )
 
 // Position component
@@ -43,17 +42,22 @@ func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
+	// Get component IDs
+	posID := ecs.ComponentID[Position](&world)
+	velID := ecs.ComponentID[Velocity](&world)
+	rotID := ecs.ComponentID[Rotation](&world)
+
 	// Register a listener function.
 	world.RegisterListener(Listen)
 
 	// Create/manipulate/delete entities and observe the listener's output
-	e0, _ := generic.NewEntity1[Position](&world)
-	e1, _, _ := generic.NewEntity2[Position, Velocity](&world)
+	e0 := world.NewEntity(posID)
+	e1 := world.NewEntity(posID, velID)
 
-	generic.Add1[Velocity](&world, e0)
-	generic.Add1[Rotation](&world, e1)
+	world.Add(e0, velID)
+	world.Add(e1, rotID)
 
-	generic.Remove2[Position, Velocity](&world, e1)
+	world.Remove(e1, posID, velID)
 
 	world.RemEntity(e0)
 	world.RemEntity(e1)
diff --git a/examples/generic/main.go b/examples/generic/main.go
index b23ea13..48d6c9a 100644
--- a/examples/generic/main.go
+++ b/examples/generic/main.go
@@ -34,12 +34,13 @@ func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
+	// Create a generic entity mutation helper.
+	mutator := generic.NewMutate4[Position, Velocity, Rotation, Elevation](&world)
+
 	// Create entities.
 	for i := 0; i < 1000; i++ {
-		// Create a new Entity.
-		entity := world.NewEntity()
-		// Add components to it.
-		pos, vel, _, _ := generic.Add4[Position, Velocity, Rotation, Elevation](&world, entity)
+		// Create a new Entity with components.
+		_, pos, vel, _, _ := mutator.NewEntity()
 
 		// Initialize component fields.
 		pos.X = rand.Float64() * 100
diff --git a/examples/locked_world/main.go b/examples/locked_world/main.go
index 2ed54fe..6c9d0a9 100644
--- a/examples/locked_world/main.go
+++ b/examples/locked_world/main.go
@@ -33,10 +33,13 @@ func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
+	// Create a generic entity mutation helper.
+	mutator := generic.NewMutate2[Position, Energy](&world)
+
 	// Create entities.
 	for i := 0; i < 1000; i++ {
 		// Create a new Entity.
-		_, pos, en := generic.NewEntity2[Position, Energy](&world)
+		_, pos, en := mutator.NewEntity()
 		pos.X = rand.Float64() * 100
 		pos.Y = rand.Float64() * 100
 		en.Value = rand.Float64()
@@ -97,7 +100,7 @@ func main() {
 			// Create offspring entities.
 			for i := 0; i < repro.Offspring; i++ {
 				// Create a new Entity and initialize it.
-				_, pos, en := generic.NewEntity2[Position, Energy](&world)
+				_, pos, en := mutator.NewEntity()
 				pos.X = repro.X + rand.NormFloat64()
 				pos.Y = repro.Y + rand.NormFloat64()
 				en.Value = rand.Float64() * 0.5
diff --git a/examples/readme/main.go b/examples/readme/main.go
index eca9174..c70243a 100644
--- a/examples/readme/main.go
+++ b/examples/readme/main.go
@@ -26,10 +26,13 @@ func main() {
 	// Create a World.
 	world := ecs.NewWorld()
 
+	// Create a generic entity mutation helper.
+	mutator := generic.NewMutate2[Position, Velocity](&world)
+
 	// Create entities.
 	for i := 0; i < 1000; i++ {
 		// Create a new Entity with components.
-		_, pos, vel := generic.NewEntity2[Position, Velocity](&world)
+		_, pos, vel := mutator.NewEntity()
 
 		// Initialize component fields.
 		pos.X = rand.Float64() * 100
diff --git a/generic/functions.go b/generic/functions.go
deleted file mode 100644
index 164dffc..0000000
--- a/generic/functions.go
+++ /dev/null
@@ -1,270 +0,0 @@
-package generic
-
-import (
-	"github.com/mlange-42/arche/ecs"
-)
-
-// NewEntity1 creates a new entity with two component types.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntity1[A any](w *ecs.World) (ecs.Entity, *A) {
-	idA := ecs.ComponentID[A](w)
-	entity := w.NewEntity(idA)
-	return entity, (*A)(w.Get(entity, idA))
-}
-
-// NewEntity2 creates a new entity with two component types.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntity2[A any, B any](w *ecs.World) (ecs.Entity, *A, *B) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	entity := w.NewEntity(idA, idB)
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB))
-}
-
-// NewEntity3 creates a new entity with three component types.
-// Returns the entity and the new components.
-//
-// See also and [ecs.World.NewEntity].
-func NewEntity3[A any, B any, C any](w *ecs.World) (ecs.Entity, *A, *B, *C) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	entity := w.NewEntity(idA, idB, idC)
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC))
-}
-
-// NewEntity4 creates a new entity with four component types.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntity4[A any, B any, C any, D any](w *ecs.World) (ecs.Entity, *A, *B, *C, *D) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	entity := w.NewEntity(idA, idB, idC, idD)
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD))
-}
-
-// NewEntity5 creates a new entity with five component types.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntity5[A any, B any, C any, D any, E any](w *ecs.World) (ecs.Entity, *A, *B, *C, *D, *E) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	idE := ecs.ComponentID[E](w)
-	entity := w.NewEntity(idA, idB, idC, idD, idE)
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD)), (*E)(w.Get(entity, idE))
-}
-
-// NewEntityWith1 creates a new entity with one component, taken from a pointer.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntityWith1[A any](w *ecs.World, a *A) (ecs.Entity, *A) {
-	idA := ecs.ComponentID[A](w)
-	entity := w.NewEntityWith(ecs.Component{ID: idA, Component: a})
-	return entity, (*A)(w.Get(entity, idA))
-}
-
-// NewEntityWith2 creates a new entity with two components, taken from pointers.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntityWith2[A any, B any](w *ecs.World, a *A, b *B) (ecs.Entity, *A, *B) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	entity := w.NewEntityWith(ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b})
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB))
-}
-
-// NewEntityWith3 creates a new entity with three components, taken from pointers.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntityWith3[A any, B any, C any](w *ecs.World, a *A, b *B, c *C) (ecs.Entity, *A, *B, *C) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	entity := w.NewEntityWith(ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c})
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC))
-}
-
-// NewEntityWith4 creates a new entity with four components, taken from pointers.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntityWith4[A any, B any, C any, D any](w *ecs.World, a *A, b *B, c *C, d *D) (ecs.Entity, *A, *B, *C, *D) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	entity := w.NewEntityWith(ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c}, ecs.Component{ID: idD, Component: d})
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD))
-}
-
-// NewEntityWith5 creates a new entity with five components, taken from pointers.
-// Returns the entity and the new components.
-//
-// See also [ecs.World.NewEntity].
-func NewEntityWith5[A any, B any, C any, D any, E any](w *ecs.World, a *A, b *B, c *C, d *D, e *E) (ecs.Entity, *A, *B, *C, *D, *E) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	idE := ecs.ComponentID[E](w)
-	entity := w.NewEntityWith(ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c}, ecs.Component{ID: idD, Component: d}, ecs.Component{ID: idE, Component: e})
-	return entity, (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD)), (*E)(w.Get(entity, idE))
-}
-
-// Add1 adds a component type to an entity, and returns the component.
-//
-// See also [ecs.World.Add].
-func Add1[A any](w *ecs.World, entity ecs.Entity) *A {
-	id := ecs.ComponentID[A](w)
-	w.Add(entity, id)
-	return (*A)(w.Get(entity, id))
-}
-
-// Add2 adds two component types to an entity, and returns the components.
-//
-// See also [ecs.World.Add].
-func Add2[A any, B any](w *ecs.World, entity ecs.Entity) (*A, *B) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	w.Add(entity, idA, idB)
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB))
-}
-
-// Add3 adds three component types to an entity, and returns the components.
-//
-// See also [ecs.World.Add].
-func Add3[A any, B any, C any](w *ecs.World, entity ecs.Entity) (*A, *B, *C) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	w.Add(entity, idA, idB, idC)
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC))
-}
-
-// Add4 adds four component types to an entity, and returns the components.
-//
-// See also [ecs.World.Add].
-func Add4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity) (*A, *B, *C, *D) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	w.Add(entity, idA, idB, idC, idD)
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD))
-}
-
-// Add5 adds five component types to an entity, and returns the components.
-//
-// See also [ecs.World.Add].
-func Add5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity) (*A, *B, *C, *D, *E) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	idE := ecs.ComponentID[E](w)
-	w.Add(entity, idA, idB, idC, idD, idE)
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD)), (*E)(w.Get(entity, idE))
-}
-
-// Assign1 adds a components to an entity.
-//
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
-func Assign1[A any](w *ecs.World, entity ecs.Entity, a *A) *A {
-	idA := ecs.ComponentID[A](w)
-	w.Assign(entity, idA, a)
-	return (*A)(w.Get(entity, idA))
-}
-
-// Assign2 adds two components to an entity.
-//
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
-func Assign2[A any, B any](w *ecs.World, entity ecs.Entity, a *A, b *B) (*A, *B) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	w.AssignN(entity, ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b})
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB))
-}
-
-// Assign3 adds three components to an entity.
-//
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
-func Assign3[A any, B any, C any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C) (*A, *B, *C) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	w.AssignN(entity, ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c})
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC))
-}
-
-// Assign4 adds four components to an entity.
-//
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
-func Assign4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C, d *D) (*A, *B, *C, *D) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	w.AssignN(entity, ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c}, ecs.Component{ID: idD, Component: d})
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD))
-}
-
-// Assign5 adds five components to an entity.
-//
-// See also [ecs.World.Assign] and [ecs.World.AssignN].
-func Assign5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity, a *A, b *B, c *C, d *D, e *E) (*A, *B, *C, *D, *E) {
-	idA := ecs.ComponentID[A](w)
-	idB := ecs.ComponentID[B](w)
-	idC := ecs.ComponentID[C](w)
-	idD := ecs.ComponentID[D](w)
-	idE := ecs.ComponentID[E](w)
-	w.AssignN(entity, ecs.Component{ID: idA, Component: a}, ecs.Component{ID: idB, Component: b}, ecs.Component{ID: idC, Component: c}, ecs.Component{ID: idD, Component: d}, ecs.Component{ID: idE, Component: e})
-	return (*A)(w.Get(entity, idA)), (*B)(w.Get(entity, idB)), (*C)(w.Get(entity, idC)), (*D)(w.Get(entity, idD)), (*E)(w.Get(entity, idE))
-}
-
-// Remove1 removes a component from an entity.
-//
-// See also [ecs.World.Remove].
-func Remove1[A any](w *ecs.World, entity ecs.Entity) {
-	w.Remove(entity, ecs.ComponentID[A](w))
-}
-
-// Remove2 removes two components from an entity.
-//
-// See also [ecs.World.Remove].
-func Remove2[A any, B any](w *ecs.World, entity ecs.Entity) {
-	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w))
-}
-
-// Remove3 removes three components from an entity.
-//
-// See also [ecs.World.Remove].
-func Remove3[A any, B any, C any](w *ecs.World, entity ecs.Entity) {
-	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w))
-}
-
-// Remove4 removes four components from an entity.
-//
-// See also [ecs.World.Remove].
-func Remove4[A any, B any, C any, D any](w *ecs.World, entity ecs.Entity) {
-	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w))
-}
-
-// Remove5 removes five components from an entity.
-//
-// See also [ecs.World.Remove].
-func Remove5[A any, B any, C any, D any, E any](w *ecs.World, entity ecs.Entity) {
-	w.Remove(entity, ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w))
-}
diff --git a/generic/generate/main.go b/generic/generate/main.go
index 96c0338..867920b 100644
--- a/generic/generate/main.go
+++ b/generic/generate/main.go
@@ -9,30 +9,102 @@ import (
 )
 
 var typeLetters = []string{"A", "B", "C", "D", "E", "F", "G", "H"}
+var numbers = []string{"zero", "one", "two", "three", "four", "five", "six", "seven", "eight"}
+var indices = []string{"zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth"}
 
 type query struct {
 	Index       int
+	NumberStr   string
 	Types       string
 	TypesFull   string
 	TypesReturn string
 	ReturnAll   string
 	Include     string
+	Components  string
+	Arguments   string
 }
 type getter struct {
-	Query query
-	ID    int
-	Index int
-	Type  string
+	Query    query
+	ID       int
+	Index    int
+	IndexStr string
+	Type     string
 }
 
 func main() {
-	fmt.Println("Generating code")
+	generateQueries()
+	generateMutates()
+}
+
+func generateMutates() {
+	fmt.Println("Generating mutates")
+
+	maxIndex := len(typeLetters)
+	text := bytes.Buffer{}
+
+	header, err := template.ParseFiles("./generate/mutate_header.gotxt")
+	if err != nil {
+		panic(err)
+	}
+	err = header.Execute(&text, struct{}{})
+	if err != nil {
+		panic(err)
+	}
+
+	mutate, err := template.ParseFiles("./generate/mutate.gotxt")
+	if err != nil {
+		panic(err)
+	}
+
+	for i := 1; i <= maxIndex; i++ {
+		returnAll := ""
+		components := ""
+		arguments := ""
+
+		types := "[" + strings.Join(typeLetters[:i], ", ") + "]"
+		returnTypes := "*" + strings.Join(typeLetters[:i], ", *")
+		fullTypes := "[" + strings.Join(typeLetters[:i], " any, ") + " any]"
+		include := "[]ecs.ID{ecs.ComponentID[" + strings.Join(typeLetters[:i], "](w), ecs.ComponentID[") + "](w)}"
+		for j := 0; j < i; j++ {
+			returnAll += fmt.Sprintf("(*%s)(m.world.Get(entity, m.ids[%d]))", typeLetters[j], j)
+			arguments += fmt.Sprintf("%s *%s", strings.ToLower(typeLetters[j]), typeLetters[j])
+			if j < i-1 {
+				returnAll += ", "
+				arguments += ", "
+			}
+			components += fmt.Sprintf("ecs.Component{ID: m.ids[%d], Component: %s},\n", j, strings.ToLower(typeLetters[j]))
+		}
+
+		data := query{
+			Index:       i,
+			NumberStr:   numbers[i],
+			Types:       types,
+			TypesReturn: returnTypes,
+			TypesFull:   fullTypes,
+			ReturnAll:   returnAll,
+			Include:     include,
+			Components:  components,
+			Arguments:   arguments,
+		}
+		err = mutate.Execute(&text, data)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	if err := os.WriteFile("mutate_generated.go", text.Bytes(), 0666); err != nil {
+		panic(err)
+	}
+}
+
+func generateQueries() {
+	fmt.Println("Generating queries")
 
 	maxIndex := len(typeLetters)
 
 	text := bytes.Buffer{}
 
-	header, err := template.ParseFiles("./generate/header.gotxt")
+	header, err := template.ParseFiles("./generate/query_header.gotxt")
 	if err != nil {
 		panic(err)
 	}
@@ -64,16 +136,19 @@ func main() {
 			types = "[" + strings.Join(typeLetters[:i], ", ") + "]"
 			returnTypes = "*" + strings.Join(typeLetters[:i], ", *")
 			fullTypes = "[" + strings.Join(typeLetters[:i], " any, ") + " any]"
-			include = "include: []reflect.Type{typeOf[" + strings.Join(typeLetters[:i], "](), typeOf[") + "]()},"
+			include = "[]reflect.Type{typeOf[" + strings.Join(typeLetters[:i], "](), typeOf[") + "]()}"
 			for j := 0; j < i; j++ {
 				returnAll += fmt.Sprintf("(*%s)(q.Query.Get(q.ids[%d]))", typeLetters[j], j)
 				if j < i-1 {
 					returnAll += ", "
 				}
 			}
+		} else {
+			include = "[]reflect.Type{}"
 		}
 		data := query{
 			Index:       i,
+			NumberStr:   numbers[i],
 			Types:       types,
 			TypesReturn: returnTypes,
 			TypesFull:   fullTypes,
@@ -95,10 +170,11 @@ func main() {
 
 		for j := 0; j < i; j++ {
 			getterData := getter{
-				Query: data,
-				ID:    j + 1,
-				Index: j,
-				Type:  typeLetters[j],
+				Query:    data,
+				ID:       j + 1,
+				Index:    j,
+				IndexStr: indices[j+1],
+				Type:     typeLetters[j],
 			}
 			err = queryGet.Execute(&text, getterData)
 			if err != nil {
diff --git a/generic/generate/mutate.gotxt b/generic/generate/mutate.gotxt
new file mode 100644
index 0000000..0f7a0db
--- /dev/null
+++ b/generic/generate/mutate.gotxt
@@ -0,0 +1,52 @@
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate{{ .Index }} is a helper for mutating {{ .NumberStr }} components.
+//
+// It can be used to create entities ([Mutate{{ .Index }}.NewEntity] and [Mutate{{ .Index }}.NewEntityWith]),
+// and to add or remove components ([Mutate{{ .Index }}.Add], [Mutate{{ .Index }}.Assign] and [Mutate{{ .Index }}.Remove]).
+type Mutate{{ .Index }}{{ .TypesFull }} struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate{{ .Index }} creates a new Mutate{{ .Index }} object.
+func NewMutate{{ .Index }}{{ .TypesFull }}(w *ecs.World) Mutate{{ .Index }}{{ .Types }} {
+	return Mutate{{ .Index }}{{ .Types }}{
+		ids:   {{ .Include }},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate{{ .Index }}'s components.
+func (m *Mutate{{ .Index }}{{ .Types }}) NewEntity() (ecs.Entity, {{ .TypesReturn }}) {
+	entity := m.world.NewEntity(m.ids...)
+	return entity, {{ .ReturnAll }}
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate{{ .Index }}'s components, using the supplied values.
+func (m *Mutate{{ .Index }}{{ .Types }}) NewEntityWith({{ .Arguments }}) (ecs.Entity, {{ .TypesReturn }}) {
+	entity := m.world.NewEntityWith(
+		{{ .Components }}
+	)
+	return entity, {{ .ReturnAll }}
+}
+
+// Add the Mutate{{ .Index }}'s components to the given entity.
+func (m *Mutate{{ .Index }}{{ .Types }}) Add(entity ecs.Entity) ({{ .TypesReturn }}) {
+	m.world.Add(entity, m.ids...)
+	return {{ .ReturnAll }}
+}
+
+// Assign the Mutate{{ .Index }}'s components to the given entity, using the supplied values.
+func (m *Mutate{{ .Index }}{{ .Types }}) Assign(entity ecs.Entity, {{ .Arguments }}) ({{ .TypesReturn }}) {
+	m.world.AssignN(entity,
+		{{ .Components }}
+	)
+	return {{ .ReturnAll }}
+}
+
+// Remove the Mutate{{ .Index }}'s components from the given entity.
+func (m *Mutate{{ .Index }}{{ .Types }}) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
diff --git a/generic/generate/mutate_header.gotxt b/generic/generate/mutate_header.gotxt
new file mode 100644
index 0000000..d5b41eb
--- /dev/null
+++ b/generic/generate/mutate_header.gotxt
@@ -0,0 +1,7 @@
+package generic
+
+// Code generated by go generate; DO NOT EDIT.
+
+import (
+	"github.com/mlange-42/arche/ecs"
+)
diff --git a/generic/generate/query.gotxt b/generic/generate/query.gotxt
index c71c482..245a7a7 100644
--- a/generic/generate/query.gotxt
+++ b/generic/generate/query.gotxt
@@ -1,7 +1,7 @@
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter{{ .Index }} builds a [Query{{ .Index }}] query
+// Filter{{ .Index }} is a helper for building [Query{{ .Index }}] query iterators.
 type Filter{{ .Index }}{{ .TypesFull }} struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -9,12 +9,12 @@ type Filter{{ .Index }}{{ .TypesFull }} struct {
 	compiled compiledQuery
 }
 
-// NewQuery{{ .Index }} creates a generic filter for two components.
+// NewFilter{{ .Index }} creates a generic Filter{{ .Index }} for {{ .NumberStr }} components.
 //
 // See also [ecs.World.Query].
 func NewFilter{{ .Index }}{{ .TypesFull }}() *Filter{{ .Index }}{{ .Types }} {
 	return &Filter{{ .Index }}{{ .Types }}{
-		{{ .Include }}
+		include: {{ .Include }},
 	}
 }
 
@@ -65,7 +65,7 @@ func (q *Filter{{ .Index }}{{ .Types }}) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query{{ .Index }} is a generic query iterator for two components.
+// Query{{ .Index }} is a generic query iterator for {{ .NumberStr }} components.
 //
 // Create one with [NewFilter{{ .Index }}] and [Filter{{ .Index }}.Query]
 type Query{{ .Index }}{{ .TypesFull }} struct {
diff --git a/generic/generate/query_get.gotxt b/generic/generate/query_get.gotxt
index 7011298..cef982f 100644
--- a/generic/generate/query_get.gotxt
+++ b/generic/generate/query_get.gotxt
@@ -1,5 +1,5 @@
 
-// Get{{ .ID }} returns the first queried component for the current query position
+// Get{{ .ID }} returns the {{ .IndexStr }} queried component for the current query iterator position.
 func (q *Query{{ .Query.Index }}{{ .Query.Types }}) Get{{ .ID }}() *{{ .Type }} {
 	return (*{{ .Type }})(q.Query.Get(q.ids[{{ .Index }}]))
 }
diff --git a/generic/mutate_generated.go b/generic/mutate_generated.go
new file mode 100644
index 0000000..179c0f1
--- /dev/null
+++ b/generic/mutate_generated.go
@@ -0,0 +1,479 @@
+package generic
+
+// Code generated by go generate; DO NOT EDIT.
+
+import (
+	"github.com/mlange-42/arche/ecs"
+)
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate1 is a helper for mutating one components.
+//
+// It can be used to create entities ([Mutate1.NewEntity] and [Mutate1.NewEntityWith]),
+// and to add or remove components ([Mutate1.Add], [Mutate1.Assign] and [Mutate1.Remove]).
+type Mutate1[A any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate1 creates a new Mutate1 object.
+func NewMutate1[A any](w *ecs.World) Mutate1[A] {
+	return Mutate1[A]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate1's components.
+func (m *Mutate1[A]) NewEntity() (ecs.Entity, *A) {
+	entity := m.world.NewEntity(m.ids...)
+	return entity, (*A)(m.world.Get(entity, m.ids[0]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate1's components, using the supplied values.
+func (m *Mutate1[A]) NewEntityWith(a *A) (ecs.Entity, *A) {
+	entity := m.world.NewEntityWith(
+		ecs.Component{ID: m.ids[0], Component: a},
+	)
+	return entity, (*A)(m.world.Get(entity, m.ids[0]))
+}
+
+// Add the Mutate1's components to the given entity.
+func (m *Mutate1[A]) Add(entity ecs.Entity) *A {
+	m.world.Add(entity, m.ids...)
+	return (*A)(m.world.Get(entity, m.ids[0]))
+}
+
+// Assign the Mutate1's components to the given entity, using the supplied values.
+func (m *Mutate1[A]) Assign(entity ecs.Entity, a *A) *A {
+	m.world.AssignN(entity,
+		ecs.Component{ID: m.ids[0], Component: a},
+	)
+	return (*A)(m.world.Get(entity, m.ids[0]))
+}
+
+// Remove the Mutate1's components from the given entity.
+func (m *Mutate1[A]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate2 is a helper for mutating two components.
+//
+// It can be used to create entities ([Mutate2.NewEntity] and [Mutate2.NewEntityWith]),
+// and to add or remove components ([Mutate2.Add], [Mutate2.Assign] and [Mutate2.Remove]).
+type Mutate2[A any, B any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate2 creates a new Mutate2 object.
+func NewMutate2[A any, B any](w *ecs.World) Mutate2[A, B] {
+	return Mutate2[A, B]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate2's components.
+func (m *Mutate2[A, B]) NewEntity() (ecs.Entity, *A, *B) {
+	entity := m.world.NewEntity(m.ids...)
+	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate2's components, using the supplied values.
+func (m *Mutate2[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},
+	)
+	return entity, (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
+}
+
+// Add the Mutate2's components to the given entity.
+func (m *Mutate2[A, B]) Add(entity ecs.Entity) (*A, *B) {
+	m.world.Add(entity, m.ids...)
+	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
+}
+
+// Assign the Mutate2's components to the given entity, using the supplied values.
+func (m *Mutate2[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},
+	)
+	return (*A)(m.world.Get(entity, m.ids[0])), (*B)(m.world.Get(entity, m.ids[1]))
+}
+
+// Remove the Mutate2's components from the given entity.
+func (m *Mutate2[A, B]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate3 is a helper for mutating three components.
+//
+// It can be used to create entities ([Mutate3.NewEntity] and [Mutate3.NewEntityWith]),
+// and to add or remove components ([Mutate3.Add], [Mutate3.Assign] and [Mutate3.Remove]).
+type Mutate3[A any, B any, C any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate3 creates a new Mutate3 object.
+func NewMutate3[A any, B any, C any](w *ecs.World) Mutate3[A, B, C] {
+	return Mutate3[A, B, C]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate3's components.
+func (m *Mutate3[A, B, C]) NewEntity() (ecs.Entity, *A, *B, *C) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate3's components, using the supplied values.
+func (m *Mutate3[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},
+	)
+	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]))
+}
+
+// Add the Mutate3's components to the given entity.
+func (m *Mutate3[A, B, C]) Add(entity ecs.Entity) (*A, *B, *C) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate3's components to the given entity, using the supplied values.
+func (m *Mutate3[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},
+	)
+	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]))
+}
+
+// Remove the Mutate3's components from the given entity.
+func (m *Mutate3[A, B, C]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate4 is a helper for mutating four components.
+//
+// It can be used to create entities ([Mutate4.NewEntity] and [Mutate4.NewEntityWith]),
+// and to add or remove components ([Mutate4.Add], [Mutate4.Assign] and [Mutate4.Remove]).
+type Mutate4[A any, B any, C any, D any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate4 creates a new Mutate4 object.
+func NewMutate4[A any, B any, C any, D any](w *ecs.World) Mutate4[A, B, C, D] {
+	return Mutate4[A, B, C, D]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate4's components.
+func (m *Mutate4[A, B, C, D]) NewEntity() (ecs.Entity, *A, *B, *C, *D) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate4's components, using the supplied values.
+func (m *Mutate4[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},
+	)
+	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]))
+}
+
+// Add the Mutate4's components to the given entity.
+func (m *Mutate4[A, B, C, D]) Add(entity ecs.Entity) (*A, *B, *C, *D) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate4's components to the given entity, using the supplied values.
+func (m *Mutate4[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},
+	)
+	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]))
+}
+
+// Remove the Mutate4's components from the given entity.
+func (m *Mutate4[A, B, C, D]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate5 is a helper for mutating five components.
+//
+// It can be used to create entities ([Mutate5.NewEntity] and [Mutate5.NewEntityWith]),
+// and to add or remove components ([Mutate5.Add], [Mutate5.Assign] and [Mutate5.Remove]).
+type Mutate5[A any, B any, C any, D any, E any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate5 creates a new Mutate5 object.
+func NewMutate5[A any, B any, C any, D any, E any](w *ecs.World) Mutate5[A, B, C, D, E] {
+	return Mutate5[A, B, C, D, E]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate5's components.
+func (m *Mutate5[A, B, C, D, E]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate5's components, using the supplied values.
+func (m *Mutate5[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},
+	)
+	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]))
+}
+
+// Add the Mutate5's components to the given entity.
+func (m *Mutate5[A, B, C, D, E]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate5's components to the given entity, using the supplied values.
+func (m *Mutate5[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},
+	)
+	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]))
+}
+
+// Remove the Mutate5's components from the given entity.
+func (m *Mutate5[A, B, C, D, E]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate6 is a helper for mutating six components.
+//
+// It can be used to create entities ([Mutate6.NewEntity] and [Mutate6.NewEntityWith]),
+// and to add or remove components ([Mutate6.Add], [Mutate6.Assign] and [Mutate6.Remove]).
+type Mutate6[A any, B any, C any, D any, E any, F any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate6 creates a new Mutate6 object.
+func NewMutate6[A any, B any, C any, D any, E any, F any](w *ecs.World) Mutate6[A, B, C, D, E, F] {
+	return Mutate6[A, B, C, D, E, F]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w), ecs.ComponentID[F](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate6's components.
+func (m *Mutate6[A, B, C, D, E, F]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate6's components, using the supplied values.
+func (m *Mutate6[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},
+	)
+	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]))
+}
+
+// Add the Mutate6's components to the given entity.
+func (m *Mutate6[A, B, C, D, E, F]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E, *F) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate6's components to the given entity, using the supplied values.
+func (m *Mutate6[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},
+	)
+	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]))
+}
+
+// Remove the Mutate6's components from the given entity.
+func (m *Mutate6[A, B, C, D, E, F]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate7 is a helper for mutating seven components.
+//
+// It can be used to create entities ([Mutate7.NewEntity] and [Mutate7.NewEntityWith]),
+// and to add or remove components ([Mutate7.Add], [Mutate7.Assign] and [Mutate7.Remove]).
+type Mutate7[A any, B any, C any, D any, E any, F any, G any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate7 creates a new Mutate7 object.
+func NewMutate7[A any, B any, C any, D any, E any, F any, G any](w *ecs.World) Mutate7[A, B, C, D, E, F, G] {
+	return Mutate7[A, B, C, D, E, F, G]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w), ecs.ComponentID[F](w), ecs.ComponentID[G](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate7's components.
+func (m *Mutate7[A, B, C, D, E, F, G]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F, *G) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate7's components, using the supplied values.
+func (m *Mutate7[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},
+	)
+	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]))
+}
+
+// Add the Mutate7's components to the given entity.
+func (m *Mutate7[A, B, C, D, E, F, G]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate7's components to the given entity, using the supplied values.
+func (m *Mutate7[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},
+	)
+	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]))
+}
+
+// Remove the Mutate7's components from the given entity.
+func (m *Mutate7[A, B, C, D, E, F, G]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// Mutate8 is a helper for mutating eight components.
+//
+// It can be used to create entities ([Mutate8.NewEntity] and [Mutate8.NewEntityWith]),
+// and to add or remove components ([Mutate8.Add], [Mutate8.Assign] and [Mutate8.Remove]).
+type Mutate8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
+	ids   []ecs.ID
+	world *ecs.World
+}
+
+// NewMutate8 creates a new Mutate8 object.
+func NewMutate8[A any, B any, C any, D any, E any, F any, G any, H any](w *ecs.World) Mutate8[A, B, C, D, E, F, G, H] {
+	return Mutate8[A, B, C, D, E, F, G, H]{
+		ids:   []ecs.ID{ecs.ComponentID[A](w), ecs.ComponentID[B](w), ecs.ComponentID[C](w), ecs.ComponentID[D](w), ecs.ComponentID[E](w), ecs.ComponentID[F](w), ecs.ComponentID[G](w), ecs.ComponentID[H](w)},
+		world: w,
+	}
+}
+
+// NewEntity creates a new [ecs.Entity] with the Mutate8's components.
+func (m *Mutate8[A, B, C, D, E, F, G, H]) NewEntity() (ecs.Entity, *A, *B, *C, *D, *E, *F, *G, *H) {
+	entity := m.world.NewEntity(m.ids...)
+	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]))
+}
+
+// NewEntityWith creates a new [ecs.Entity] with the Mutate8's components, using the supplied values.
+func (m *Mutate8[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},
+	)
+	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]))
+}
+
+// Add the Mutate8's components to the given entity.
+func (m *Mutate8[A, B, C, D, E, F, G, H]) Add(entity ecs.Entity) (*A, *B, *C, *D, *E, *F, *G, *H) {
+	m.world.Add(entity, m.ids...)
+	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]))
+}
+
+// Assign the Mutate8's components to the given entity, using the supplied values.
+func (m *Mutate8[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},
+	)
+	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]))
+}
+
+// Remove the Mutate8's components from the given entity.
+func (m *Mutate8[A, B, C, D, E, F, G, H]) Remove(entity ecs.Entity) {
+	m.world.Remove(entity, m.ids...)
+}
diff --git a/generic/query_generated.go b/generic/query_generated.go
index 8f647ac..8cbfda1 100644
--- a/generic/query_generated.go
+++ b/generic/query_generated.go
@@ -10,7 +10,7 @@ import (
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter0 builds a [Query0] query
+// Filter0 is a helper for building [Query0] query iterators.
 type Filter0 struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -18,11 +18,13 @@ type Filter0 struct {
 	compiled compiledQuery
 }
 
-// NewQuery0 creates a generic filter for two components.
+// NewFilter0 creates a generic Filter0 for zero components.
 //
 // See also [ecs.World.Query].
 func NewFilter0() *Filter0 {
-	return &Filter0{}
+	return &Filter0{
+		include: []reflect.Type{},
+	}
 }
 
 // Optional makes some of the query's components optional.
@@ -72,7 +74,7 @@ func (q *Filter0) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query0 is a generic query iterator for two components.
+// Query0 is a generic query iterator for zero components.
 //
 // Create one with [NewFilter0] and [Filter0.Query]
 type Query0 struct {
@@ -82,7 +84,7 @@ type Query0 struct {
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter1 builds a [Query1] query
+// Filter1 is a helper for building [Query1] query iterators.
 type Filter1[A any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -90,7 +92,7 @@ type Filter1[A any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery1 creates a generic filter for two components.
+// NewFilter1 creates a generic Filter1 for one components.
 //
 // See also [ecs.World.Query].
 func NewFilter1[A any]() *Filter1[A] {
@@ -146,7 +148,7 @@ func (q *Filter1[A]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query1 is a generic query iterator for two components.
+// Query1 is a generic query iterator for one components.
 //
 // Create one with [NewFilter1] and [Filter1.Query]
 type Query1[A any] struct {
@@ -159,14 +161,14 @@ func (q *Query1[A]) GetAll() (ecs.Entity, *A) {
 	return q.Entity(), (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query1[A]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter2 builds a [Query2] query
+// Filter2 is a helper for building [Query2] query iterators.
 type Filter2[A any, B any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -174,7 +176,7 @@ type Filter2[A any, B any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery2 creates a generic filter for two components.
+// NewFilter2 creates a generic Filter2 for two components.
 //
 // See also [ecs.World.Query].
 func NewFilter2[A any, B any]() *Filter2[A, B] {
@@ -243,19 +245,19 @@ func (q *Query2[A, B]) GetAll() (ecs.Entity, *A, *B) {
 	return q.Entity(), (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query2[A, B]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query2[A, B]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter3 builds a [Query3] query
+// Filter3 is a helper for building [Query3] query iterators.
 type Filter3[A any, B any, C any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -263,7 +265,7 @@ type Filter3[A any, B any, C any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery3 creates a generic filter for two components.
+// NewFilter3 creates a generic Filter3 for three components.
 //
 // See also [ecs.World.Query].
 func NewFilter3[A any, B any, C any]() *Filter3[A, B, C] {
@@ -319,7 +321,7 @@ func (q *Filter3[A, B, C]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query3 is a generic query iterator for two components.
+// Query3 is a generic query iterator for three components.
 //
 // Create one with [NewFilter3] and [Filter3.Query]
 type Query3[A any, B any, C any] struct {
@@ -332,24 +334,24 @@ func (q *Query3[A, B, C]) GetAll() (ecs.Entity, *A, *B, *C) {
 	return q.Entity(), (*A)(q.Query.Get(q.ids[0])), (*B)(q.Query.Get(q.ids[1])), (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query3[A, B, C]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query3[A, B, C]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query3[A, B, C]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter4 builds a [Query4] query
+// Filter4 is a helper for building [Query4] query iterators.
 type Filter4[A any, B any, C any, D any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -357,7 +359,7 @@ type Filter4[A any, B any, C any, D any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery4 creates a generic filter for two components.
+// NewFilter4 creates a generic Filter4 for four components.
 //
 // See also [ecs.World.Query].
 func NewFilter4[A any, B any, C any, D any]() *Filter4[A, B, C, D] {
@@ -413,7 +415,7 @@ func (q *Filter4[A, B, C, D]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query4 is a generic query iterator for two components.
+// Query4 is a generic query iterator for four components.
 //
 // Create one with [NewFilter4] and [Filter4.Query]
 type Query4[A any, B any, C any, D any] struct {
@@ -426,29 +428,29 @@ func (q *Query4[A, B, C, D]) GetAll() (ecs.Entity, *A, *B, *C, *D) {
 	return q.Entity(), (*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]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query4[A, B, C, D]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query4[A, B, C, D]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query4[A, B, C, D]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get4 returns the first queried component for the current query position
+// Get4 returns the fourth queried component for the current query iterator position.
 func (q *Query4[A, B, C, D]) Get4() *D {
 	return (*D)(q.Query.Get(q.ids[3]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter5 builds a [Query5] query
+// Filter5 is a helper for building [Query5] query iterators.
 type Filter5[A any, B any, C any, D any, E any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -456,7 +458,7 @@ type Filter5[A any, B any, C any, D any, E any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery5 creates a generic filter for two components.
+// NewFilter5 creates a generic Filter5 for five components.
 //
 // See also [ecs.World.Query].
 func NewFilter5[A any, B any, C any, D any, E any]() *Filter5[A, B, C, D, E] {
@@ -512,7 +514,7 @@ func (q *Filter5[A, B, C, D, E]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query5 is a generic query iterator for two components.
+// Query5 is a generic query iterator for five components.
 //
 // Create one with [NewFilter5] and [Filter5.Query]
 type Query5[A any, B any, C any, D any, E any] struct {
@@ -525,34 +527,34 @@ func (q *Query5[A, B, C, D, E]) GetAll() (ecs.Entity, *A, *B, *C, *D, *E) {
 	return q.Entity(), (*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]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query5[A, B, C, D, E]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query5[A, B, C, D, E]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query5[A, B, C, D, E]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get4 returns the first queried component for the current query position
+// Get4 returns the fourth queried component for the current query iterator position.
 func (q *Query5[A, B, C, D, E]) Get4() *D {
 	return (*D)(q.Query.Get(q.ids[3]))
 }
 
-// Get5 returns the first queried component for the current query position
+// Get5 returns the fifth queried component for the current query iterator position.
 func (q *Query5[A, B, C, D, E]) Get5() *E {
 	return (*E)(q.Query.Get(q.ids[4]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter6 builds a [Query6] query
+// Filter6 is a helper for building [Query6] query iterators.
 type Filter6[A any, B any, C any, D any, E any, F any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -560,7 +562,7 @@ type Filter6[A any, B any, C any, D any, E any, F any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery6 creates a generic filter for two components.
+// NewFilter6 creates a generic Filter6 for six components.
 //
 // See also [ecs.World.Query].
 func NewFilter6[A any, B any, C any, D any, E any, F any]() *Filter6[A, B, C, D, E, F] {
@@ -616,7 +618,7 @@ func (q *Filter6[A, B, C, D, E, F]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query6 is a generic query iterator for two components.
+// Query6 is a generic query iterator for six components.
 //
 // Create one with [NewFilter6] and [Filter6.Query]
 type Query6[A any, B any, C any, D any, E any, F any] struct {
@@ -629,39 +631,39 @@ func (q *Query6[A, B, C, D, E, F]) GetAll() (ecs.Entity, *A, *B, *C, *D, *E, *F)
 	return q.Entity(), (*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]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get4 returns the first queried component for the current query position
+// Get4 returns the fourth queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get4() *D {
 	return (*D)(q.Query.Get(q.ids[3]))
 }
 
-// Get5 returns the first queried component for the current query position
+// Get5 returns the fifth queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get5() *E {
 	return (*E)(q.Query.Get(q.ids[4]))
 }
 
-// Get6 returns the first queried component for the current query position
+// Get6 returns the sixth queried component for the current query iterator position.
 func (q *Query6[A, B, C, D, E, F]) Get6() *F {
 	return (*F)(q.Query.Get(q.ids[5]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter7 builds a [Query7] query
+// Filter7 is a helper for building [Query7] query iterators.
 type Filter7[A any, B any, C any, D any, E any, F any, G any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -669,7 +671,7 @@ type Filter7[A any, B any, C any, D any, E any, F any, G any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery7 creates a generic filter for two components.
+// NewFilter7 creates a generic Filter7 for seven components.
 //
 // See also [ecs.World.Query].
 func NewFilter7[A any, B any, C any, D any, E any, F any, G any]() *Filter7[A, B, C, D, E, F, G] {
@@ -725,7 +727,7 @@ func (q *Filter7[A, B, C, D, E, F, G]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query7 is a generic query iterator for two components.
+// Query7 is a generic query iterator for seven components.
 //
 // Create one with [NewFilter7] and [Filter7.Query]
 type Query7[A any, B any, C any, D any, E any, F any, G any] struct {
@@ -738,44 +740,44 @@ func (q *Query7[A, B, C, D, E, F, G]) GetAll() (ecs.Entity, *A, *B, *C, *D, *E,
 	return q.Entity(), (*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]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get4 returns the first queried component for the current query position
+// Get4 returns the fourth queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get4() *D {
 	return (*D)(q.Query.Get(q.ids[3]))
 }
 
-// Get5 returns the first queried component for the current query position
+// Get5 returns the fifth queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get5() *E {
 	return (*E)(q.Query.Get(q.ids[4]))
 }
 
-// Get6 returns the first queried component for the current query position
+// Get6 returns the sixth queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get6() *F {
 	return (*F)(q.Query.Get(q.ids[5]))
 }
 
-// Get7 returns the first queried component for the current query position
+// Get7 returns the seventh queried component for the current query iterator position.
 func (q *Query7[A, B, C, D, E, F, G]) Get7() *G {
 	return (*G)(q.Query.Get(q.ids[6]))
 }
 
 //////////////////////////////////////////////////////////////////////////
 
-// Filter8 builds a [Query8] query
+// Filter8 is a helper for building [Query8] query iterators.
 type Filter8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
 	include  []reflect.Type
 	optional []reflect.Type
@@ -783,7 +785,7 @@ type Filter8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
 	compiled compiledQuery
 }
 
-// NewQuery8 creates a generic filter for two components.
+// NewFilter8 creates a generic Filter8 for eight components.
 //
 // See also [ecs.World.Query].
 func NewFilter8[A any, B any, C any, D any, E any, F any, G any, H any]() *Filter8[A, B, C, D, E, F, G, H] {
@@ -839,7 +841,7 @@ func (q *Filter8[A, B, C, D, E, F, G, H]) Filter(w *ecs.World) ecs.MaskPair {
 	return q.compiled.Filter()
 }
 
-// Query8 is a generic query iterator for two components.
+// Query8 is a generic query iterator for eight components.
 //
 // Create one with [NewFilter8] and [Filter8.Query]
 type Query8[A any, B any, C any, D any, E any, F any, G any, H any] struct {
@@ -852,42 +854,42 @@ func (q *Query8[A, B, C, D, E, F, G, H]) GetAll() (ecs.Entity, *A, *B, *C, *D, *
 	return q.Entity(), (*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]))
 }
 
-// Get1 returns the first queried component for the current query position
+// Get1 returns the first queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get1() *A {
 	return (*A)(q.Query.Get(q.ids[0]))
 }
 
-// Get2 returns the first queried component for the current query position
+// Get2 returns the second queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get2() *B {
 	return (*B)(q.Query.Get(q.ids[1]))
 }
 
-// Get3 returns the first queried component for the current query position
+// Get3 returns the third queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get3() *C {
 	return (*C)(q.Query.Get(q.ids[2]))
 }
 
-// Get4 returns the first queried component for the current query position
+// Get4 returns the fourth queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get4() *D {
 	return (*D)(q.Query.Get(q.ids[3]))
 }
 
-// Get5 returns the first queried component for the current query position
+// Get5 returns the fifth queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get5() *E {
 	return (*E)(q.Query.Get(q.ids[4]))
 }
 
-// Get6 returns the first queried component for the current query position
+// Get6 returns the sixth queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get6() *F {
 	return (*F)(q.Query.Get(q.ids[5]))
 }
 
-// Get7 returns the first queried component for the current query position
+// Get7 returns the seventh queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get7() *G {
 	return (*G)(q.Query.Get(q.ids[6]))
 }
 
-// Get8 returns the first queried component for the current query position
+// Get8 returns the eighth queried component for the current query iterator position.
 func (q *Query8[A, B, C, D, E, F, G, H]) Get8() *H {
 	return (*H)(q.Query.Get(q.ids[7]))
 }


Test patch:
diff --git a/benchmark/add/arche_test.go b/benchmark/add/arche_test.go
index 447e603..ac1f948 100644
--- a/benchmark/add/arche_test.go
+++ b/benchmark/add/arche_test.go
@@ -26,10 +26,11 @@ func addArcheGeneric(b *testing.B, count int) {
 	for i := 0; i < b.N; i++ {
 		b.StopTimer()
 		world := ecs.NewWorld()
+		mut := generic.NewMutate2[position, rotation](&world)
 		b.StartTimer()
 
 		for i := 0; i < count; i++ {
-			_, _, _ = generic.NewEntity2[position, rotation](&world)
+			_, _, _ = mut.NewEntity()
 		}
 	}
 }
diff --git a/ecs/query_test.go b/ecs/query_test.go
index 9cb5631..f663423 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -42,6 +42,7 @@ 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
diff --git a/generic/functions_test.go b/generic/functions_test.go
deleted file mode 100644
index c61dd78..0000000
--- a/generic/functions_test.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package generic
-
-import (
-	"testing"
-
-	"github.com/mlange-42/arche/ecs"
-	"github.com/stretchr/testify/assert"
-)
-
-//lint:ignore U1000 test type
-type testStruct0 struct{ val int8 }
-
-//lint:ignore U1000 test type
-type testStruct1 struct{ val int32 }
-
-//lint:ignore U1000 test type
-type testStruct2 struct {
-	val  int32
-	val2 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct3 struct {
-	val  int32
-	val2 int32
-	val3 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct4 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct5 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-	val5 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct6 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-	val5 int32
-	val6 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct7 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-	val5 int32
-	val6 int32
-	val7 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct8 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-	val5 int32
-	val6 int32
-	val7 int32
-	val8 int32
-}
-
-//lint:ignore U1000 test type
-type testStruct9 struct {
-	val  int32
-	val2 int32
-	val3 int32
-	val4 int32
-	val5 int32
-	val6 int32
-	val7 int32
-	val8 int32
-	val9 int32
-}
-
-func registerAll(w *ecs.World) []ecs.ID {
-	_ = testStruct0{}
-	_ = testStruct1{}
-	_ = testStruct2{}
-	_ = testStruct3{}
-	_ = testStruct4{}
-	_ = testStruct5{}
-	_ = testStruct6{}
-	_ = testStruct7{}
-	_ = testStruct8{}
-	_ = testStruct9{}
-
-	ids := make([]ecs.ID, 10)
-	ids[0] = ecs.ComponentID[testStruct0](w)
-	ids[1] = ecs.ComponentID[testStruct1](w)
-	ids[2] = ecs.ComponentID[testStruct2](w)
-	ids[3] = ecs.ComponentID[testStruct3](w)
-	ids[4] = ecs.ComponentID[testStruct4](w)
-	ids[5] = ecs.ComponentID[testStruct5](w)
-	ids[6] = ecs.ComponentID[testStruct6](w)
-	ids[7] = ecs.ComponentID[testStruct7](w)
-	ids[8] = ecs.ComponentID[testStruct8](w)
-	ids[9] = ecs.ComponentID[testStruct9](w)
-
-	return ids
-}
-
-func TestGenericAddRemove(t *testing.T) {
-	w := ecs.NewWorld()
-	get := NewMap[testStruct0](&w)
-
-	e0 := w.NewEntity()
-
-	Add1[testStruct0](&w, e0)
-	_ = get.Has(e0)
-	_ = get.Get(e0)
-	Remove1[testStruct0](&w, e0)
-
-	Add2[testStruct0, testStruct1](&w, e0)
-	Remove2[testStruct0, testStruct1](&w, e0)
-
-	Add3[testStruct0, testStruct1, testStruct2](&w, e0)
-	Remove3[testStruct0, testStruct1, testStruct2](&w, e0)
-
-	Add4[testStruct0, testStruct1, testStruct2, testStruct3](&w, e0)
-	Remove4[testStruct0, testStruct1, testStruct2, testStruct3](&w, e0)
-
-	Add5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w, e0)
-	Remove5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w, e0)
-}
-
-func TestGenericAssignRemove(t *testing.T) {
-	w := ecs.NewWorld()
-
-	e0 := w.NewEntity()
-
-	Assign1(&w, e0, &testStruct0{})
-	Remove1[testStruct0](&w, e0)
-
-	Assign2(&w, e0, &testStruct0{}, &testStruct1{})
-	Remove2[testStruct0, testStruct1](&w, e0)
-
-	Assign3(&w, e0, &testStruct0{}, &testStruct1{}, &testStruct2{})
-	Remove3[testStruct0, testStruct1, testStruct2](&w, e0)
-
-	Assign4(&w, e0, &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{})
-	Remove4[testStruct0, testStruct1, testStruct2, testStruct3](&w, e0)
-
-	Assign5(&w, e0, &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, &testStruct4{})
-	Remove5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w, e0)
-}
-
-func TestGenericNewEntity(t *testing.T) {
-	w := ecs.NewConfig().WithCapacityIncrement(32).Build()
-
-	id0 := ecs.ComponentID[testStruct0](&w)
-	id1 := ecs.ComponentID[testStruct1](&w)
-	id2 := ecs.ComponentID[testStruct2](&w)
-
-	e0 := w.NewEntity()
-	e1, _, _, _ := NewEntity3[testStruct0, testStruct1, testStruct2](&w)
-	e2, _, _, _ := NewEntityWith3(&w, &testStruct0{1}, &testStruct1{2}, &testStruct2{3, 4})
-	e3 := w.NewEntityWith()
-
-	assert.Equal(t, ecs.NewBitMask(), w.Mask(e0))
-	assert.Equal(t, ecs.NewBitMask(id0, id1, id2), w.Mask(e1))
-	assert.Equal(t, ecs.NewBitMask(id0, id1, id2), w.Mask(e2))
-	assert.Equal(t, ecs.NewBitMask(), w.Mask(e3))
-
-	s0 := (*testStruct0)(w.Get(e2, id0))
-	s1 := (*testStruct1)(w.Get(e2, id1))
-	s2 := (*testStruct2)(w.Get(e2, id2))
-
-	assert.Equal(t, &testStruct0{1}, s0)
-	assert.Equal(t, &testStruct1{2}, s1)
-	assert.Equal(t, &testStruct2{3, 4}, s2)
-
-	w.RemEntity(e0)
-	w.RemEntity(e1)
-	w.RemEntity(e2)
-	w.RemEntity(e3)
-
-	for i := 0; i < 35; i++ {
-		e, _, _, _ := NewEntityWith3(&w,
-			&testStruct0{int8(i + 1)},
-			&testStruct1{int32(i + 2)},
-			&testStruct2{int32(i + 3), int32(i + 4)},
-		)
-
-		s0 := (*testStruct0)(w.Get(e, id0))
-		s1 := (*testStruct1)(w.Get(e, id1))
-		s2 := (*testStruct2)(w.Get(e, id2))
-
-		assert.Equal(t, &testStruct0{int8(i + 1)}, s0)
-		assert.Equal(t, &testStruct1{int32(i + 2)}, s1)
-		assert.Equal(t, &testStruct2{int32(i + 3), int32(i + 4)}, s2)
-	}
-}
-
-func TestGenericNewEntityAll(t *testing.T) {
-	w := ecs.NewWorld()
-
-	e, _ := NewEntity1[testStruct0](&w)
-	Remove1[testStruct0](&w, e)
-	w.Remove(e)
-
-	e, _, _ = NewEntity2[testStruct0, testStruct1](&w)
-	Remove2[testStruct0, testStruct1](&w, e)
-	w.Remove(e)
-
-	e, _, _, _ = NewEntity3[testStruct0, testStruct1, testStruct2](&w)
-	Remove3[testStruct0, testStruct1, testStruct2](&w, e)
-	w.Remove(e)
-
-	e, _, _, _, _ = NewEntity4[testStruct0, testStruct1, testStruct2, testStruct3](&w)
-	Remove4[testStruct0, testStruct1, testStruct2, testStruct3](&w, e)
-	w.Remove(e)
-
-	e, _, _, _, _, _ = NewEntity5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w)
-	Remove5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w, e)
-	w.Remove(e)
-}
-
-func TestGenericNewEntityWithAll(t *testing.T) {
-	w := ecs.NewWorld()
-
-	e, _ := NewEntityWith1(&w, &testStruct0{})
-	Remove1[testStruct0](&w, e)
-	w.Remove(e)
-
-	e, _, _ = NewEntityWith2(&w, &testStruct0{}, &testStruct1{})
-	Remove2[testStruct0, testStruct1](&w, e)
-	w.Remove(e)
-
-	e, _, _, _ = NewEntityWith3(&w, &testStruct0{}, &testStruct1{}, &testStruct2{})
-	Remove3[testStruct0, testStruct1, testStruct2](&w, e)
-	w.Remove(e)
-
-	e, _, _, _, _ = NewEntityWith4(&w, &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{})
-	Remove4[testStruct0, testStruct1, testStruct2, testStruct3](&w, e)
-	w.Remove(e)
-
-	e, _, _, _, _, _ = NewEntityWith5(&w, &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{}, &testStruct4{})
-	Remove5[testStruct0, testStruct1, testStruct2, testStruct3, testStruct4](&w, e)
-	w.Remove(e)
-}
diff --git a/generic/generate/header.gotxt b/generic/generate/query_header.gotxt
similarity index 100%
rename from generic/generate/header.gotxt
rename to generic/generate/query_header.gotxt
diff --git a/generic/map_test.go b/generic/map_test.go
index 3df0e61..d3a18b1 100644
--- a/generic/map_test.go
+++ b/generic/map_test.go
@@ -10,12 +10,13 @@ import (
 func TestGenericMap(t *testing.T) {
 	w := ecs.NewWorld()
 	get := NewMap[testStruct0](&w)
+	mut := NewMutate1[testStruct0](&w)
 
 	assert.Equal(t, ecs.ComponentID[testStruct0](&w), get.ID())
 
 	e0 := w.NewEntity()
 
-	Add1[testStruct0](&w, e0)
+	mut.Add(e0)
 	has := get.Has(e0)
 	_ = get.Get(e0)
 	assert.True(t, has)
diff --git a/generic/mutate_test.go b/generic/mutate_test.go
new file mode 100644
index 0000000..1b99a0d
--- /dev/null
+++ b/generic/mutate_test.go
@@ -0,0 +1,290 @@
+package generic
+
+import (
+	"testing"
+
+	"github.com/mlange-42/arche/ecs"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMutate1(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate1[testStruct0](&w)
+	map0 := NewMap[testStruct0](&w)
+
+	e, s0 := mut.NewEntity()
+	assert.NotNil(t, s0)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+
+	s0 = mut.Add(e)
+	assert.NotNil(t, s0)
+
+	e = w.NewEntity()
+	_ = mut.Assign(e, &testStruct0{})
+	assert.True(t, map0.Has(e))
+
+	e, _ = mut.NewEntityWith(&testStruct0{})
+	assert.True(t, map0.Has(e))
+}
+
+func TestMutate2(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate2[testStruct0, testStruct1](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1 := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1 = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _ = mut.Assign(e, &testStruct0{}, &testStruct1{})
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _ = mut.NewEntityWith(&testStruct0{}, &testStruct1{})
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate3(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate3[
+		testStruct0, testStruct1, testStruct2,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate4(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate4[
+		testStruct0, testStruct1, testStruct2, testStruct3,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate5(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate5[
+		testStruct0, testStruct1, testStruct2, testStruct3,
+		testStruct4,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _, _, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _, _, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate6(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate6[
+		testStruct0, testStruct1, testStruct2, testStruct3,
+		testStruct4, testStruct5,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _, _, _, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _, _, _, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _, _, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate7(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate7[
+		testStruct0, testStruct1, testStruct2, testStruct3,
+		testStruct4, testStruct5, testStruct6,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _, _, _, _, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _, _, _, _, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _, _, _, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _, _, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
+
+func TestMutate8(t *testing.T) {
+	w := ecs.NewWorld()
+	registerAll(&w)
+
+	mut := NewMutate8[
+		testStruct0, testStruct1, testStruct2, testStruct3,
+		testStruct4, testStruct5, testStruct6, testStruct7,
+	](&w)
+	map0 := NewMap[testStruct0](&w)
+	map1 := NewMap[testStruct1](&w)
+
+	e, s0, s1, _, _, _, _, _, _ := mut.NewEntity()
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	mut.Remove(e)
+	assert.False(t, map0.Has(e))
+	assert.False(t, map1.Has(e))
+
+	s0, s1, _, _, _, _, _, _ = mut.Add(e)
+	assert.NotNil(t, s0)
+	assert.NotNil(t, s1)
+
+	e = w.NewEntity()
+	_, _, _, _, _, _, _, _ = mut.Assign(e,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+
+	e, _, _, _, _, _, _, _, _ = mut.NewEntityWith(
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{},
+	)
+	assert.True(t, map0.Has(e))
+	assert.True(t, map1.Has(e))
+}
diff --git a/generic/query_test.go b/generic/query_test.go
index ea8482e..e321284 100644
--- a/generic/query_test.go
+++ b/generic/query_test.go
@@ -622,3 +622,112 @@ func TestQueryGeneric(t *testing.T) {
 	}
 	assert.Equal(t, count, cnt)
 }
+
+func registerAll(w *ecs.World) []ecs.ID {
+	_ = testStruct0{}
+	_ = testStruct1{}
+	_ = testStruct2{}
+	_ = testStruct3{}
+	_ = testStruct4{}
+	_ = testStruct5{}
+	_ = testStruct6{}
+	_ = testStruct7{}
+	_ = testStruct8{}
+	_ = testStruct9{}
+
+	ids := make([]ecs.ID, 10)
+	ids[0] = ecs.ComponentID[testStruct0](w)
+	ids[1] = ecs.ComponentID[testStruct1](w)
+	ids[2] = ecs.ComponentID[testStruct2](w)
+	ids[3] = ecs.ComponentID[testStruct3](w)
+	ids[4] = ecs.ComponentID[testStruct4](w)
+	ids[5] = ecs.ComponentID[testStruct5](w)
+	ids[6] = ecs.ComponentID[testStruct6](w)
+	ids[7] = ecs.ComponentID[testStruct7](w)
+	ids[8] = ecs.ComponentID[testStruct8](w)
+	ids[9] = ecs.ComponentID[testStruct9](w)
+
+	return ids
+}
+
+//lint:ignore U1000 test type
+type testStruct0 struct{ val int8 }
+
+//lint:ignore U1000 test type
+type testStruct1 struct{ val int32 }
+
+//lint:ignore U1000 test type
+type testStruct2 struct {
+	val  int32
+	val2 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct3 struct {
+	val  int32
+	val2 int32
+	val3 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct4 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct5 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+	val5 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct6 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+	val5 int32
+	val6 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct7 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+	val5 int32
+	val6 int32
+	val7 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct8 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+	val5 int32
+	val6 int32
+	val7 int32
+	val8 int32
+}
+
+//lint:ignore U1000 test type
+type testStruct9 struct {
+	val  int32
+	val2 int32
+	val3 int32
+	val4 int32
+	val5 int32
+	val6 int32
+	val7 int32
+	val8 int32
+	val9 int32
+}