Repository: mlange-42/arche
Pull request #152 (2023-03-24 10:11:38)

Optional queries from batch-creation
* Methods like `NewEntities` don't return a query.
* For creation with a query, there is `NewEntitiesWith` etc.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 641c192..8e40074 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,40 +4,41 @@
 
 ### Breaking changes
 
-* Generic mappers do no longer return all components when creating entities or components (!145)
-* Resources API moved out of the world, to a helper to get by `World.Resources()` (!150)
+* Generic mappers do no longer return all components when creating entities or components (#145)
+* Resources API moved out of the world, to a helper to get by `World.Resources()` (#150)
 
 ### Features
 
-* Adds method `World.Reset()`, to allow for more efficient systematic simulations (!138)
-* Adds `World.Batch()` helper for performing optimized batch-creation and batch-removal of entities (!149)
-* Adds method `Mask.Exact()` to create a filter matching an exact component composition (!149)
-* Generic mappers (`Map1`, ...) have methods `NewEntities`, `NewEntitiesWith` and `RemoveEntities` for batch operations (!151)
+* Adds method `World.Reset()`, to allow for more efficient systematic simulations (#138)
+* Adds `World.Batch()` helper for performing optimized batch-creation and batch-removal of entities (#149)
+* Adds method `Mask.Exact()` to create a filter matching an exact component composition (#149)
+* Generic mappers (`Map1`, ...) have methods `NewEntities`, `NewEntitiesWith` and `RemoveEntities` for batch operations (#151)
+* Batch-creation methods (ID-based and generic) have variants like `NewEntitiesQuery` that return a query over the created entities (#152)
 
 ### Performance
 
-* Speedup of archetype mask checks by 10% by checking mask before empty archetype (!139)
-* Speedup of generic queries and mappers to come closer to ID-based access (!144)
-* Speedup of archetype mask checks by casting filter interface to concrete type when possible (!148)
+* Speedup of archetype mask checks by 10% by checking mask before empty archetype (#139)
+* Speedup of generic queries and mappers to come closer to ID-based access (#144)
+* Speedup of archetype mask checks by casting filter interface to concrete type when possible (#148)
 
 ### Bugfixes
 
-* Archetype storage buffers are "zeroed" when removing entities, to allow GC on pointers and slices in components (!147)
+* Archetype storage buffers are "zeroed" when removing entities, to allow GC on pointers and slices in components (#147)
 
 ## [[v0.5.1]](https://github.com/mlange-42/arche/compare/v0.5.0...v0.5.1)
 
 ### Performance
 
-* Speedup of archetype access by 5-10% by merging storages into archetypes (!137)
+* Speedup of archetype access by 5-10% by merging storages into archetypes (#137)
 
 ### Documentation
 
-* Document all private functions, types and methods  (!136)
-* Adds a section  and plot on benchmarks against other Go ECS implementations to the README (!138)
+* Document all private functions, types and methods  (#136)
+* Adds a section  and plot on benchmarks against other Go ECS implementations to the README (#138)
 
 ### Other
 
-* Internal code refactoring (!136)
+* Internal code refactoring (#136)
   * Move method `nextArchetype` from `World` to `Query`.
   * Remove internal type `queryIter`.
   * Move repetitive pointer copying code in `storage` into a private method.
@@ -49,34 +50,34 @@ Feature release. Does not break v0.4.x projects.
 
 ### Features
 
-* The World handles ECS resources, i.e. component-like global data (!132)
-* Generic access to world resources (!132)
+* The World handles ECS resources, i.e. component-like global data (#132)
+* Generic access to world resources (#132)
 
 ### Documentation
 
-* Adds an example for the use of resources (!132)
+* Adds an example for the use of resources (#132)
 
 ## [[v0.4.6]](https://github.com/mlange-42/arche/compare/v0.4.5...v0.4.6)
 
 ### Performance
 
-* Speedup archetype access by 10%, by elimination of bounds checks (!126)
-* Speedup entity access from queries by 50% by using a component storage for them (!131)
-* Minor optimizations of component storage (!128)
+* Speedup archetype access by 10%, by elimination of bounds checks (#126)
+* Speedup entity access from queries by 50% by using a component storage for them (#131)
+* Minor optimizations of component storage (#128)
 
 ### Documentation
 
-* Adds an example to demonstrate how to implement classical ECS systems (!129)
+* Adds an example to demonstrate how to implement classical ECS systems (#129)
 
 ## [[v0.4.5]](https://github.com/mlange-42/arche/compare/v0.4.4...v0.4.5)
 
 ### Features
 
-* Adds memory per entity to archetype stats (!124)
+* Adds memory per entity to archetype stats (#124)
 
 ### Other
 
-* Adds benchmarks of Arche vs. Array of Structs (AoS) and Array of Pointers (AoP), for different memory per entity and number of entities (!123)
+* Adds benchmarks of Arche vs. Array of Structs (AoS) and Array of Pointers (AoP), for different memory per entity and number of entities (#123)
 
 ## [[v0.4.4]](https://github.com/mlange-42/arche/compare/v0.4.3...v0.4.4)
 
diff --git a/ecs/batch.go b/ecs/batch.go
index 381c61e..03dfd74 100644
--- a/ecs/batch.go
+++ b/ecs/batch.go
@@ -10,29 +10,57 @@ type Batch struct {
 // NewEntities creates the given number of entities [Entity].
 // The given component types are added to all entities.
 //
+// See also [Batch.NewEntitiesQuery].
+//
+// 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.Map1], etc.
+func (b *Batch) NewEntities(count int, comps ...ID) {
+	b.world.newEntities(count, comps...)
+}
+
+// NewEntitiesQuery creates the given number of entities [Entity].
+// The given component types are added to all entities.
+//
 // Returns a [Query] for iterating the created entities.
 // The [Query] must be closed if it is not used!
+// See also [Batch.NewEntities].
 //
 // 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.Map1], etc.
-func (b *Batch) NewEntities(count int, comps ...ID) Query {
-	return b.world.newEntities(count, comps...)
+func (b *Batch) NewEntitiesQuery(count int, comps ...ID) Query {
+	return b.world.newEntitiesQuery(count, comps...)
 }
 
 // NewEntitiesWith creates the given number of entities [Entity].
 // The given component values are assigned to all entity.
 //
+// See also [Batch.NewEntitiesWithQuery].
+//
+// 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.Map1], etc.
+func (b *Batch) NewEntitiesWith(count int, comps ...Component) {
+	b.world.newEntitiesWith(count, comps...)
+}
+
+// NewEntitiesWithQuery creates the given number of entities [Entity].
+// The given component values are assigned to all entity.
+//
 // Returns a [Query] for iterating the created entities.
 // The [Query] must be closed if it is not used!
+// See also [Batch.NewEntitiesWith].
 //
 // 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.Map1], etc.
-func (b *Batch) NewEntitiesWith(count int, comps ...Component) Query {
-	return b.world.newEntitiesWith(count, comps...)
+func (b *Batch) NewEntitiesWithQuery(count int, comps ...Component) Query {
+	return b.world.newEntitiesWithQuery(count, comps...)
 }
 
 // RemoveEntities removes and recycles all entities matching a filter.
diff --git a/ecs/world.go b/ecs/world.go
index 2bbd95b..276b6a4 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -120,7 +120,13 @@ func (w *World) NewEntity(comps ...ID) Entity {
 	return entity
 }
 
-func (w *World) newEntities(count int, comps ...ID) Query {
+func (w *World) newEntitiesQuery(count int, comps ...ID) Query {
+	arch, startIdx := w.newEntities(count, comps...)
+	lock := w.lock()
+	return newArchQuery(w, lock, arch, startIdx)
+}
+
+func (w *World) newEntities(count int, comps ...ID) (*archetype, uint32) {
 	w.checkLocked()
 
 	if count < 1 {
@@ -146,8 +152,7 @@ func (w *World) newEntities(count int, comps ...ID) Query {
 		w.unlock(lock)
 	}
 
-	lock := w.lock()
-	return newArchQuery(w, lock, arch, startIdx)
+	return arch, startIdx
 }
 
 // NewEntityWith returns a new or recycled [Entity].
@@ -187,7 +192,13 @@ func (w *World) NewEntityWith(comps ...Component) Entity {
 	return entity
 }
 
-func (w *World) newEntitiesWith(count int, comps ...Component) Query {
+func (w *World) newEntitiesWithQuery(count int, comps ...Component) Query {
+	arch, startIdx := w.newEntitiesWith(count, comps...)
+	lock := w.lock()
+	return newArchQuery(w, lock, arch, startIdx)
+}
+
+func (w *World) newEntitiesWith(count int, comps ...Component) (*archetype, uint32) {
 	w.checkLocked()
 
 	if count < 1 {
@@ -231,8 +242,7 @@ func (w *World) newEntitiesWith(count int, comps ...Component) Query {
 		w.unlock(lock)
 	}
 
-	lock := w.lock()
-	return newArchQuery(w, lock, arch, startIdx)
+	return arch, startIdx
 }
 
 // RemoveEntity removes and recycles an [Entity].
diff --git a/generic/generate/map.go.txt b/generic/generate/map.go.txt
index c093c2a..163a6d3 100644
--- a/generic/generate/map.go.txt
+++ b/generic/generate/map.go.txt
@@ -35,9 +35,17 @@ func (m *Map{{ .Index }}{{ .Types }}) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map{{ .Index }}'s components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map{{ .Index }}{{ .Types }}) NewEntities(count int) Query{{ .Index }}{{ .Types }} {
-	query := m.world.Batch().NewEntities(count, {{ .IDList }})
+// See also [Map{{ .Index }}.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map{{ .Index }}{{ .Types }}) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, {{ .IDList }})
+}
+
+// NewEntities creates entities with the Map{{ .Index }}'s components.
+// It returns a [Query{{ .Index }}] over the new entities.
+//
+// See also [Map{{ .Index }}.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map{{ .Index }}{{ .Types }}) NewEntitiesQuery(count int) Query{{ .Index }}{{ .Types }} {
+	query := m.world.Batch().NewEntitiesQuery(count, {{ .IDList }})
 	return Query{{ .Index }}{{ .Types }}{
 		Query: query,
 		{{ .IDAssign2 }}
@@ -57,9 +65,17 @@ func (m *Map{{ .Index }}{{ .Types }}) NewEntityWith({{ .Arguments }}) ecs.Entity
 
 // NewEntitiesWith creates entities with the Map{{ .Index }}'s components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map{{ .Index }}{{ .Types }}) NewEntitiesWith(count int, {{ .Arguments }}) Query{{ .Index }}{{ .Types }} {
-	query := m.world.Batch().NewEntitiesWith(count, {{ .Components }})
+// See also [Map{{ .Index }}.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map{{ .Index }}{{ .Types }}) NewEntitiesWith(count int, {{ .Arguments }}) {
+	m.world.Batch().NewEntitiesWith(count, {{ .Components }})
+}
+
+// NewEntitiesWithQuery creates entities with the Map{{ .Index }}'s components, using the supplied values.
+// It returns a [Query{{ .Index }}] over the new entities.
+//
+// See also [Map{{ .Index }}.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map{{ .Index }}{{ .Types }}) NewEntitiesWithQuery(count int, {{ .Arguments }}) Query{{ .Index }}{{ .Types }} {
+	query := m.world.Batch().NewEntitiesWithQuery(count, {{ .Components }})
 	return Query{{ .Index }}{{ .Types }}{
 		Query: query,
 		{{ .IDAssign2 }}
diff --git a/generic/map_generated.go b/generic/map_generated.go
index da8a4e9..9175d36 100644
--- a/generic/map_generated.go
+++ b/generic/map_generated.go
@@ -42,9 +42,17 @@ func (m *Map1[A]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map1's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map1[A]) NewEntities(count int) Query1[A] {
-	query := m.world.Batch().NewEntities(count, m.id0)
+// See also [Map1.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map1[A]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0)
+}
+
+// NewEntities creates entities with the Map1's components.
+// It returns a [Query1] over the new entities.
+//
+// See also [Map1.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map1[A]) NewEntitiesQuery(count int) Query1[A] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0)
 	return Query1[A]{
 		Query: query,
 		id0:   m.id0,
@@ -63,9 +71,17 @@ func (m *Map1[A]) NewEntityWith(a *A) ecs.Entity {
 
 // NewEntitiesWith creates entities with the Map1's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map1[A]) NewEntitiesWith(count int, a *A) Query1[A] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a})
+// See also [Map1.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map1[A]) NewEntitiesWith(count int, a *A) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a})
+}
+
+// NewEntitiesWithQuery creates entities with the Map1's components, using the supplied values.
+// It returns a [Query1] over the new entities.
+//
+// See also [Map1.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map1[A]) NewEntitiesWithQuery(count int, a *A) Query1[A] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a})
 	return Query1[A]{
 		Query: query,
 		id0:   m.id0,
@@ -97,8 +113,6 @@ func (m *Map1[A]) Remove(entity ecs.Entity) {
 
 // RemoveEntities removes all components from the world that match the Map1's components.
 //
-// The argument determines whether entities are matched exactly, or if they may contain further components.
-//
 // See also [ecs.World.NewEntityWith].
 func (m *Map1[A]) RemoveEntities(exact bool) {
 	if exact {
@@ -148,9 +162,17 @@ func (m *Map2[A, B]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map2's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map2[A, B]) NewEntities(count int) Query2[A, B] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1)
+// See also [Map2.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map2[A, B]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1)
+}
+
+// NewEntities creates entities with the Map2's components.
+// It returns a [Query2] over the new entities.
+//
+// See also [Map2.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map2[A, B]) NewEntitiesQuery(count int) Query2[A, B] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1)
 	return Query2[A, B]{
 		Query: query,
 		id0:   m.id0,
@@ -171,9 +193,19 @@ func (m *Map2[A, B]) NewEntityWith(a *A, b *B) ecs.Entity {
 
 // NewEntitiesWith creates entities with the Map2's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map2[A, B]) NewEntitiesWith(count int, a *A, b *B) Query2[A, B] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map2.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map2[A, B]) NewEntitiesWith(count int, a *A, b *B) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map2's components, using the supplied values.
+// It returns a [Query2] over the new entities.
+//
+// See also [Map2.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map2[A, B]) NewEntitiesWithQuery(count int, a *A, b *B) Query2[A, B] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 	)
 	return Query2[A, B]{
@@ -261,9 +293,17 @@ func (m *Map3[A, B, C]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map3's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map3[A, B, C]) NewEntities(count int) Query3[A, B, C] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2)
+// See also [Map3.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map3[A, B, C]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2)
+}
+
+// NewEntities creates entities with the Map3's components.
+// It returns a [Query3] over the new entities.
+//
+// See also [Map3.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map3[A, B, C]) NewEntitiesQuery(count int) Query3[A, B, C] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2)
 	return Query3[A, B, C]{
 		Query: query,
 		id0:   m.id0,
@@ -286,9 +326,20 @@ func (m *Map3[A, B, C]) NewEntityWith(a *A, b *B, c *C) ecs.Entity {
 
 // NewEntitiesWith creates entities with the Map3's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map3[A, B, C]) NewEntitiesWith(count int, a *A, b *B, c *C) Query3[A, B, C] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map3.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map3[A, B, C]) NewEntitiesWith(count int, a *A, b *B, c *C) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map3's components, using the supplied values.
+// It returns a [Query3] over the new entities.
+//
+// See also [Map3.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map3[A, B, C]) NewEntitiesWithQuery(count int, a *A, b *B, c *C) Query3[A, B, C] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 	)
@@ -382,9 +433,17 @@ func (m *Map4[A, B, C, D]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map4's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map4[A, B, C, D]) NewEntities(count int) Query4[A, B, C, D] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3)
+// See also [Map4.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map4[A, B, C, D]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3)
+}
+
+// NewEntities creates entities with the Map4's components.
+// It returns a [Query4] over the new entities.
+//
+// See also [Map4.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map4[A, B, C, D]) NewEntitiesQuery(count int) Query4[A, B, C, D] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2, m.id3)
 	return Query4[A, B, C, D]{
 		Query: query,
 		id0:   m.id0,
@@ -409,9 +468,21 @@ func (m *Map4[A, B, C, D]) NewEntityWith(a *A, b *B, c *C, d *D) ecs.Entity {
 
 // NewEntitiesWith creates entities with the Map4's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map4[A, B, C, D]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D) Query4[A, B, C, D] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map4.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map4[A, B, C, D]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+		ecs.Component{ID: m.id3, Comp: d},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map4's components, using the supplied values.
+// It returns a [Query4] over the new entities.
+//
+// See also [Map4.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map4[A, B, C, D]) NewEntitiesWithQuery(count int, a *A, b *B, c *C, d *D) Query4[A, B, C, D] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 		ecs.Component{ID: m.id3, Comp: d},
@@ -511,9 +582,17 @@ func (m *Map5[A, B, C, D, E]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map5's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map5[A, B, C, D, E]) NewEntities(count int) Query5[A, B, C, D, E] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4)
+// See also [Map5.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map5[A, B, C, D, E]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4)
+}
+
+// NewEntities creates entities with the Map5's components.
+// It returns a [Query5] over the new entities.
+//
+// See also [Map5.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map5[A, B, C, D, E]) NewEntitiesQuery(count int) Query5[A, B, C, D, E] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2, m.id3, m.id4)
 	return Query5[A, B, C, D, E]{
 		Query: query,
 		id0:   m.id0,
@@ -540,9 +619,22 @@ func (m *Map5[A, B, C, D, E]) NewEntityWith(a *A, b *B, c *C, d *D, e *E) ecs.En
 
 // NewEntitiesWith creates entities with the Map5's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map5[A, B, C, D, E]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E) Query5[A, B, C, D, E] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map5.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map5[A, B, C, D, E]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+		ecs.Component{ID: m.id3, Comp: d},
+		ecs.Component{ID: m.id4, Comp: e},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map5's components, using the supplied values.
+// It returns a [Query5] over the new entities.
+//
+// See also [Map5.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map5[A, B, C, D, E]) NewEntitiesWithQuery(count int, a *A, b *B, c *C, d *D, e *E) Query5[A, B, C, D, E] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 		ecs.Component{ID: m.id3, Comp: d},
@@ -648,9 +740,17 @@ func (m *Map6[A, B, C, D, E, F]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map6's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map6[A, B, C, D, E, F]) NewEntities(count int) Query6[A, B, C, D, E, F] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5)
+// See also [Map6.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map6[A, B, C, D, E, F]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5)
+}
+
+// NewEntities creates entities with the Map6's components.
+// It returns a [Query6] over the new entities.
+//
+// See also [Map6.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map6[A, B, C, D, E, F]) NewEntitiesQuery(count int) Query6[A, B, C, D, E, F] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5)
 	return Query6[A, B, C, D, E, F]{
 		Query: query,
 		id0:   m.id0,
@@ -679,9 +779,23 @@ func (m *Map6[A, B, C, D, E, F]) NewEntityWith(a *A, b *B, c *C, d *D, e *E, f *
 
 // NewEntitiesWith creates entities with the Map6's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map6[A, B, C, D, E, F]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F) Query6[A, B, C, D, E, F] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map6.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map6[A, B, C, D, E, F]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+		ecs.Component{ID: m.id3, Comp: d},
+		ecs.Component{ID: m.id4, Comp: e},
+		ecs.Component{ID: m.id5, Comp: f},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map6's components, using the supplied values.
+// It returns a [Query6] over the new entities.
+//
+// See also [Map6.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map6[A, B, C, D, E, F]) NewEntitiesWithQuery(count int, a *A, b *B, c *C, d *D, e *E, f *F) Query6[A, B, C, D, E, F] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 		ecs.Component{ID: m.id3, Comp: d},
@@ -793,9 +907,17 @@ func (m *Map7[A, B, C, D, E, F, G]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map7's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map7[A, B, C, D, E, F, G]) NewEntities(count int) Query7[A, B, C, D, E, F, G] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6)
+// See also [Map7.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map7[A, B, C, D, E, F, G]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6)
+}
+
+// NewEntities creates entities with the Map7's components.
+// It returns a [Query7] over the new entities.
+//
+// See also [Map7.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map7[A, B, C, D, E, F, G]) NewEntitiesQuery(count int) Query7[A, B, C, D, E, F, G] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6)
 	return Query7[A, B, C, D, E, F, G]{
 		Query: query,
 		id0:   m.id0,
@@ -826,9 +948,24 @@ func (m *Map7[A, B, C, D, E, F, G]) NewEntityWith(a *A, b *B, c *C, d *D, e *E,
 
 // NewEntitiesWith creates entities with the Map7's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map7[A, B, C, D, E, F, G]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G) Query7[A, B, C, D, E, F, G] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map7.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map7[A, B, C, D, E, F, G]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+		ecs.Component{ID: m.id3, Comp: d},
+		ecs.Component{ID: m.id4, Comp: e},
+		ecs.Component{ID: m.id5, Comp: f},
+		ecs.Component{ID: m.id6, Comp: g},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map7's components, using the supplied values.
+// It returns a [Query7] over the new entities.
+//
+// See also [Map7.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map7[A, B, C, D, E, F, G]) NewEntitiesWithQuery(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G) Query7[A, B, C, D, E, F, G] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 		ecs.Component{ID: m.id3, Comp: d},
@@ -946,9 +1083,17 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewEntity() ecs.Entity {
 
 // NewEntities creates entities with the Map8's components.
 //
-// See also [ecs.Batch.NewEntities].
-func (m *Map8[A, B, C, D, E, F, G, H]) NewEntities(count int) Query8[A, B, C, D, E, F, G, H] {
-	query := m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7)
+// See also [Map8.NewEntitiesQuery] and [ecs.Batch.NewEntities].
+func (m *Map8[A, B, C, D, E, F, G, H]) NewEntities(count int) {
+	m.world.Batch().NewEntities(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7)
+}
+
+// NewEntities creates entities with the Map8's components.
+// It returns a [Query8] over the new entities.
+//
+// See also [Map8.NewEntities] and [ecs.Batch.NewEntitiesQuery].
+func (m *Map8[A, B, C, D, E, F, G, H]) NewEntitiesQuery(count int) Query8[A, B, C, D, E, F, G, H] {
+	query := m.world.Batch().NewEntitiesQuery(count, m.id0, m.id1, m.id2, m.id3, m.id4, m.id5, m.id6, m.id7)
 	return Query8[A, B, C, D, E, F, G, H]{
 		Query: query,
 		id0:   m.id0,
@@ -981,9 +1126,25 @@ func (m *Map8[A, B, C, D, E, F, G, H]) NewEntityWith(a *A, b *B, c *C, d *D, e *
 
 // NewEntitiesWith creates entities with the Map8's components, using the supplied values.
 //
-// See also [ecs.Batch.NewEntitiesWith].
-func (m *Map8[A, B, C, D, E, F, G, H]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H) Query8[A, B, C, D, E, F, G, H] {
-	query := m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+// See also [Map8.NewEntitiesWithQuery] and [ecs.Batch.NewEntitiesWith].
+func (m *Map8[A, B, C, D, E, F, G, H]) NewEntitiesWith(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H) {
+	m.world.Batch().NewEntitiesWith(count, ecs.Component{ID: m.id0, Comp: a},
+		ecs.Component{ID: m.id1, Comp: b},
+		ecs.Component{ID: m.id2, Comp: c},
+		ecs.Component{ID: m.id3, Comp: d},
+		ecs.Component{ID: m.id4, Comp: e},
+		ecs.Component{ID: m.id5, Comp: f},
+		ecs.Component{ID: m.id6, Comp: g},
+		ecs.Component{ID: m.id7, Comp: h},
+	)
+}
+
+// NewEntitiesWithQuery creates entities with the Map8's components, using the supplied values.
+// It returns a [Query8] over the new entities.
+//
+// See also [Map8.NewEntitiesWith] and [ecs.Batch.NewEntitiesWithQuery].
+func (m *Map8[A, B, C, D, E, F, G, H]) NewEntitiesWithQuery(count int, a *A, b *B, c *C, d *D, e *E, f *F, g *G, h *H) Query8[A, B, C, D, E, F, G, H] {
+	query := m.world.Batch().NewEntitiesWithQuery(count, ecs.Component{ID: m.id0, Comp: a},
 		ecs.Component{ID: m.id1, Comp: b},
 		ecs.Component{ID: m.id2, Comp: c},
 		ecs.Component{ID: m.id3, Comp: d},


Test patch:
diff --git a/ecs/batch_test.go b/ecs/batch_test.go
index e436cb2..064bbc4 100644
--- a/ecs/batch_test.go
+++ b/ecs/batch_test.go
@@ -20,9 +20,37 @@ func TestBatch(t *testing.T) {
 
 	b := world.Batch()
 
-	q := b.NewEntities(100, posID)
+	b.NewEntities(100, posID)
+	b.NewEntitiesWith(100, comps...)
+
+	filter1 := All(posID).Exact()
+	q := world.Query(&filter1)
+	assert.Equal(t, 100, q.Count())
+	q.Close()
+
+	filter2 := All(posID, rotID).Exact()
+	q = world.Query(&filter2)
+	assert.Equal(t, 100, q.Count())
+	q.Close()
+}
+
+func TestBatchQuery(t *testing.T) {
+	world := NewWorld(NewConfig().WithCapacityIncrement(16))
+	world.SetListener(func(e EntityEvent) {})
+
+	posID := ComponentID[position](&world)
+	rotID := ComponentID[rotation](&world)
+
+	comps := []Component{
+		{ID: posID, Comp: &position{100, 200}},
+		{ID: rotID, Comp: &rotation{300}},
+	}
+
+	b := world.Batch()
+
+	q := b.NewEntitiesQuery(100, posID)
 	q.Close()
-	q = b.NewEntitiesWith(100, comps...)
+	q = b.NewEntitiesWithQuery(100, comps...)
 	q.Close()
 
 	filter1 := All(posID).Exact()
diff --git a/ecs/world_test.go b/ecs/world_test.go
index 7924726..87e70d9 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -368,9 +368,9 @@ func TestWorldNewEntities(t *testing.T) {
 
 	world.NewEntity(posID, rotID)
 
-	assert.Panics(t, func() { world.newEntities(0, posID, rotID) })
+	assert.Panics(t, func() { world.newEntitiesQuery(0, posID, rotID) })
 
-	query := world.newEntities(100, posID, rotID)
+	query := world.newEntitiesQuery(100, posID, rotID)
 	assert.Equal(t, 100, query.Count())
 
 	cnt := 0
@@ -394,7 +394,7 @@ func TestWorldNewEntities(t *testing.T) {
 
 	world.Reset()
 
-	query = world.newEntities(100, posID, rotID)
+	query = world.newEntitiesQuery(100, posID, rotID)
 	assert.Equal(t, 100, query.Count())
 
 	entities := make([]Entity, query.Count())
@@ -408,7 +408,7 @@ func TestWorldNewEntities(t *testing.T) {
 	for _, e := range entities {
 		world.RemoveEntity(e)
 	}
-	query = world.newEntities(100, posID, rotID)
+	query = world.newEntitiesQuery(100, posID, rotID)
 	query.Close()
 }
 
@@ -426,11 +426,11 @@ func TestWorldNewEntitiesWith(t *testing.T) {
 
 	world.NewEntity(posID, rotID)
 
-	assert.Panics(t, func() { world.newEntitiesWith(0, comps...) })
-	query := world.newEntitiesWith(1)
+	assert.Panics(t, func() { world.newEntitiesWithQuery(0, comps...) })
+	query := world.newEntitiesWithQuery(1)
 	query.Close()
 
-	query = world.newEntitiesWith(100, comps...)
+	query = world.newEntitiesWithQuery(100, comps...)
 	assert.Equal(t, 100, query.Count())
 
 	cnt := 0
@@ -456,7 +456,7 @@ func TestWorldNewEntitiesWith(t *testing.T) {
 
 	world.Reset()
 
-	query = world.newEntitiesWith(100,
+	query = world.newEntitiesWithQuery(100,
 		Component{ID: posID, Comp: &position{100, 200}},
 		Component{ID: rotID, Comp: &rotation{300}},
 	)
@@ -476,11 +476,11 @@ func TestWorldRemoveEntities(t *testing.T) {
 	posID := ComponentID[position](&world)
 	rotID := ComponentID[rotation](&world)
 
-	query := world.newEntities(100, posID)
+	query := world.newEntitiesQuery(100, posID)
 	assert.Equal(t, 100, query.Count())
 	query.Close()
 
-	query = world.newEntities(100, posID, rotID)
+	query = world.newEntitiesQuery(100, posID, rotID)
 	assert.Equal(t, 100, query.Count())
 	query.Close()
 
@@ -937,8 +937,7 @@ func BenchmarkNewEntitiesBatch_10_000_New(b *testing.B) {
 		posID := ComponentID[position](&world)
 		velID := ComponentID[velocity](&world)
 
-		q := world.newEntities(10000, posID, velID)
-		q.Close()
+		world.newEntities(10000, posID, velID)
 	}
 }
 
@@ -976,8 +975,7 @@ func BenchmarkNewEntitiesBatch_10_000_Reset(b *testing.B) {
 
 	for i := 0; i < b.N; i++ {
 		world.Reset()
-		q := world.newEntities(10000, posID, velID)
-		q.Close()
+		world.newEntities(10000, posID, velID)
 	}
 }
 
@@ -990,7 +988,7 @@ func BenchmarkRemoveEntities_10_000(b *testing.B) {
 		velID := ComponentID[velocity](&world)
 
 		entities := make([]Entity, 10000)
-		q := world.newEntities(10000, posID, velID)
+		q := world.newEntitiesQuery(10000, posID, velID)
 
 		cnt := 0
 		for q.Next() {
@@ -1014,7 +1012,7 @@ func BenchmarkRemoveEntitiesBatch_10_000(b *testing.B) {
 		posID := ComponentID[position](&world)
 		velID := ComponentID[velocity](&world)
 
-		q := world.newEntities(10000, posID, velID)
+		q := world.newEntitiesQuery(10000, posID, velID)
 		q.Close()
 		b.StartTimer()
 		world.removeEntities(All(posID, velID))
diff --git a/generic/map_generated_test.go b/generic/map_generated_test.go
index 05ce85e..4475847 100644
--- a/generic/map_generated_test.go
+++ b/generic/map_generated_test.go
@@ -35,9 +35,12 @@ func TestMap1Generated(t *testing.T) {
 	s0 = mut.Get(e)
 	assert.NotNil(t, s0)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2, &testStruct0{})
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2, &testStruct0{})
+	q = mut.NewEntitiesWithQuery(2, &testStruct0{})
 	q.Close()
 	mut.RemoveEntities(true)
 	mut.RemoveEntities(false)
@@ -78,9 +81,12 @@ func TestMap2Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2, &testStruct0{}, &testStruct1{})
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{},
 	)
 	q.Close()
@@ -129,9 +135,12 @@ func TestMap3Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2, &testStruct0{}, &testStruct1{}, &testStruct2{})
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{},
 	)
 	q.Close()
@@ -180,9 +189,12 @@ func TestMap4Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2, &testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{})
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
 	)
 	q.Close()
@@ -234,9 +246,15 @@ func TestMap5Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{},
+	)
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
 		&testStruct4{},
 	)
@@ -289,9 +307,15 @@ func TestMap6Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{},
+	)
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
 		&testStruct4{}, &testStruct5{},
 	)
@@ -344,9 +368,15 @@ func TestMap7Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{},
+	)
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
 		&testStruct4{}, &testStruct5{}, &testStruct6{},
 	)
@@ -399,9 +429,15 @@ func TestMap8Generated(t *testing.T) {
 	assert.NotNil(t, s0)
 	assert.NotNil(t, s1)
 
-	q := mut.NewEntities(2)
+	mut.NewEntities(2)
+	mut.NewEntitiesWith(2,
+		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
+		&testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{},
+	)
+
+	q := mut.NewEntitiesQuery(2)
 	q.Close()
-	q = mut.NewEntitiesWith(2,
+	q = mut.NewEntitiesWithQuery(2,
 		&testStruct0{}, &testStruct1{}, &testStruct2{}, &testStruct3{},
 		&testStruct4{}, &testStruct5{}, &testStruct6{}, &testStruct7{},
 	)