Skip to content

Commit 36d0572

Browse files
committed
#575 add ability to access flecs::iter from each, add more C++ query examples
1 parent 39ffd94 commit 36d0572

File tree

25 files changed

+637
-23
lines changed

25 files changed

+637
-23
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef AD_HOC_QUERY_H
2+
#define AD_HOC_QUERY_H
3+
4+
/* This generated file contains includes for project dependencies */
5+
#include "ad_hoc_query/bake_config.h"
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
#ifdef __cplusplus
12+
}
13+
#endif
14+
15+
#endif
16+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
)
3+
(.)
4+
.|.
5+
| |
6+
_.--| |--._
7+
.-'; ;`-'& ; `&.
8+
\ & ; & &_/
9+
|"""---...---"""|
10+
\ | | | | | | | /
11+
`---.|.|.|.---'
12+
13+
* This file is generated by bake.lang.c for your convenience. Headers of
14+
* dependencies will automatically show up in this file. Include bake_config.h
15+
* in your main project file. Do not edit! */
16+
17+
#ifndef AD_HOC_QUERY_BAKE_CONFIG_H
18+
#define AD_HOC_QUERY_BAKE_CONFIG_H
19+
20+
/* Headers of public dependencies */
21+
#include <flecs.h>
22+
23+
#endif
24+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"id": "ad_hoc_query",
3+
"type": "application",
4+
"value": {
5+
"use": [
6+
"flecs"
7+
],
8+
"language": "c++",
9+
"public": false
10+
}
11+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <ad_hoc_query.h>
2+
#include <iostream>
3+
4+
struct Position {
5+
double x, y;
6+
};
7+
8+
struct Velocity {
9+
double x, y;
10+
};
11+
12+
int main(int, char *[]) {
13+
flecs::world ecs;
14+
15+
// Create a few test entities for a Position, Velocity query
16+
ecs.entity("e1")
17+
.set<Position>({10, 20})
18+
.set<Velocity>({1, 2});
19+
20+
ecs.entity("e2")
21+
.set<Position>({10, 20})
22+
.set<Velocity>({3, 4});
23+
24+
// This entity will not match as it does not have Position, Velocity
25+
ecs.entity("e3")
26+
.set<Position>({10, 20});
27+
28+
// Ad hoc queries are bit slower to iterate than flecs::query, but are
29+
// faster to create, and in most cases require no allocations. Under the
30+
// hood this API uses flecs::filter, which can be used directly for more
31+
// complex queries.
32+
ecs.each([](flecs::entity e, Position& p, Velocity& v) {
33+
p.x += v.x;
34+
p.y += v.y;
35+
std::cout << e.name() << ": {" << p.x << ", " << p.y << "}\n";
36+
});
37+
}

