117 lines
4.6 KiB
C
117 lines
4.6 KiB
C
#include <instancing.h>
|
|
#include <stdio.h>
|
|
|
|
/* Instancing is the ability of queries to iterate results with fields that have
|
|
* different numbers of elements. The term "instancing" is borrowed from
|
|
* graphics APIs, where it means reusing the same data for multiple "instances".
|
|
*
|
|
* Query instancing works in much the same way. By default queries match all
|
|
* components on the same entity. It is however possible to request data from
|
|
* other entities, like getting the Position from the entity's parent.
|
|
*
|
|
* Instancing refers to the ability of queries to iterate components for
|
|
* multiple entities while at the same time providing "instanced" components,
|
|
* which are always provided one element at a time.
|
|
*
|
|
* Instancing is often used in combination with parent-child relationships and
|
|
* prefabs, but is applicable to any kind of query where some of the terms are
|
|
* matched on N entities, and some on a single entity.
|
|
*
|
|
* By default queries are not instanced, which means that if a result contains
|
|
* mixed fields, entities will be iterated one by one instead of in batches.
|
|
* This is safer, as code doesn't have to do anything different for owned and
|
|
* shared fields, but does come at a performance penalty.
|
|
*
|
|
* The each() iterator function always uses an instanced iterator under the
|
|
* hood. This is transparent to the application, but improves performance. For
|
|
* this reason using each() can be faster than using uninstanced iter().
|
|
*/
|
|
|
|
typedef struct {
|
|
double x, y;
|
|
} Position, Velocity;
|
|
|
|
int main(int argc, char *argv[]) {
|
|
ecs_world_t *world = ecs_init_w_args(argc, argv);
|
|
|
|
ECS_COMPONENT(world, Position);
|
|
ECS_COMPONENT(world, Velocity);
|
|
|
|
// Create a query for Position, Velocity. We'll create a few entities that
|
|
// have Velocity as owned and shared component.
|
|
ecs_query_t *q = ecs_query(world, {
|
|
.filter = {
|
|
.terms = {
|
|
// Position must always be owned by the entity
|
|
{ .id = ecs_id(Position), .src.flags = EcsSelf },
|
|
{ .id = ecs_id(Velocity) } // Velocity may be shared (default)
|
|
},
|
|
.instanced = true
|
|
}
|
|
});
|
|
|
|
// Create a prefab with Velocity. Prefabs are not matched with queries.
|
|
ecs_entity_t prefab = ecs_new_prefab(world, "p");
|
|
ecs_set(world, prefab, Velocity, {1, 2});
|
|
|
|
// Create a few entities that own Position & share Velocity from the prefab.
|
|
ecs_entity_t e1 = ecs_new_entity(world, "e1");
|
|
ecs_add_pair(world, e1, EcsIsA, prefab);
|
|
ecs_set(world, e1, Position, {10, 20});
|
|
|
|
ecs_entity_t e2 = ecs_new_entity(world, "e2");
|
|
ecs_add_pair(world, e2, EcsIsA, prefab);
|
|
ecs_set(world, e2, Position, {10, 20});
|
|
|
|
// Create a few entities that own all components
|
|
ecs_entity_t e3 = ecs_new_entity(world, "e3");
|
|
ecs_set(world, e3, Position, {10, 20});
|
|
ecs_set(world, e3, Velocity, {3, 4});
|
|
|
|
ecs_entity_t e4 = ecs_new_entity(world, "e4");
|
|
ecs_set(world, e4, Position, {10, 20});
|
|
ecs_set(world, e4, Velocity, {4, 5});
|
|
|
|
// Iterate the instanced query. Note how when a query is instanced, it needs
|
|
// to check whether a field is owned or not in order to know how to access
|
|
// it. In the case of an owned field it is iterated as an array, whereas
|
|
// in the case of a shared field, it is accessed as a pointer.
|
|
ecs_iter_t it = ecs_query_iter(world, q);
|
|
while (ecs_query_next(&it)) {
|
|
Position *p = ecs_field(&it, Position, 1);
|
|
Velocity *v = ecs_field(&it, Velocity, 2);
|
|
|
|
// Check if Velocity is owned, in which case it's accessed as array.
|
|
// Position will always be owned, since we set the term to Self.
|
|
if (ecs_field_is_self(&it, 2)) { // Velocity is term 2
|
|
printf("Velocity is owned\n");
|
|
for (int i = 0; i < it.count; i ++) {
|
|
// If Velocity is shared, access the field as an array.
|
|
p[i].x += v[i].x;
|
|
p[i].y += v[i].y;
|
|
printf("%s: {%f, %f}\n", ecs_get_name(it.world, it.entities[i]),
|
|
p[i].x, p[i].y);
|
|
}
|
|
} else {
|
|
printf("Velocity is shared\n");
|
|
for (int i = 0; i < it.count; i ++) {
|
|
// If Velocity is shared, access the field as a pointer.
|
|
p[i].x += v->x;
|
|
p[i].y += v->y;
|
|
printf("%s: {%f, %f}\n", ecs_get_name(it.world, it.entities[i]),
|
|
p[i].x, p[i].y);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ecs_fini(world);
|
|
|
|
// Output
|
|
// Velocity is shared
|
|
// e1: {11.000000, 22.000000}
|
|
// e2: {11.000000, 22.000000}
|
|
// Velocity is owned
|
|
// e3: {13.000000, 24.000000}
|
|
// e4: {14.000000, 25.000000}
|
|
}
|