Repository: mlange-42/arche
Pull request #65 (2023-02-13 14:17:44)

Get entity bitmask
* Add the possibility to get an entity's mask from world and queries


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1abf2db..0a4f462 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 
 * Generic queries support optional, additional and excluded components (#53, #56, #58, #59, #60, #63)
 * Logic filters for complex queries (#54, #58, #61)
+* `Query` and `World` have a method `Mask(Entity)` to access archetype masks (#65)
 
 ### Other
 
diff --git a/ecs/query.go b/ecs/query.go
index 98b43b8..10608fd 100644
--- a/ecs/query.go
+++ b/ecs/query.go
@@ -117,12 +117,12 @@ type queryIter struct {
 	lockBit   uint8
 }
 
-// Has returns whether the current [Entity] has the given component
+// Has returns whether the current [Entity] has the given component.
 func (q *queryIter) Has(comp ID) bool {
 	return q.archetype.Has(comp)
 }
 
-// Get returns the pointer to the given component at the iterator's current [Entity]
+// Get returns the pointer to the given component at the iterator's current [Entity].
 func (q *queryIter) Get(comp ID) unsafe.Pointer {
 	return q.archetype.Get(comp)
 }
@@ -132,6 +132,13 @@ func (q *queryIter) Entity() Entity {
 	return q.archetype.Entity()
 }
 
+// Mask returns the archetype [BitMask] for the [Entity] at the iterator's current position.
+//
+// Can be used for fast checks of the entity composition, e.g. using a [Filter].
+func (q *queryIter) Mask() BitMask {
+	return q.archetype.Archetype.Mask
+}
+
 // Close closes the Query and unlocks the world.
 //
 // Automatically called when iteration finishes.
diff --git a/ecs/world.go b/ecs/world.go
index 0f0c572..a735e94 100644
--- a/ecs/world.go
+++ b/ecs/world.go
@@ -138,6 +138,13 @@ func (w *World) Has(entity Entity, comp ID) bool {
 	return w.entities[entity.id].arch.HasComponent(comp)
 }
 
+// Mask returns the archetype [BitMask] for the given [Entity].
+//
+// Can be used for fast checks of the entity composition, e.g. using a [Filter].
+func (w *World) Mask(entity Entity) BitMask {
+	return w.entities[entity.id].arch.Mask
+}
+
 // Add adds components to an [Entity].
 //
 // Panics when called on a locked world or for an already removed entity.
diff --git a/examples/base/main.go b/examples/base/main.go
index f795776..c4886e5 100644
--- a/examples/base/main.go
+++ b/examples/base/main.go
@@ -54,12 +54,12 @@ func main() {
 	// Time loop.
 	for t := 0; t < 1000; t++ {
 		// Get a fresh query iterator.
-		q := world.Query(filter)
+		query := world.Query(filter)
 		// Iterate it
-		for q.Next() {
+		for query.Next() {
 			// Component access through a Query.
-			pos := (*Position)(q.Get(posID))
-			vel := (*Position)(q.Get(velID))
+			pos := (*Position)(query.Get(posID))
+			vel := (*Velocity)(query.Get(velID))
 			// Update component fields.
 			pos.X += vel.X
 			pos.Y += vel.Y
diff --git a/examples/filter/main.go b/examples/filter/main.go
index 3708001..8bfa420 100644
--- a/examples/filter/main.go
+++ b/examples/filter/main.go
@@ -80,7 +80,7 @@ func main() {
 		for query.Next() {
 			// Component access through a Query.
 			pos := (*Position)(query.Get(posID))
-			vel := (*Position)(query.Get(velID))
+			vel := (*Velocity)(query.Get(velID))
 			// Update component fields.
 			pos.X += vel.X
 			pos.Y += vel.Y
diff --git a/examples/generic/main.go b/examples/generic/main.go
index caf67ab..76bf332 100644
--- a/examples/generic/main.go
+++ b/examples/generic/main.go
@@ -55,11 +55,11 @@ func main() {
 	// Time loop.
 	for t := 0; t < 1000; t++ {
 		// Get a fresh query iterator.
-		q := filter.Query(&world)
+		query := filter.Query(&world)
 		// Iterate it.
-		for q.Next() {
+		for query.Next() {
 			// Component access through a Query.
-			pos, vel := q.GetAll()
+			pos, vel := query.GetAll()
 			// Update component fields.
 			pos.X += vel.X
 			pos.Y += vel.Y


Test patch:
diff --git a/ecs/query_test.go b/ecs/query_test.go
index 1b7fdf2..9cb5631 100644
--- a/ecs/query_test.go
+++ b/ecs/query_test.go
@@ -41,6 +41,7 @@ func TestQuery(t *testing.T) {
 		ent := q.Entity()
 		pos := (*position)(q.Get(posID))
 		rot := (*rotation)(q.Get(rotID))
+		assert.Equal(t, w.Mask(ent), q.Mask())
 		_ = ent
 		_ = pos
 		_ = rot
diff --git a/ecs/world_test.go b/ecs/world_test.go
index c434bdb..6293b5f 100644
--- a/ecs/world_test.go
+++ b/ecs/world_test.go
@@ -65,6 +65,9 @@ func TestWorldComponents(t *testing.T) {
 	w.Add(e2, posID, rotID)
 	assert.Equal(t, 3, w.archetypes.Len())
 
+	assert.Equal(t, All(posID).BitMask, w.Mask(e0))
+	assert.Equal(t, All(posID, rotID).BitMask, w.Mask(e1))
+
 	w.Remove(e2, posID)
 
 	maskNone := NewBitMask()