examples/cpp/queries/basics/src/main.cpp

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,42 @@ int main(int, char *[]) {
2929
ecs.entity("e3")
3030
.set<Position>({10, 20});
3131

32-
// Iterate entities matching the query
32+
33+
// The next lines show the different ways in which a query can be iterated.
34+
// Note how the 'const' qualifier matches the query template arguments.
35+
36+
// The each() function iterates each entity individually and accepts an
37+
// entity argument plus arguments for each query component:
3338
q.each([](flecs::entity e, Position& p, const Velocity& v) {
3439
p.x += v.x;
3540
p.y += v.y;
3641
std::cout << e.name() << ": {" << p.x << ", " << p.y << "}\n";
3742
});
3843

39-
// Ad hoc queries are bit slower to iterate than queries, but are faster to
40-
// create as they don't require setting up a cache.
41-
ecs.each([](flecs::entity e, Position& p, Velocity& v) {
44+
// You can omit the flecs::entity argument if it's not needed:
45+
q.each([](Position& p, const Velocity& v) {
4246
p.x += v.x;
4347
p.y += v.y;
44-
std::cout << e.name() << ": {" << p.x << ", " << p.y << "}\n";
48+
std::cout << "{" << p.x << ", " << p.y << "}\n";
49+
});
50+
51+
// Each also accepts flecs::iter + index (for the iterated entity) arguemnts
52+
// currently being iterated. A flecs::iter has lots of information on what
53+
// is being iterated, which is demonstrated in the "iter" example.
54+
q.each([](flecs::iter& it, size_t i, Position& p, const Velocity& v) {
55+
p.x += v.x;
56+
p.y += v.y;
57+
std::cout << it.entity(i).name() << ": {" << p.x << ", " << p.y << "}\n";
58+
});
59+
60+
// Iter is a bit more verbose, but allows for more control over how entities
61+
// are iterated as it provides multiple entities in the same callback.
62+
q.iter([](flecs::iter& it, Position *p, const Velocity *v) {
63+
for (auto i : it) {
64+
p[i].x += v[i].x;
65+
p[i].y += v[i].y;
66+
std::cout << it.entity(i).name() <<
67+
": {" << p[i].x << ", " << p[i].y << "}\n";
68+
}
4569
});
4670
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef INSTANCING_H
2+
#define INSTANCING_H
3+
4+
/* This generated file contains includes for project dependencies */
5+
#include "instancing/bake_config.h"
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
#ifdef __cplusplus
12+
}
13+
#endif
14+
15+
#endif
16+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
)
3+
(.)
4+
.|.
5+
| |
6+
_.--| |--._
7+
.-'; ;`-'& ; `&.
8+
\ & ; & &_/
9+
|"""---...---"""|
10+
\ | | | | | | | /
11+
`---.|.|.|.---'
12+
13+
* This file is generated by bake.lang.c for your convenience. Headers of
14+
* dependencies will automatically show up in this file. Include bake_config.h
15+
* in your main project file. Do not edit! */
16+
17+
#ifndef INSTANCING_BAKE_CONFIG_H
18+
#define INSTANCING_BAKE_CONFIG_H
19+
20+
/* Headers of public dependencies */
21+
#include <flecs.h>
22+
23+
#endif
24+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"id": "instancing",
3+
"type": "application",
4+
"value": {
5+
"use": [
6+
"flecs"
7+
],
8+
"language": "c++",
9+
"public": false
10+
}
11+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include <instancing.h>
2+
#include <iostream>
3+
4+
/* Instancing is the ability of queries to iterate results with fields that have
5+
* different numbers of elements. The term "instancing" is borrowed from
6+
* graphics APIs, where it means reusing the same data for multiple "instances".
7+
*
8+
* Query instancing works in much the same way. By default queries match all
9+
* components on the same entity. It is however possible to request data from
10+
* other entities, like getting the Position from the entity's parent.
11+
*
12+
* Instancing refers to the ability of queries to iterate components for
13+
* multiple entities while at the same time providing "instanced" components,
14+
* which are always provided one element at a time.
15+
*
16+
* Instancing is often used in combination with parent-child relationships and
17+
* prefabs, but is applicable to any kind of query where some of the terms are
18+
* matched on N entities, and some on a single entity.
19+
*
20+
* By default queries are not instanced, which means that if a result contains
21+
* mixed fields, entities will be iterated one by one instead of in batches.
22+
* This is safer, as code doesn't have to do anything different for owned and
23+
* shared fields, but does come at a performance penalty.
24+
*
25+
* The each() iterator function always uses an instanced iterator under the
26+
* hood. This is transparent to the application, but improves performance. For
27+
* this reason using each() can be faster than using uninstanced iter().
28+
*/
29+
30+
struct Position {
31+
double x, y;
32+
};
33+
34+
struct Velocity {
35+
double x, y;
36+
};
37+
38+
int main(int, char *[]) {
39+
flecs::world ecs;
40+
41+
// Create a query for Position, Velocity. We'll create a few entities that
42+
// have Velocity as owned and shared component.
43+
auto q = ecs.query_builder<Position, const Velocity>()
44+
.arg(1).set(flecs::Self) // Ensure Position is never shared
45+
.instanced() // create instanced query
46+
.build();
47+
48+
// Create a prefab with Velocity. Prefabs are not matched with queries.
49+
auto prefab = ecs.prefab("p")
50+
.set<Velocity>({1, 2});
51+
52+
// Create a few entities that own Position & share Velocity from the prefab.
53+
ecs.entity("e1").is_a(prefab)
54+
.set<Position>({10, 20});
55+
56+
ecs.entity("e2").is_a(prefab)
57+
.set<Position>({10, 20});
58+
59+
// Create a few entities that own all components
60+
ecs.entity("e3")
61+
.set<Position>({10, 20})
62+
.set<Velocity>({3, 4});
63+
64+
ecs.entity("e4")
65+
.set<Position>({10, 20})
66+
.set<Velocity>({3, 4});
67+
68+
69+
// Iterate the instanced query. Note how when a query is instanced, it needs
70+
// to check whether a field is owned or not in order to know how to access
71+
// it. In the case of an owned field it is iterated as an array, whereas
72+
// in the case of a shared field, it is accessed as a pointer.
73+
q.iter([](flecs::iter& it, Position *p, const Velocity *v) {
74+
75+
// Check if Velocity is owned, in which case it's accessed as array.
76+
// Position will always be owned, since we set the term to Self.
77+
if (it.is_owned(2)) { // Velocity is term 2
78+
std::cout << "Velocity is owned" << std::endl;
79+
for (auto i : it) {
80+
p[i].x += v[i].x;
81+
p[i].y += v[i].y;
82+
std::cout << it.entity(i).name() <<
83+
": {" << p[i].x << ", " << p[i].y << "}\n";
84+
}
85+
86+
// If Velocity is shared, access the field as a pointer.
87+
} else {
88+
std::cout << "Velocity is shared" << std::endl;
89+
for (auto i : it) {
90+
p[i].x += v->x;
91+
p[i].y += v->y;
92+
std::cout << it.entity(i).name() <<
93+
": {" << p[i].x << ", " << p[i].y << "}\n";
94+
}
95+
}
96+
});
97+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#ifndef ITER_H
2+
#define ITER_H
3+
4+
/* This generated file contains includes for project dependencies */
5+
#include "iter/bake_config.h"
6+
7+
#ifdef __cplusplus
8+
extern "C" {
9+
#endif
10+
11+
#ifdef __cplusplus
12+
}
13+
#endif
14+
15+
#endif
16+

0 commit comments

Comments
 (0)