class MySystem : SystemBase() {
private val enemies = CachedQuery.create { has(Enemy.id, Health.id) }
override fun execute() {
// Returns same List instance if membership unchanged
// Only does native work when entities added/removed
for (entity in enemies.get()) {
val health = entity.getComponent<Health>()
// getComponent uses existing timestamp caching
}
}
}
private val activeEnemies = CachedQuery.create {
where { has(Enemy.id) }.filter { by(Enemy.statusData).isEqualTo(Status.ACTIVE) }
}
true to include the entity, false to exclude it.
private val activeEnemies = CachedQuery.create { has(Enemy.id) }
.onAdd { entity ->
// Called when entity first matches the query
// Return true to include in result set
entity.getComponent<Enemy>().isActive
}
.onUpdate { entity ->
// Called when a tracked component changes on a matching entity
// Return true to keep in result set, false to remove
entity.getComponent<Enemy>().isActive
}
.onDelete { entity ->
// Called when entity is removed from result set (destroyed or no longer matches)
cleanupEnemyState(entity)
}
class CachedQuery
contains
(
entity
)
|
Check if an entity is in the result set.
Signature
fun contains(entity: Entity): Boolean Parameters Returns
Boolean
true if the entity is currently in the cached result set.
|
count
()
|
Returns the number of entities matching the query. O(1) after first CachedQuery.get call.
Signature
fun count(): Int Returns
Int
|
dispose
()
|
Unregisters the delete listener and destroys the filter handle. Call this when the CachedQuery is no longer needed.
Note: With weak reference semantics, calling dispose() is optional for the delete listener. If the CachedQuery goes out of scope without calling dispose(), the listener will automatically stop receiving notifications and be cleaned up during the next delete event.
However, the filter handle holds native memory that will only be released when dispose() is called or when the CachedQuery is garbage collected with a finalizer.
Signature
fun dispose() |
get
()
|
Get entities matching the query.
Returns the SAME List instance if membership hasn't changed since the last call. When entities are added or removed, callbacks are invoked and a new list is returned.
Cost breakdown:
Signature
fun get(): List<Entity> Returns
List
|
invalidate
()
|
Force a full re-query on the next CachedQuery.get call.
Use this after major scene changes where incremental updates may be less efficient than a full re-query.
Signature
fun invalidate() |
isEmpty
()
|
Returns true if no entities match the query.
Signature
fun isEmpty(): Boolean Returns
Boolean
|
onAdd
(
callback
)
|
Register a callback invoked when an entity first matches the query.
The callback receives the entity and should return:
If no callback is registered, all matching entities are included.
Example:
CachedQuery.create { has(Enemy.id) }
.onAdd { entity ->
val enemy = entity.getComponent<Enemy>()
enemy.isActive // Only include active enemies
}
Signature
fun onAdd(callback: (Entity) -> Boolean): CachedQuery Parameters
callback:
Function1
|
onDelete
(
callback
)
|
Register a callback invoked when an entity is removed from the result set.
This is called when:
Use this for cleanup logic like releasing resources associated with the entity.
Example:
CachedQuery.create { has(Enemy.id) }
.onDelete { entity ->
enemyStateMap.remove(entity)
Log.d("Enemy", "Removed from tracking: ${entity.id}")
}
Signature
fun onDelete(callback: (Entity) -> Unit): CachedQuery Parameters
callback:
Function1
|
onUpdate
(
callback
)
|
Register a callback invoked when a tracked component changes on a matching entity.
The callback receives the entity and should return:
If no callback is registered, entities remain in the set as long as they have the required components.
Note: This is called when component data changes, not when components are added/removed. For component add/remove, see CachedQuery.onAdd and CachedQuery.onDelete.
Example:
CachedQuery.create { has(Enemy.id, Health.id) }
.onUpdate { entity ->
val health = entity.getComponent<Health>()
health.current 0 // Remove dead enemies from result set
}
Signature
fun onUpdate(callback: (Entity) -> Boolean): CachedQuery Parameters
callback:
Function1
|
create
(
dm
, initializer
)
|
Creates a new CachedQuery using a restricted DSL that requires where { ... }.
All CachedQuery usage must go through where, and filter is optional after where.
Example:
// Basic usage (no filter)
val enemies = CachedQuery.create { where { has(Enemy.id, Health.id) } }
val players = CachedQuery.create { where { has(Player.id) and has(Transform.id) } }
// With filter
val activeEnemies = CachedQuery.create {
where { has(Enemy.id) }.filter { by(Enemy.statusData).isEqualTo(Status.ACTIVE) }
}
Signature
fun create(dm: DataModel = EntityContext.getDataModel()!!, initializer: CachedQueryBuilder.() -> FilteredCachedQueryNode): CachedQuery Parameters
initializer:
Function1
|