diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 282589148..3d0966da1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -339,41 +339,21 @@ jobs: bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_MODULE bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_MODULE - - name: FLECS_SNAPSHOT - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SNAPSHOT - - name: FLECS_STATS run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_STATS bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_STATS - - name: FLECS_PARSER - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PARSER - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PARSER - - - name: FLECS_PLECS + - name: FLECS_SCRIPT run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_PLECS - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_PLECS + bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_SCRIPT + bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_SCRIPT - name: FLECS_META run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META - - name: FLECS_META_C - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_META_C - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_META_C - - - name: FLECS_EXPR - run: | - bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_EXPR - bake rebuild --strict --cfg release -D FLECS_CUSTOM_BUILD -D FLECS_EXPR - - name: FLECS_JSON run: | bake rebuild --strict -D FLECS_CUSTOM_BUILD -D FLECS_JSON @@ -488,8 +468,11 @@ jobs: - name: build flecs run: bake --strict - - name: test api - run: bake run test/api -- -j 8 + - name: test core + run: bake run test/core -- -j 8 + + - name: test query + run: bake run test/query -- -j 8 - name: test addons run: bake run test/addons -- -j 8 @@ -497,6 +480,9 @@ jobs: - name: test meta run: bake run test/meta -- -j 8 + - name: test script + run: bake run test/script -- -j 8 + - name: test collections run: bake run test/collections -- -j 8 @@ -550,7 +536,7 @@ jobs: run: bake --strict - name: test c++ - run: bake run test/cpp_api -- -j 8 + run: bake run test/cpp -- -j 8 test-cpp-macos: needs: [build-macos] @@ -590,7 +576,7 @@ jobs: run: bake --strict - name: test c++ - run: bake run test/cpp_api -- -j 8 + run: bake run test/cpp -- -j 8 test-windows: needs: build-windows @@ -613,8 +599,8 @@ jobs: - name: build flecs run: bake/bake - - name: test api - run: bake/bake run test\api -- -j 8 + - name: test core + run: bake/bake run test\core -- -j 8 - name: test addons run: bake/bake run test\addons -- -j 8 @@ -622,11 +608,14 @@ jobs: - name: test meta run: bake/bake run test\meta -- -j 8 + - name: test script + run: bake/bake run test\script -- -j 8 + - name: test collections run: bake/bake run test\collections -- -j 8 - name: test c++ - run: bake/bake run test\cpp_api -- -j 8 + run: bake/bake run test\cpp -- -j 8 test-msys: runs-on: windows-latest @@ -669,10 +658,13 @@ jobs: bake/bake setup - name: build flecs - run: bake/bake --strict + run: bake/bake --strict --trace + + - name: test core + run: bake/bake run test/core -- -j 8 - - name: test api - run: bake/bake run test/api -- -j 8 + - name: test query + run: bake/bake run test/query -- -j 8 - name: test addons run: bake/bake run test/addons -- -j 8 @@ -680,13 +672,16 @@ jobs: - name: test meta run: bake/bake run test/meta -- -j 8 + - name: test script + run: bake/bake run test/script -- -j 8 + - name: test collections run: bake/bake run test/collections -- -j 8 - name: test c++ - run: bake/bake run test/cpp_api -- -j 8 + run: bake/bake run test/cpp -- -j 8 - test-sanitized-api: + test-sanitized-core: needs: [ build-linux ] runs-on: ${{ matrix.os }} timeout-minutes: 30 @@ -708,7 +703,31 @@ jobs: - name: run tests run: | - bake run test/api --cfg sanitize -- -j 8 + bake run test/core --cfg sanitize -- -j 8 + + test-sanitized-query: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-20.04 ] + + steps: + - uses: actions/checkout@v4 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/query --cfg sanitize -- -j 8 test-sanitized-addons: needs: [ build-linux ] @@ -758,6 +777,30 @@ jobs: run: | bake run test/meta --cfg sanitize -- -j 8 + test-sanitized-script: + needs: [ build-linux ] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-20.04 ] + + steps: + - uses: actions/checkout@v4 + - name: install bake + run: | + git clone https://github.com/SanderMertens/bake + make -C bake/build-$(uname) + bake/bake setup + + - name: build flecs + run: bake --strict + + - name: run tests + run: | + bake run test/script --cfg sanitize -- -j 8 + test-sanitized-collections: needs: [ build-linux ] runs-on: ${{ matrix.os }} @@ -782,7 +825,7 @@ jobs: run: | bake run test/collections --cfg sanitize -- -j 8 - test-sanitized-cpp_api: + test-sanitized-cpp: needs: [ build-linux ] runs-on: ${{ matrix.os }} timeout-minutes: 30 @@ -804,7 +847,7 @@ jobs: - name: run tests run: | - bake run test/cpp_api --cfg sanitize -- -j 8 + bake run test/cpp --cfg sanitize -- -j 8 test-cmake: needs: [ build-linux, build-macos ] diff --git a/README.md b/README.md index 8bd399a60..fa367434e 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ typedef struct { } Position, Velocity; void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; @@ -79,7 +79,7 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(ecs, Move, EcsOnUpdate, Position, Velocity); - ecs_entity_t e = ecs_new_id(ecs); + ecs_entity_t e = ecs_new(ecs); ecs_set(ecs, e, Position, {10, 20}); ecs_set(ecs, e, Velocity, {1, 2}); @@ -108,7 +108,7 @@ int main(int argc, char *argv[]) { }); auto e = ecs.entity() - .set([](Position& p, Velocity& v) { + .insert([](Position& p, Velocity& v) { p = {10, 20}; v = {1, 2}; }); diff --git a/docs/DesignWithFlecs.md b/docs/DesignWithFlecs.md index be8ed14d9..b45c60f4a 100644 --- a/docs/DesignWithFlecs.md +++ b/docs/DesignWithFlecs.md @@ -44,7 +44,7 @@ There is a misconception that ECS components can only be plain data types, and s Queries are the primary method in Flecs for finding the entities for a set of components (or more specifically: a component expression). Queries are easy to use, but there a few things to keep in mind. ### Use the right query -Flecs has cached queries and uncached queries. Cached queries (`ecs_query_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_filter_t`, `flecs::filter`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred. +Flecs has cached queries and uncached queries. Cached queries (`ecs_query_cache_t` and `flecs::query`) are expensive to create but very cheap to iterate. Uncached queries (`ecs_query_t`, `flecs::query`) are fast to create, but more expensive to iterate. If you need to do a quick ad-hoc query for which you couldn't know in advance what you had to query for, an uncached query is the best option. If you have a query that you know in advance and need to iterate many times, a cached query is preferred. Another difference is that uncached queries can be created from systems, while cached queries cannot. If you need a cached query in a system, it has to be created in advance and passed into the system, either by setting it as system context, adding a component to the system with the query, or passing it in the lambda capture list (C++ only). Systems themselves use cached queries. diff --git a/docs/FlecsScriptManual.md b/docs/FlecsScriptManual.md new file mode 100644 index 000000000..82c83f528 --- /dev/null +++ b/docs/FlecsScriptManual.md @@ -0,0 +1,708 @@ +# Flecs Script + +## Introduction +Flecs Script is a runtime interpreted DSL for creating entities and components that is optimized for defining scenes, assets and configuration. In a nutshell, Flecs Script is to ECS what HTML/JSX is to a browser. + +Some of the features of Flecs Script are: + +- Native support for named entities, hierarchies and inheritance +- Assign component values +- Expressions and variables (`$var + 10`) +- Conditionals (`if $var > 10`) +- Native integration with assemblies (procedural assets) + +To learn Flecs Script, check out the [Tutorial](FlecsScriptTutorial.md)! + +## Example + +```c +using flecs.meta + +struct MaxSpeed { + value = f32 +} + +struct Position { + x = f32 + y = f32 +} + +Prefab SpaceShip { + MaxSpeed: {value: 100} + + cockpit { + Position: {x: -10, y: 0} + } +} + +my_spaceship : SpaceShip { + Position: {x: 10, y: 20} +} +``` + +## The Basics +This section goes over the basic syntax over Flecs Script. + +### Entities +An entity is created by specifying an identifier followed by a scope. Example: + +```c +my_entity {} +``` + +An entity scope can contain components and child entities. The following example shows how to add a child entity: + +```c +my_parent { + my_child {} +} +``` + +Note how a scope is also added to the child entity. + +So far these examples show how to create named entities. To create anonymous entities, use the `_` identifier: + +```c +_ { + my_child {} // named child with anonymous parent +} +``` + +### Tags +A tag can be added to an entity by simply specifying the tag's identifier in an entity scope. Example: + +```c +my_entity { + SpaceShip // SpaceShip tag +} +``` + +If a tag didn't exist yet, it will be automatically created in the script root. The following example is equivalent to the previous one: + +```c +SpaceShip + +my_entity { + SpaceShip // Now resolves to the existing SpaceShip tag +} +``` + +### Pairs +Pairs are added to entities by adding them to an entity scope, just like tags: + +```c +my_entity { + (Likes, Pizza) +} +``` + +Just like with tags, if the entities in the pair did not yet exist, they will be created in the script root. + +### Components +Components are specified like tags, but with an additional value: + +```c +my_entity { + Position: {x: 10, y: 20} +} +``` + +A component must exist before it can be used. Furthermore, for it to be assigneable with a value, it also needs to be described in the reflection framework. + +A component can also be added without a value. This will create a default constructed component. Example: + +```c +my_entity { + Position +} +``` + +Components can be defined in a script: + +```c +using flecs.meta + +struct Position { + x = f32 + y = f32 +} + +my_entity { + Position: {x: 10, y: 20} +} +``` + +Components can be pairs: + +```c +my_entity { + (Start, Position): {x: 0, y: 0} + (Stop, Position): {x: 10, y: 20} +} +``` + +### Entity kinds +An entity can be created with a "kind", which is a component specified before the entity name. This is similar to adding a tag or component in a scope, but can provide a more natural way to describe things. For example: + +```c +SpaceShip my_spaceship {} +``` + +This is equivalent to doing: + +```c +my_spaceship { + SpaceShip +} +``` + +When using the entity kind syntax, the scope is optional: + +```c +SpaceShip my_spaceship // no {} +``` + +If the specified kind is a component, a value can be specified between parentheses: + +```c +CheckBox my_checkbox(checked: true) +``` + +When the entity kind is a component, a value will always be assigned even if none is specified. This is different from component assignments in a scope. Example: + +```c +CheckBox my_checkbox(checked: true) + +// is equivalent to + +my_checkbox { + CheckBox: {checked: true} +} +``` + +```c +CheckBox my_checkbox + +// is equivalent to + +my_checkbox { + CheckBox: {} +} +``` + +### Inheritance +Scripts can natively specify inheritance relationships between entities, which is useful in particular for prefabs. Example: + +```c +Prefab SpaceShip { + MaxSpeed: {value: 100} +} + +my_spaceship : SpaceShip {} +``` + +When specifying inheritance, the scope is optional: + +```c +my_spaceship : SpaceShip // no {} +``` + +This is equivalent to doing: + +```c +my_spaceship { + (IsA, SpaceShip) +} +``` + +### Relationship hierarchies +By default entity hierarchies are created with the `ChildOf` relationship. Other relationships can also be used to create hierarchies by combining a pair with a scope. Example: + +```c +(IsA, Thing) { + (IsA, Organism) { + (IsA, Plant) { + Tree + } + (IsA, Animal) { + Human + } + } +} +``` + +## Advanced Features + +### Module statement +The `module` statement puts all contents of a script in a module. Example: + +```c +module components.transform + +// Creates components.transform.Position +struct Position { + x = f32 + y = f32 +} +``` + +The `components.transform` entity will be created with the `Module` tag. + +### Using statement +The `using` keyword imports a namespace into the current namespace. Example: + +```c +// Without using +flecs.meta.struct Position { + x = flecs.meta.f32 + y = flecs.meta.f32 +} +``` +```c +// With using +using flecs.meta + +struct Position { + x = f32 + y = f32 +} +``` + +The `using` keyword only applies to the scope in which it is specified. Example: + +```c +// Scope without using +my_engine { + game.engines.FtlEngine: {active: true} +} +``` +```c +// Scope with using +my_spaceship { + using game.engines + + FtlEngine: {active: true} +} +``` + +A `using` statement may end with a wildcard (`*`). This will import all namespaces matching the path. Example: + +```c +using flecs.* + +struct Position { + x = f32 + y = f32 +} +``` + +### With statement +When you're buildin a scene or asset you may find yourself often repeating the same components for multiple entities. To avoid this, a `with` statement can be used. For example: + +```c +with SpaceShip { + MilleniumFalcon {} + UssEnterprise {} + UssVoyager {} + Rocinante {} +} +``` + +This is equivalent to doing: + +```c +MilleniumFalcon { + SpaceShip +} + +UssEnterprise { + SpaceShip +} + +UssVoyager { + SpaceShip +} + +Rocinante { + SpaceShip +} +``` + +With statements can contain multiple tags: + +```c +with SpaceShip, HasWeapons { + MilleniumFalcon {} + UssEnterprise {} + UssVoyager {} + Rocinante {} +} +``` + +With statements can contain component values, specified between parentheses: + +```c +with Color(38, 25, 13) { + pillar_1 {} + pillar_2 {} + pillar_3 {} +} +``` + +### Variables +Scripts can contain variables, which are useful for often repeated values. Variables are created with the `const` keyworld. Example: + +```c +const pi = 3.1415926 + +my_entity { + Rotation: {angle: $pi} +} +``` + +Variables can be combined with expressions: + +```c +const pi = 3.1415926 +const pi_2 = $pi * 2 + +my_entity { + Rotation: {angle: $pi / 2} +} +``` + +In the above examples, the type of the variable is inferred. Variables can also be provided with an explicit type: + +```c +const wood = Color: {38, 25, 13} +``` + +Variables can be used in component values as shown in the previous examples, or can be used directly as component. Example: + +```c +const wood = Color: {38, 25, 13} + +my_entity { + $wood +} + +// is equivalent to + +my_entity { + Color: {38, 25, 13} +} +``` + +Additionally, variables can also be used in combination with `with` statements: + +```c +const wood = Color: {38, 25, 13} + +with $color { + pillar_1 {} + pillar_2 {} + pillar_3 {} +} +``` + +### If statement +Parts of a script can be conditionally executed with an if statement. Example: + +```c +const daytime = bool: false + +lantern { + Color: {210, 255, 200} + + if $daytime { + Emissive: {value: 0} + } else { + Emissive: {value: 1} + } +} +``` + +### Default components +A scope can have a default component, which means entities in that scope can assign values of that component without having to specify the component name. + +There are different ways to specify a default component. One way is to use a `with` statement. Default component values are assigned with the `=` operator, and don't need a `{}` surrounding the value. Example: + +```c +with Position { + ent_a = 10, 20 + ent_b = 20, 30 +} +``` + +Another way a default components are derived is from the entity kind. If an entity is specified with a kind, a `DefaultChildComponent` component will be looked up on the kind to find the default component for the scope, if any. For example: + +```c +// Create a PositionList tag with a DefaultChildComponent +PositionList { + DefaultChildComponent: {Position} +} + +// Derive default component for scope from PositionList +PositionList plist { + ent_a = 10, 20 + ent_b = 10, 20 + ent_c = 10, 20 +} +``` + +A common use of default components is when creating structs. `struct` is a component with `member` as default child component. Example: + +```c +struct Position { + x = f32 + y = f32 +} + +// is equivalent to + +struct Position { + member x(f32) + member y(f32) +} +``` + +Note how `member` is also used as kind for the children. This means that children of `x` and `y` derive their default child component from `member`, which is set to `member`. This makes it easy to create nested members: + +```c +struct Line { + start { + x = f32 + y = f32 + } + stop { + x = f32 + y = f32 + } +} + +// is equivalent to + +struct Line { + member start { + member x(f32) + member y(f32) + } + member stop { + member x(f32) + member y(f32) + } +} +``` + +### Semicolon operator +Multiple statements can be combined on a single line when using the semicolon operator. Example: + +```c +my_spaceship { + SpaceShip; HasFtl +} +``` + +## Comma operator +The comma operator can be used as a shortcut to create multiple entities in a scope. Example: + +```c +my_spaceship { + pilot_a, + pilot_b, + pilot_c +} + +// is equivalent to + +my_spaceship { + pilot_a {} + pilot_b {} + pilot_c {} +} +``` + +This allows for a more natural way to describe things like enum types: + +```c +enum Color { + Red, + Green, + Blue +} + +// is equivalent to + +enum Color { + constant Red + constant Green + constant Blue +} +``` + +## Assemblies +Assemblies are parametrizable scripts that can be used to create procedural assets. Assemblies can be created with the `assembly` keyword. Example: + +```c +assembly Square { + Color: {255, 0, 0} + Rectangle: {width: 100, height: 100} +} +``` + +The script contents of an assembly are not ran immediately. Instead they are ran whenever an assembly is _instantiated_. To instantiate an assembly, add it as a regular component to an entity: + +```c +my_entity { + Square +} + +// is equivalent to + +my_entity { + Color: {255, 0, 0} + Rectangle: {width: 100, height: 100} +} +``` + +Assemblies are commonly used in combination with the kind syntax: + +```c +Square my_entity +``` + +Assemblies can be parametrized with properties. Properties are variables that are exposed as component members. To create a property, use the `prop` keyword. Example: + +```c +assembly Square { + prop size = i32: 10 + prop color = Color: {255, 0, 0} + + $color + Rectangle: {width: $size, height: $size} +} + +Square my_entity(size: 20, color: {38, 25, 13}) +``` + +Assembly scripts can do anything a regular script can do, including creating child entities. The following example shows how to create an assembly that uses a nested assembly to create children: + +```c +assembly Tree { + prop height = f32: 10 + + const wood_color = Color: {38, 25, 13} + const leaves_color = Color: {51, 76, 38} + + const canopy_height = 2 + const trunk_height = $height - $canopy_height + const trunk_width = 2 + + Trunk { + Position: {0, ($height / 2), 0} + Rectangle: {$trunk_width, $trunk_height} + $wood_color + } + + Canopy { + const canopy_y = $trunk_height + ($canopy_height / 2) + + Position3: {0, $canopy_y, 0} + Box: {$canopy_width, $canopy_height} + $leaves_color + } +} + +assembly Forest { + Tree(height: 5) { + Position: {x: -10} + } + + Tree(height: 10) { + Position: {x: 0} + } + + Tree(height: 7) { + Position: {x: 10} + } +} + +Forest my_forest +``` + +## API +This section goes over how to run scripts in an application. + +### Run once +To run a script once, use the `ecs_script_run` function. Example: + +```c +const char *code = "my_spaceship {}"; + +if (ecs_script_run(world, "my_script_name", code)) { + // error +} +``` + +Alternatively a script can be ran directly from a file: + +```c +if (ecs_script_run_file(world, "my_script.flecs")) { + // error +} +``` + +If a script fails, the entities created by the script will not be automatically deleted. When a script contains assemblies, script resources will not get cleaned up until the entities associated with the assemblies are deleted. + +### Run multiple times +A script can be ran multiple times by using the `ecs_script_parse` and `ecs_script_eval` functions. Example: + +```c +const char *code = "my_spaceship {}"; + +ecs_script_t *script = ecs_script_parse( + world, "my_script_name", code); +if (!script) { + // error +} + +if (ecs_script_eval(script)) { + // error +} + +// Run again +if (ecs_script_eval(script)) { +} + +// Free script resources +ecs_script_free(script); +``` + +If a script fails, the entities created by the script will not be automatically deleted. When a script contains assemblies, script resources will not get cleaned up until the entities associated with the assemblies are deleted. + +### Managed script +Managed scripts are scripts that are associated with an entity, and can be ran multiple times. Entities created by a managed script are tagged with the script. When script execution fails, the entities associated with the script will be deleted. Additionally, if after executing the script again an entity is no longer created by the script, it will also be deleted. + +To run a managed script, do: + +```c +const char *code = "my_spaceship {}"; + +ecs_entity_t s = ecs_script(world, { + .code = code +}); + +if (!s) { + // error +} +``` + +To update the code of a managed script, use the `ecs_script_update` function: + +```c +if (ecs_script_update(world, s, 0, new_code)) { + // error +} +``` + +When a script contains assemblies, script resources will not get cleaned up until the entities associated with the assemblies are deleted. diff --git a/docs/FlecsScriptTutorial.md b/docs/FlecsScriptTutorial.md index 601ecc6af..e2c8e6674 100644 --- a/docs/FlecsScriptTutorial.md +++ b/docs/FlecsScriptTutorial.md @@ -5,7 +5,7 @@ In this tutorial we will be designing a simple fence asset from scratch, and mak [![preview of fence asset](img/script_tutorial/tut_playground_preview.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%20%2F%202%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20prop%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Fence%0A%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%2C%20color%3A%24%7D%0A%20%20%7D%0A%7D%20%2F%2F%20assembly%20Enclosing%0A%0Afence_a%20%7B%0A%20%20-%20Enclosing%7B%7D%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B21%2C%2019%2C%20-37%7D%0A%20%20-%20Rotation3%7B-0.42%2C%20-0.52%7D%0A%7D%0A) -Note: in order to use Flecs script an app needs to be built with the `FLECS_PLECS` addon. +Note: in order to use Flecs script an app needs to be built with the `FLECS_SCRIPT` addon. ## Getting Started with the Explorer The Flecs explorer is a web-based application that lets us write scripts and see the results directly. In the tutorial we will use the explorer in combination with the Flecs playground, which has a rendering canvas and comes preloaded with a number of modules and assets. @@ -145,7 +145,7 @@ Now add these lines into the editor to create our ground `plane`: ```js plane { - Position3{} - - Rectangle{100, 100} + Rectangle: {100, 100} - Rgb{0.9, 0.9, 0.9} } ``` @@ -181,8 +181,8 @@ Let's now add a cube to the scene. The code for this looks similar to the plane: ```js box { - Position3{} - - Box{10, 10, 10} - - Rgb{1, 0, 0} + Box: {10, 10, 10} + Rgb: {1, 0, 0} } ``` @@ -195,8 +195,8 @@ To fix this, we can move it up by setting the `y` member of `Position3` to half ```js box { - Position3{y: 5} - - Box{10, 10, 10} - - Rgb{1, 0, 0} + Box: {10, 10, 10} + Rgb: {1, 0, 0} } ``` @@ -223,11 +223,11 @@ Inside the `with` statement we can now create two box entities for the left and with Rgb{0.15, 0.1, 0.05} { left_pillar { - Position3{x: -10, y: 5} - - Box{2, 10, 2} + Box: {2, 10, 2} } right_pillar { - Position3{x: 10, y: 5} - - Box{2, 10, 2} + Box: {2, 10, 2} } } ``` @@ -241,7 +241,7 @@ That works, but the code is starting to look a bit unwieldy. There are lots of m First lets define two variables for the color and box shape of the pillars. We already saw an example of a variable when we defined `PI`. These looks similar, except that because they are composite values, we also define their type: ```js -const color : Rgb = {0.15, 0.1, 0.05} +const color = Rgb: {0.15, 0.1, 0.05} const pillar_box : Box = {2, 10, 2} ``` @@ -249,7 +249,7 @@ This is better, but `pillar_box` still contains values that we have to update ea ```js const height = 10 -const color : Rgb = {0.15, 0.1, 0.05} +const color = Rgb: {0.15, 0.1, 0.05} const pillar_width = 2 const pillar_box : Box = { @@ -287,7 +287,7 @@ This is no fence yet. We're still missing the crossbars. Let's add another entit ```js bar { - Position3{y: $height / 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } ``` @@ -299,12 +299,12 @@ Let's add a second entity and space the two entities out a bit so they don't ove ```js top_bar { - Position3{y: $height/2 + 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } bottom_bar { - Position3{y: $height/2 - 2} - - Box{$width, 2, 1} + Box: {$width, 2, 1} - $color } ``` @@ -411,7 +411,7 @@ We could save this script as is, load it into our game, and instantiate the pref ```c // In C -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); ecs_entity_t fence = ecs_lookup(world, "Fence"); ecs_entity_t fence_a = ecs_new_w_pair(world, EcsIsA, fence); @@ -424,7 +424,7 @@ ecs_set(world, fence_b, EcsPosition3, {10}); // In C++ using namespace flecs::components::transform; -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); auto fence = world.lookup("Fence"); auto fence_a = world.entity().is_a(fence); @@ -473,9 +473,9 @@ Let's take the existing `width`, `height` and `color` variables, and change them ```js assembly Fence { - prop width : flecs.meta.f32 = 20 - prop height : flecs.meta.f32 = 10 - prop color : Rgb = {0.15, 0.1, 0.05} + prop width = flecs.meta.f32: 20 + prop height = flecs.meta.f32: 10 + prop color = Rgb: {0.15, 0.1, 0.05} // fence code } @@ -491,9 +491,9 @@ The code can now be changed to this: ```js assembly Fence { - prop width : f32 = 20 - prop height : f32 = 10 - prop color : Rgb = {0.15, 0.1, 0.05} + prop width = f32: 20 + prop height = f32: 10 + prop color = Rgb: {0.15, 0.1, 0.05} // fence code } @@ -516,11 +516,11 @@ The fences are back, with the default values we provided to our props. But we no ```js fence_a { - - Fence{width: 10, height: 20} + Fence: {width: 10, height: 20} - Position3{-10} } fence_b { - - Fence{width: 25, height: 10} + Fence: {width: 25, height: 10} - Position3{10} } ``` @@ -544,11 +544,11 @@ ECS_COMPONENT(world, Fence); // Because the Fence assembly has the same name as the // component it will "bind" to it. -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); // Set the component as usual -ecs_entity_t fence_a = ecs_set(world, 0, Fence, {10, 20}); -ecs_entity_t fence_b = ecs_set(world, 0, Fence, {25, 10}); +ecs_entity_t fence_a = ecs_insert(world, ecs_value(Fence, {10, 20})); +ecs_entity_t fence_b = ecs_insert(world, ecs_value(Fence, {25, 10})); ecs_set(world, fence_a, EcsPosition3, {-10}); ecs_set(world, fence_b, EcsPosition3, {10}); @@ -565,7 +565,7 @@ struct Fence { // Because the Fence assembly has the same name as the // component it will "bind" to it. -ecs_plecs_from_file(world, "fence.flecs"); +ecs_script_run_file(world, "fence.flecs"); auto fence_a = world.entity().set({10, 20}); auto fence_b = world.entity().set({25, 10}); @@ -644,7 +644,7 @@ fence :- Fence{} And increase the `width` of the fence to `60`: ```js - prop width : f32 = 60 + prop width = f32: 60 ``` We now get a number of pillars that matches the fence length: @@ -679,10 +679,10 @@ When put together, this is what it looks like: ```js assembly Enclosing { - prop width: f32 = 40 - prop height: f32 = 10 - prop depth: f32 = 40 - prop color: Rgb = {0.15, 0.1, 0.05} + prop width = f32: 40 + prop height = f32: 10 + prop depth = f32: 40 + prop color = Rgb: {0.15, 0.1, 0.05} // enclosing code goes here } @@ -699,20 +699,20 @@ up writing the same divisions multiple times. left { - Position3{x: -$width_half} - Rotation3{y: $PI/2} - - Fence{width: $depth, height:$, color:$} + Fence: {width: $depth, height:$, color:$} } right { - Position3{x: $width_half} - Rotation3{y: $PI/2} - - Fence{width: $depth, height:$, color:$} + Fence: {width: $depth, height:$, color:$} } back { - Position3{z: -$depth_half} - - Fence{width: $width, height:$, color:$} + Fence: {width: $width, height:$, color:$} } front { - Position3{z: $depth_half} - - Fence{width: $width, height:$, color:$} + Fence: {width: $width, height:$, color:$} } ``` @@ -737,7 +737,7 @@ Here is what that looks like. You might need to zoom out a bit with the camera t We can now easily modify our enclosing by passing in parameters for width and height: ``` -enclosing :- Enclosing{width: 100, height: 30} +enclosing { Enclosing: {width: 100, height: 30} } ``` [![a tall enclosing](img/script_tutorial/tut_playground_pasture_2.png)](https://www.flecs.dev/explorer/?local=true&wasm=https://www.flecs.dev/explorer/playground.js&script=using%20flecs.components.*%0Ausing%20flecs.meta%0Ausing%20flecs.game%0A%0Aconst%20PI%20%3D%203.1415926%0A%0A%2F%2F%20The%20ground%20plane%0Aplane%20%7B%0A%20%20-%20Position3%7B%7D%0A%20%20-%20Rotation3%7B%24PI%2F2%7D%0A%20%20-%20Rectangle%7B10000%2C%2010000%7D%0A%20%20-%20Rgb%7B0.9%2C%200.9%2C%200.9%7D%0A%7D%0A%0Aassembly%20Fence%20%7B%0A%20%20%2F%2F%20Fence%20parameters%0A%20%20prop%20width%20%3A%20f32%20%3D%2040%0A%20%20prop%20height%20%3A%20f32%20%3D%2020%0A%20%20const%20color%20%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%0A%20%20%2F%2F%20Pillar%20parameters%0A%20%20const%20pillar_width%20%3D%202%0A%20%20const%20pillar_spacing%20%3D%2010%0A%20%20const%20pillar_count%20%3D%20%24width%20%2F%20%24pillar_spacing%0A%20%20const%20p_grid_spacing%20%3D%20%24width%20%2F%20(%24pillar_count%20-%201)%0A%20%0A%20%20%2F%2F%20Bar%20parameters%0A%20%20const%20bar_spacing%20%3D%203%0A%20%20const%20bar_height%20%3D%202%0A%20%20const%20bar_depth%20%3D%20%24pillar_width%20%2F%202%0A%20%20const%20bar_count%20%3D%20%24height%20%2F%20%24bar_spacing%0A%20%20const%20b_grid_spacing%20%3D%20%24height%20%2F%20%24bar_count%0A%20%20%20%0A%20%20with%20Prefab%2C%20%24color%20%7B%0A%20%20%20%20Pillar%20%3A-%20Box%20%7B%0A%20%20%20%20%20%20%24pillar_width%2C%20%24height%2C%20%24pillar_width%0A%20%20%20%20%7D%0A%20%20%20%20Bar%20%3A-%20Box%20%7B%24width%2C%20%24bar_height%2C%20%24bar_depth%7D%0A%20%20%7D%20%20%20%20%0A%20%20%0A%20%20%2F%2F%20Pillars%0A%20%20pillars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20x.count%3A%20%24pillar_count%2C%0A%20%20%20%20%20%20x.spacing%3A%20%24p_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Pillar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20%2F%2F%20Bars%0A%20%20bars%20%7B%0A%20%20%20%20-%20Position3%7By%3A%20%24height%2F2%7D%0A%20%20%20%20-%20Grid%7B%0A%20%20%20%20%20%20y.count%3A%20%24bar_count%2C%0A%20%20%20%20%20%20y.spacing%3A%20%24b_grid_spacing%0A%20%20%20%20%20%20prefab%3A%20Bar%20%20%20%20%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%20%2F%2F%20Prefab%20Fence%0A%0Aassembly%20Enclosing%20%7B%0A%20%20prop%20width%3A%20f32%20%3D%2040%0A%20%20prop%20height%3A%20f32%20%3D%2010%0A%20%20prop%20depth%3A%20f32%20%3D%2040%0A%20%20prop%20color%3A%20Rgb%20%3D%20%7B0.15%2C%200.1%2C%200.05%7D%0A%20%20%0A%20%20const%20width_half%20%3D%20%24width%20%2F%202%0A%20%20const%20depth_half%20%3D%20%24depth%20%2F%202%0A%20%20const%20PI%20%3D%203.1415926%0A%0A%20%20left%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20-%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20right%20%7B%0A%20%20%20%20-%20Position3%7Bx%3A%20%24width_half%7D%0A%20%20%20%20-%20Rotation3%7By%3A%20%24PI%20%2F%202%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24depth%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20back%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20-%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%20%20front%20%7B%0A%20%20%20%20-%20Position3%7Bz%3A%20%24depth_half%7D%0A%20%20%20%20-%20Fence%7Bwidth%3A%20%24width%2C%20height%3A%24%7D%0A%20%20%7D%0A%7D%0A%0Apasture%20%3A-%20Enclosing%7B%0A%20%20width%3A%20100%2C%20%0A%20%20depth%3A%20100%0A%20%20height%3A%2030%0A%7D%0A%0Acamera%20%7B%0A%20%20-%20Position3%7B52%2C%2041%2C%20-82%7D%0A%20%20-%20Rotation3%7B-0.45%2C%20-0.54%7D%0A%7D%0A) @@ -757,7 +757,7 @@ const PI = 3.1415926 plane { - Position3{} - Rotation3{$PI / 2} - - Rectangle{10000, 10000} + Rectangle: {10000, 10000} - Rgb{0.9, 0.9, 0.9} } diff --git a/docs/Manual.md b/docs/Manual.md index c66b0f308..31f742786 100644 --- a/docs/Manual.md +++ b/docs/Manual.md @@ -60,8 +60,8 @@ typedef struct Velocity { // System names ('Move') use PascalCase. API types use snake_case_t void Move(ecs_iter_t *it) { // Functions use snake_case - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x; @@ -83,7 +83,7 @@ int main(int argc, char *argv[]) { ECS_SYSTEM(world, Move, EcsOnUpdate, Position, Velocity); // Function wrapper macros use snake_case - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); // Builtin entities use PascalCase ecs_add(world, EcsWorld, Position); @@ -206,7 +206,7 @@ ecs_entity_t e = ecs_lookup(world, "MyEntity"); When an entity is part of a hierarchy, names can be used to form a path: ```c -ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t grandchild = ecs_new_w_pair(world, EcsChildOf, child); @@ -250,7 +250,7 @@ typedef struct Position { ECS_COMPONENT(world, Position); -ecs_entity_t e = ecs_new(world, Position); +ecs_entity_t e = ecs_new_w(world, Position); ``` From a readability perspective this code looks fine as we can easily tell what is happening here. Though if we take a closer look, we can see that a typename is used where we expect an expression, and that is not possible in plain C. So what is going on? @@ -314,7 +314,7 @@ typedef uint64_t ecs_entity_t; Zero indicates an invalid entity. Applications can create new entities with the `ecs_new` operation: ```c -ecs_entity_t e = ecs_new(world, 0); +ecs_entity_t e = ecs_new(world); ``` This operation guarantees to return an unused entity identifier. The first entity returned is not 1, as Flecs creates a number of builtin entities during the initialization of the world. The identifier of the first returned entity is stored in the `EcsFirstUserEntityId` constant. @@ -330,10 +330,10 @@ When using multiple threads, the `ecs_new` operation guarantees that the returne When an entity is deleted, the generation count for that entity id is increased. The entity generation count enables an application to test whether an entity is still alive or whether it has been deleted, even after the id has been recycled. Consider: ```c -ecs_entity_t e = ecs_new(world, 0); +ecs_entity_t e = ecs_new(world); ecs_delete(world, e); // Increases generation -e = ecs_new(world, 0); // Recycles id, but with new generation +e = ecs_new(world); // Recycles id, but with new generation ``` The generation is encoded in the entity id, which means that even though the base id is the same in the above example, the value returned by the second `ecs_new` is different than the first. @@ -341,10 +341,10 @@ The generation is encoded in the entity id, which means that even though the bas To test whether an entity is alive, an application can use the `ecs_is_alive` call: ```c -ecs_entity_t e1 = ecs_new(world, 0); +ecs_entity_t e1 = ecs_new(world); ecs_delete(world, e1); -ecs_entity_t e2 = ecs_new(world, 0); +ecs_entity_t e2 = ecs_new(world); ecs_is_alive(world, e1); // false ecs_is_alive(world, e2); // true ``` @@ -400,7 +400,7 @@ A type is typically used to describe the contents (components) of an entity. A s ```c // Create entity with type Position -ecs_entity_t e = ecs_new(world, Position); +ecs_entity_t e = ecs_new_w(world, Position); // Add Velocity to the entity ecs_add(world, e, Velocity); @@ -430,10 +430,10 @@ typedef vector ecs_type_t; As a result, an application is able to do this: ```c -ecs_entity_t tag_1 = ecs_new(world, 0); -ecs_entity_t tag_2 = ecs_new(world, 0); +ecs_entity_t tag_1 = ecs_new(world); +ecs_entity_t tag_2 = ecs_new(world); -ecs_entity_t e = ecs_new(world, 0); +ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, tag_1); ecs_add_id(world, e, tag_2); ``` @@ -473,7 +473,7 @@ int main() { ECS_COMPONENT(world, Position); // Create a new entity with the component - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); // Remove the component from the entity ecs_remove(world, e, Position); @@ -517,7 +517,7 @@ ECS_COMPONENT_DECLARE(Position); // Function that uses the global component variable ecs_entity_t create_entity(ecs_world_t *world) { - return ecs_new(world, Position); + return ecs_new_w(world, Position); } int main(int argc, char *argv[]) { @@ -554,7 +554,7 @@ typedef struct Position { void new_w_position(ecs_world_t *t, ecs_id_t ecs_id(Position)) { // ecs_new uses an ecs_id_t - ecs_new(world, Position); + ecs_new_w(world, Position); } int main() { @@ -606,7 +606,7 @@ int main() { ECS_COMPONENT(world, Position); - ecs_entity_t e = ecs_new(world, Position); + ecs_entity_t e = ecs_new_w(world, Position); /* Component is enabled by default */ @@ -614,7 +614,7 @@ int main() { ecs_enable_component(world, e, Position, false); /* Will return false */ - printf("%d\n", ecs_is_enabled_component(world, e, Position)); + printf("%d\n", ecs_is_enabled(world, e, Position)); /* Re-enable the component */ ecs_enable_component(world, e, Position, true); @@ -660,7 +660,7 @@ Just like components, the API needs a handle to a tag before it can use it, and ```c void new_w_tag(ecs_world_t *t, ecs_type_t ecs_type(Tag)) { // ecs_new uses an ecs_type_t - ecs_new(world, Tag); + ecs_new_w(world, Tag); } int main() { @@ -686,7 +686,7 @@ int main() { ECS_TAG(world, Tag); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); add_tag(world, e, Tag); ecs_fini(world); @@ -710,7 +710,7 @@ The implementation of the observer looks similar to a system: ```c void AddPosition(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); + Position *p = ecs_field(it, Position, 0); for (int i = 0; i < it->count; i++) { p[i].x = 10; @@ -784,7 +784,7 @@ ecs_world_t *world = ecs_init(); ECS_IMPORT(world, Vehicles); /* The module contents can now be used */ -ecs_entity_t e = ecs_new(world, Car); +ecs_entity_t e = ecs_new_w(world, Car); ``` Module contents are namespaced, which means that the identifiers of the content of the module (components, tags, systems) are stored in the scope of the module. For the above example module, everything would be stored in the `vehicles` scope. To resolve the `Car` component by name, an application would have to do: @@ -829,7 +829,7 @@ world.import(); Entities in Flecs can be organized in hierarchies, which is useful when for example constructing a scene graph. To create hierarchies, applications can add `ChildOf` relationships to entities. This creates a relationship between a parent entity and a child entity that the application can later traverse. This is an example of a simple hierarchy: ```c -ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ``` @@ -876,14 +876,14 @@ Queries and systems can request data from parents of the entity being iterated o ```c // Iterate all entities with Position that have a parent that also has Position -ecs_query_t *q = ecs_query_new(world, "Position(parent), Position"); +ecs_query_t *q = ecs_query(world, { .expr = "Position(parent), Position" }); ``` Additionally, a query can iterate the hierarchy in breadth-first order by providing the `cascade` modifier: ```c // Iterate all entities with Position that have a parent that also has Position -ecs_query_t *q = ecs_query_new(world, "Position(parent|cascade), Position"); +ecs_query_t *q = ecs_query(world, { .expr = "Position(cascade), Position" }); ``` This does two things. First, it will iterate over all entities that have Position and that _optionally_ have a parent that has `Position`. By making the parent component optional, it is ensured that if an application is iterating a tree of entities, the root is also included. Secondly, the query iterates over the children in breadth-first order. This is particularly useful when writing transform systems, as they require parent entities to be transformed before child entities. @@ -891,7 +891,7 @@ This does two things. First, it will iterate over all entities that have Positio The above query does not match root entities, as they do not have a parent with `Position`. To also match root entities, add `?` to make the term optional: ```c -ecs_query_t *q = ecs_query_new(world, "?Position(parent|cascade), Position"); +ecs_query_t *q = ecs_query(world, { .expr = "?Position(cascade), Position" }); ``` See the [query manual](Queries.md) section for more details. @@ -937,13 +937,13 @@ Note that the path separator is provided twice, once for the prefix and once for Applications can set a default scope with the `ecs_set_scope` function, so that all operations are evaluated relative to a scope. The scope is set on a stage, which makes it thread safe when executed from within a flecs worker thread. This example shows how to set the scope: ```c -ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t parent = ecs_new(world); // Set the current scope to the parent ecs_entity_t prev_scope = ecs_set_scope(world, parent); // This entity is created as child of parent -ecs_entity_t child = ecs_new(world, 0); +ecs_entity_t child = ecs_new(world); // Look for "child" relative to parent ecs_entity_t e = ecs_lookup(world, "child"); @@ -990,7 +990,7 @@ Inheritance is the ability to share components between entities by _inheriting_ ```c // Create a base entity -ecs_entity_t base = ecs_new(world, 0); +ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); // Derive from base @@ -1017,7 +1017,7 @@ ECS_ENTITY(world, e, (IsA, base)); `IsA` relationships can be nested: ```c -ecs_entity_t base = ecs_new(world, 0); +ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); ecs_entity_t derived = ecs_new_w_pair(world, EcsIsA, base); @@ -1035,7 +1035,7 @@ Derived entities can override components from their base by adding the component ```c // Shortcut for creating a base entity and setting Position -ecs_entity_t base = ecs_set(world, 0, Position, {10, 20}); +ecs_entity_t base = ecs_insert(world, ecs_value(Position, {10, 20})); // Derive from the base ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -1067,7 +1067,7 @@ Overrides work with nested `IsA` relationships: ```c // Shortcut for creating a base entity and setting Position -ecs_entity_t base = ecs_new(world, 0); +ecs_entity_t base = ecs_new(world); ecs_set(world, base, Position, {10, 20}); ecs_set(world, base, Velocity, {1, 1}); @@ -1086,7 +1086,7 @@ In some scenarios it is desirable that an entity is initialized with a specific ```c // Create a base. Simply deriving the base will share the component, but not override it. -ecs_entity_t Base = ecs_set(world, 0, Position, {10, 20}); +ecs_entity_t Base = ecs_insert(world, ecs_value(Position, {10, 20})); // Mark as OVERRIDE. This ensures that when base is derived from, Position is overridden ecs_add_id(world, world, Base, ECS_OVERRIDE | ecs_id(Position)); @@ -1110,7 +1110,7 @@ ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, Base); If a base entity has children, derived entities of that base entity will, when the `IsA` relationship is added, acquire the same set of children. Take this example: ```c -ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t parent = ecs_new(world); ecs_entity_t child_1 = ecs_new_w_pair(world, EcsChildOf, parent); ecs_entity_t child_2 = ecs_new_w_pair(world, EcsChildOf, parent); @@ -1121,7 +1121,7 @@ ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, parent); The children that are copied to the entity will have exactly the same set of components as the children of the base. For example, if the base child has components `Position, Velocity`, the derived child will also have `Position, Velocity`. Furthermore, the values of the base child components will be copied to the entity child: ```c -ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t parent = ecs_new(world); ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); ecs_set_name(world, child, "Child"); // Give child a name, so we can look it up ecs_set(world, child, Position, {10, 20}); @@ -1139,10 +1139,10 @@ ecs_get(world, child, Position) != ecs_get(world, e_child, Position); // 1 Since the children of the derived entity have the exact same components as the base children, their components are not shared. Component sharing between children is possible however, as `IsA` relationships are also copied over to the child of the derived entity: ```c -ecs_entity_t parent = ecs_new(world, 0); +ecs_entity_t parent = ecs_new(world); // Create child base from which we will share components -ecs_entity_t child_base = ecs_new(world, 0); +ecs_entity_t child_base = ecs_new(world); ecs_set(world, child_base, Position, {10, 20}); ecs_set_name(world, child, "Child"); @@ -1202,7 +1202,7 @@ Applications can defer entity with the `ecs_defer_begin` and `ecs_defer_end` fun ```c ecs_defer_begin(world); - ecs_entity_t e = ecs_new(world, 0); + ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_set(world, e, Velocity, {1, 1}); ecs_defer_end(world); diff --git a/docs/Queries.md b/docs/Queries.md index a614a5103..d92884bc3 100644 --- a/docs/Queries.md +++ b/docs/Queries.md @@ -55,20 +55,20 @@ https://github.com/SanderMertens/flecs/tree/master/examples ## Types Flecs has different query types, which are optimized for different kinds of use cases. This section provides a brief overview of each kind: - ### Filters - Filters are cheap to create, low overhead, reasonably efficient to iterate. They are good for ad-hoc queries with runtime-defined conditions. An example: + ### Querys + Querys are cheap to create, low overhead, reasonably efficient to iterate. They are good for ad-hoc queries with runtime-defined conditions. An example: ```c - ecs_filter_t *f = ecs_filter(world, { + ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x; @@ -77,8 +77,8 @@ while (ecs_filter_next(&it)) { } ``` ```cpp -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); f.each([](Position& p, Velocity& v) { p.x += v.x; @@ -91,15 +91,15 @@ Cached queries cache the output of a filter. They are more expensive to create a ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); 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); + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x; @@ -121,16 +121,16 @@ q.each([](Position& p, Velocity& v) { Rules are a constraint-based query engine capable of traversing graphs. They are more expensive to create than filters, have low overhead, and their iteration performance depends on query complexity. An example: ```c -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); -ecs_iter_t it = ecs_rule_iter(world, r); -while (ecs_rule_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); +ecs_iter_t it = ecs_query_iter(world, r); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x; @@ -139,8 +139,8 @@ while (ecs_rule_next(&it)) { } ``` ```cpp -flecs::rule r = - world.rule(); +flecs::query r = + world.query(); r.each([](Position& p, Velocity& v) { p.x += v.x; @@ -154,10 +154,10 @@ For more information on how each implementation performs, see [Performance](#per This section explains how to create queries in the different language bindings and the flecs query DSL. ### Query Descriptors (C) -Query descriptors are the C API for creating queries. The API uses a type called `ecs_filter_desc_t`, to describe the structure of a query. This type is used to create all query kinds (`ecs_filter_t`, `ecs_query_t`, `ecs_rule_t`). An example: +Query descriptors are the C API for creating queries. The API uses a type called `ecs_query_desc_t`, to describe the structure of a query. This type is used to create all query kinds (`ecs_query_t`, `ecs_query_cache_t`, `ecs_query_impl_t`). An example: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } @@ -167,7 +167,7 @@ ecs_filter_t *f = ecs_filter(world, { The example shows the short notation, which looks like this when expanded: ```c -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *f = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } @@ -177,28 +177,28 @@ ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ Query descriptors can also be used by the C++ API. However because C++ does not support taking the address of a temporary, and not all language revisions support designated initializers, query descriptors in C++ should be used like this: ```c -ecs_filter_desc_t desc = {}; // Zero-initialize the struct +ecs_query_desc_t desc = {}; // Zero-initialize the struct desc.terms[0].id = ecs_id(Position); desc.terms[1].id = ecs_id(Velocity); -ecs_filter_t *f = ecs_filter_init(world, &desc); +ecs_query_t *f = ecs_query_init(world, &desc); ``` The following table provides an overview of the query types with the init/fini functions: | Kind | Type | Init | Fini | Descriptor type | |--------|----------------|-------------------|-------------------|---------------------| -| Filter | `ecs_filter_t` | `ecs_filter_init` | `ecs_filter_fini` | `ecs_filter_desc_t` | -| Query | `ecs_query_t` | `ecs_query_init` | `ecs_query_fini` | `ecs_query_desc_t` | -| Rule | `ecs_rule_t` | `ecs_rule_init` | `ecs_rule_fini` | `ecs_filter_desc_t` | +| Query | `ecs_query_t` | `ecs_query_init` | `ecs_query_fini` | `ecs_query_desc_t` | +| Query | `ecs_query_cache_t` | `ecs_query_cache_init` | `ecs_query_fini` | `ecs_query_desc_t` | +| Rule | `ecs_query_impl_t` | `ecs_query_init` | `ecs_query_fini` | `ecs_query_desc_t` | -Additionally the descriptor types for systems (`ecs_system_desc_t`) and observers (`ecs_observer_desc_t`) embed the `ecs_filter_desc_t` descriptor type. +Additionally the descriptor types for systems (`ecs_system_desc_t`) and observers (`ecs_observer_desc_t`) embed the `ecs_query_desc_t` descriptor type. ### Query Builder (C++) Query builders are the C++ API for creating queries. The builder API is built on top of the descriptor API, and adds a layer of convenience and type safety that matches modern idiomatic C++. The builder API is implemented for all query kinds (filters, cached queries, rules). An example of a simple query: ```cpp -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); ``` Queries created with template arguments provide a type safe way to iterate components: @@ -213,7 +213,7 @@ f.each([](Position& p, const Velocity& v) { The builder API allows for incrementally constructing queries: ```cpp -flecs::filter q = world.filter_builder(); +flecs::query q = world.query_builder(); f.term(); if (add_npc) { @@ -227,9 +227,9 @@ The following table provides an overview of the query types with the factory fun | Kind | Type | Factory | |--------|-----------------|-------------------------| -| Filter | `flecs::filter` | `world::filter_builder` | +| Query | `flecs::query` | `world::filter_builder` | | Query | `flecs::query` | `world::query_builder` | -| Rule | `flecs::rule` | `world::rule_builder` | +| Rule | `flecs::query` | `world::rule_builder` | Additional helper methods have been added to the C++ API to replace combinations of the `term` method with other methods. They are the following: @@ -256,41 +256,39 @@ ECS_SYSTEM(world, Move, EcsOnUpdate, Position, [in] Velocity); Queries can be created from expressions with both the descriptor and builder APIs: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .expr = "Position, [in] Velocity" }); ``` ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .expr("Position, [in] Velocity") .build(); ``` -The query DSL requires the `FLECS_PARSER` addon to be included in a build. - ## Iteration This section describes the different ways queries can be iterated. The code examples use filters, but also apply to cached queries and rules. ### Iterators (C) -In the C API an iterator object of type `ecs_iter_t` can be created for each of the query kinds, using the `ecs_filter_iter`, `ecs_query_iter` and `ecs_rule_iter` functions. This iterator can then be iterated with the respective `next` functions: `ecs_filter_next`, `ecs_query_next` and `ecs_rule_next`. +In the C API an iterator object of type `ecs_iter_t` can be created for each of the query kinds, using the `ecs_query_iter`, `ecs_query_iter` and `ecs_query_iter` functions. This iterator can then be iterated with the respective `next` functions: `ecs_query_next`, `ecs_query_next` and `ecs_query_next`. An iterator can also be iterated with the `ecs_iter_next` function which is slightly slower, but does not require knowledge about the source the iterator was created for. An example: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }, } }); -ecs_iter_t it = ecs_filter_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, f); // Outer loop: matching tables -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); // 1st term - Velocity *v = ecs_field(&it, Velocity, 2); // 2nd term +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); // 1st term + Velocity *v = ecs_field(&it, Velocity, 1); // 2nd term // Inner loop: entities in table for (int i = 0; i < it.count; i ++) { @@ -305,10 +303,10 @@ Iteration is split up into two loops: the outer loop which iterates tables, and The indices provided to the `ecs_field` function must correspond with the order in which terms have been specified in the query. This index starts counting from `1`, with index `0` reserved for the array containing entity ids. ### Each (C++) -The `each` function is the default and often fastest approach for iterating a query in C++. `each` can be called directly on a `flecs::filter`, `flecs::query` and `flecs::rule`. An example: +The `each` function is the default and often fastest approach for iterating a query in C++. `each` can be called directly on a `flecs::query`, `flecs::query` and `flecs::query`. An example: ```cpp -auto f = world.filter(); +auto f = world.query(); f.each([](Position& p, const Velocity& v) { p.x += v.x; @@ -319,7 +317,7 @@ f.each([](Position& p, const Velocity& v) { A `flecs::entity` can be added as first argument: ```cpp -auto f = world.filter(); +auto f = world.query(); f.each([](flecs::entity e, Position& p) { std::cout << e.name() << ": " @@ -331,14 +329,14 @@ f.each([](flecs::entity e, Position& p) { A `flecs::iter` and `size_t` argument can be added as first arguments. This variant of `each` provides access to the `flecs::iter` object, which contains more information about the object being iterated. The `size_t` argument contains the index of the entity being iterated, which can be used to obtain entity-specific data from the `flecs::iter` object. An example: ```cpp -auto f = world.filter_builder() +auto f = world.query_builder() .term(Likes, flecs::Wildcard) .build(); f.each([](flecs::iter& it, size_t index, Position& p) { flecs::entity e = it.entity(index); std::cout << e.name() << ": " - << it.id(2).str() // prints pair + << it.id(1).str() // prints pair << std::endl; }); ``` @@ -348,7 +346,7 @@ When a query contains a template argument that is an empty type (a struct withou ```cpp struct Tag { }; -auto f = world.filter(); +auto f = world.query(); f.each([](flecs::entity e, Tag) { std::cout << e.name() << std::endl; @@ -360,7 +358,7 @@ Alternatively an empty type can be specified outside of the query type, which re ```cpp struct Tag { }; -auto f = world.filter_builder() +auto f = world.query_builder() .term() .build(); @@ -375,7 +373,7 @@ The `iter` function has an outer and inner loop (similar to C iterators) which p An example: ```cpp -auto f = world.filter(); +auto f = world.query(); f.iter([](flecs::iter& it, Position *p, Velocity *v) { // Inner loop @@ -403,11 +401,11 @@ f.iter([](flecs::iter& it, Position *p, Velocity *v) { The component arguments may be omitted, and can be obtained from the iterator object: ```cpp -auto f = world.filter(); +auto f = world.query(); f.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); + auto p = it.field(0); + auto v = it.field(1); for (auto i : it) { p[i].x += v[i].x; @@ -419,11 +417,11 @@ f.iter([](flecs::iter& it) { This can be combined with an untyped variant of the `field` method to access component data without having to know component types at compile time. This can be useful for generic code, like serializers: ```cpp -auto f = world.filter(); +auto f = world.query(); f.iter([](flecs::iter& it) { - void *ptr = it.field(1); - flecs::id type_id = it.id(1); + void *ptr = it.field(0); + flecs::id type_id = it.id(0); // ... }); @@ -435,7 +433,7 @@ Entities can be moved between tables when components are added or removed. This When an application attempts to add or remove components to an entity in a table being iterated over, this can throw a runtime assert. An example: ```cpp -auto f = world.filter(); +auto f = world.query(); f.each([](flecs::entity e, Position&) { e.add(); // throws locked table assert @@ -445,7 +443,7 @@ f.each([](flecs::entity e, Position&) { This can be addressed by deferring operations while the query is being iterated: ```cpp -auto f = world.filter(); +auto f = world.query(); world.defer([&]{ f.each([](flecs::entity e, Position&) { @@ -457,7 +455,7 @@ world.defer([&]{ An application can also use the `defer_begin` and `defer_end` functions which achieve the same goal: ```cpp -auto f = world.filter(); +auto f = world.query(); world.defer_begin(); @@ -505,7 +503,7 @@ To query for a component in C, the `id` field of a term can be set: ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_id(Position) }, { .id = ecs_id(Velocity) } @@ -516,7 +514,7 @@ ecs_filter_t *f = ecs_filter(world, { The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } @@ -528,9 +526,9 @@ The `ecs_id` macro converts the component typename into the variable name that h ```c ECS_TAG(world, Npc); -ecs_entity_t Platoon_01 = ecs_new_id(world); +ecs_entity_t Platoon_01 = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { Npc }, { Platoon_01 } @@ -544,7 +542,7 @@ Components can also be queried for by name by setting the `.first.name` member i ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { .first.name = "Position" }, { .first.name = "Velocity" } @@ -556,8 +554,8 @@ ecs_filter_t *f = ecs_filter(world, { An easy way to query for components in C++ is to pass them as template arguments to the query factory function: ```cpp -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); ``` This changes the returned query type, which determines the type of the function used to iterate the query: @@ -569,8 +567,8 @@ f.each([](Position& p, const Velocity& v) { }); The builder API makes it possible to add components to a query without modifying the query type: ```cpp -flecs::filter f = - world.filter_builder() +flecs::query f = + world.query_builder() .term() .build(); ``` @@ -583,7 +581,7 @@ The builder API makes it possible to query for regular entity ids created at run flecs::entity Npc = world.entity(); flecs::entity Platoon_01 = world.entity(); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(Npc) .term(Platoon_01) .build(); @@ -598,7 +596,7 @@ world.component(); // Create entity with name so we can look it up flecs::entity Npc = world.entity("Npc"); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term("Position") .term("Npc") .build(); @@ -656,11 +654,11 @@ The `Wildcard` wildcard returns an individual result for anything that it matche ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { EcsWildcard } } @@ -671,7 +669,7 @@ flecs::entity e = world.entity() .add() .add(); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(flecs::Wildcard) .build(); ``` @@ -682,11 +680,11 @@ The `Any` wildcard returns a single result for the first component that it match ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { EcsAny } } @@ -697,7 +695,7 @@ flecs::entity e = world.entity() .add() .add(); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(flecs::Any) .build(); ``` @@ -719,10 +717,10 @@ The following sections describe how to create queries for pairs in the different To query for a pair in C, the `id` field of a term can be set to a pair using the `ecs_pair` macro: ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { .id = ecs_pair(Likes, Bob) } } @@ -732,10 +730,10 @@ ecs_filter_t *f = ecs_filter(world, { The `id` field is guaranteed to be the first member of a term, which allows the previous code to be rewritten in this shorter form: ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_pair(Likes, Bob) } } @@ -746,9 +744,9 @@ When an element of the pair is a component type, use the `ecs_id` macro to obtai ```c ECS_COMPONENT(world, Eats); -ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Apples = ecs_new(world); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_pair(ecs_id(Eats), Apples) } } @@ -758,13 +756,13 @@ ecs_filter_t *f = ecs_filter(world, { The `ecs_isa`, `ecs_childof` and `ecs_dependson` convenience macros can be used to create pairs for builtin relationships. The two queries in the next example are equivalent: ```c -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *f_1 = ecs_query(world, { .terms = { { ecs_pair(EcsChildOf, parent) } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *f_2 = ecs_query(world, { .terms = { { ecs_childof(parent) } } @@ -774,7 +772,7 @@ ecs_filter_t *f_2 = ecs_filter(world, { Pair queries can be created by setting their individual elements in the `first.id` and `second.id` members of a term: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { .first.id = Eats, .second.id = Apples } } @@ -788,13 +786,13 @@ Alternatively, one or both elements of a pair can be resolved by name. The two q ECS_TAG(world, Eats); ECS_TAG(world, Apples); -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *f_1 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.id = Apples } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *f_2 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.name = "Apples" } } @@ -804,15 +802,15 @@ ecs_filter_t *f_2 = ecs_filter(world, { When a query pair contains a wildcard, the `ecs_field_id` function can be used to determine the id of the pair element that matched the query: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_pair(Likes, EcsWildcard) } } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - ecs_id_t id = ecs_field_id(&it, 1); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + ecs_id_t id = ecs_field_id(&it, 0); ecs_entity_t second = ecs_pair_second(world, id); for (int i = 0; i < it.count; i ++) { @@ -832,8 +830,8 @@ struct Apples { }; // Alias to save typing using EatsApples = flecs::pair; -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); // Do not use reference argument for pair f.each([](EatsApples v) { @@ -845,8 +843,8 @@ f.each([](EatsApples v) { When using the `iter` function to iterate a query with a pair template, the argument type assumes the type of the pair. This is required as the component array being passed directly to the `iter` function. An example: ```cpp -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); f.iter([](flecs::iter& it, Eats *v) { for (auto i : it) { @@ -864,15 +862,15 @@ struct Apples { }; flecs::entity eats = world.component(); flecs::entity apples = world.component(); -flecs::filter<> f_1 = world.filter_builder() +flecs::query<> f_1 = world.query_builder() .term() .build(); -flecs::filter<> f_2 = world.filter_builder() +flecs::query<> f_2 = world.query_builder() .term(apples) .build(); -flecs::filter<> f_3 = world.filter_builder() +flecs::query<> f_3 = world.query_builder() .term(eats, apples) .build(); ``` @@ -880,7 +878,7 @@ flecs::filter<> f_3 = world.filter_builder() Individual elements of a pair can be specified with the `first` and `second` methods. The methods apply to the last added term. An example: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().first().second(apples) .build(); ``` @@ -888,7 +886,7 @@ flecs::filter<> f = world.filter_builder() Individual elements of a pair can be resolved by name by using the `first` and `second` methods: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().first("Eats").second("Apples") .build(); ``` @@ -896,12 +894,12 @@ flecs::filter<> f = world.filter_builder() When a query pair contains a wildcard, the `flecs::iter::pair` method can be used to determine the id of the pair element that matched the query: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(flecs::Wildcard) .build(); f.each([](flecs::iter& it, size_t index) { - flecs::entity second = it.pair(1).second(); + flecs::entity second = it.pair(0).second(); flecs::entity e = it.entity(index); std::cout << "entity " << e.name() @@ -969,7 +967,7 @@ The following sections show how to use access modifiers in the different languag Access modifiers can be set using the `inout` member: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .inout = EcsIn } @@ -981,7 +979,7 @@ ecs_filter_t *f = ecs_filter(world, { Access modifiers can be set using the `inout` method: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().inout(flecs::In) .build(); @@ -991,14 +989,14 @@ When the `const` modifier is added to a type, the `flecs::In` modifier is automa ```c // Velocity term will be added with flecs::In modifier -flecs::filter f = - world.filter(); +flecs::query f = + world.query(); ``` This also applies to types added with `term`: ```c -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term() // uses flecs::In modifier .build(); @@ -1007,23 +1005,23 @@ flecs::filter<> f = world.filter_builder() When a component is added by the `term` method and retrieved from a `flecs::iter` object during iteration, it must meet the constraints of the access modifiers. If the constraints are not met, a runtime assert may be thrown: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().inout(flecs::In) .build(); f.iter([](flecs::iter& it) { - auto p = it.field(1); // OK - auto p = it.field(1); // OK - auto v = it.field(2); // OK - auto v = it.field(2); // Throws assert + auto p = it.field(0); // OK + auto p = it.field(0); // OK + auto v = it.field(1); // OK + auto v = it.field(1); // Throws assert }); ``` The builder API has `in()`, `inout()`, `out()` and `inout_none()` convenience methods: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().inout() .term().in() .build(); @@ -1063,14 +1061,14 @@ The `And` operator is used when no other operators are specified. The following When no operator is specified, `And` is assumed. The following two queries are equivalent: ```c -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *f_1 = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *f_2 = ecs_query(world, { .terms = { { ecs_id(Position), .oper = EcsAnd }, { ecs_id(Velocity), .oper = EcsAnd } @@ -1082,14 +1080,14 @@ ecs_filter_t *f_2 = ecs_filter(world, { When no operator is specified, `And` is assumed. The following two queries are equivalent: ```cpp -flecs::filter f_1 = world.filter(); +flecs::query f_1 = world.query(); -flecs::filter<> f_2 = world.filter_builder() +flecs::query<> f_2 = world.query_builder() .term() .term() .build(); -flecs::filter<> f_2 = world.filter_builder() +flecs::query<> f_2 = world.query_builder() .term().oper(flecs::And) .term().oper(flecs::And) .build(); @@ -1098,7 +1096,7 @@ flecs::filter<> f_2 = world.filter_builder() The builder API has a `and_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().and_(); // note escaping, 'and' is a C++ keyword .term().and_(); .build(); @@ -1131,7 +1129,7 @@ To create a query with `Or` terms, set `oper` to `EcsOr`: ```c // Position, Velocity || Speed, Mass -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsOr }, @@ -1140,12 +1138,12 @@ ecs_filter_t *f = ecs_filter(world, { } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Mass *m = ecs_field(&it, Mass, 3); // not 4, because of the Or expression +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Mass *m = ecs_field(&it, Mass, 2); // not 4, because of the Or expression - ecs_id_t vs_id = ecs_field_id(&it, 2); + ecs_id_t vs_id = ecs_field_id(&it, 1); if (vs_id == ecs_id(Velocity)) { // We can only use ecs_field if the field type is the same for all results, // but we can get the table column directly. @@ -1163,7 +1161,7 @@ To create a query with `Or` terms, use the `oper` method with `flecs::Or`: ```cpp // Position, Velocity || Speed, Mass -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().oper(flecs::Or) .term() @@ -1171,10 +1169,10 @@ flecs::filter<> f = world.filter_builder() .build(); f.iter([&](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(3); // not 4, because of the Or expression + auto p = it.field(0); + auto v = it.field(2); // not 4, because of the Or expression - flecs::id vs_id = it.id(2); + flecs::id vs_id = it.id(1); if (vs_id == world.id()) { // We can only use ecs_field if the field type is the same for all results, // but we can use range() to get the table column directly. @@ -1190,7 +1188,7 @@ f.iter([&](flecs::iter& it) { The builder API has a `or_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().or_(); // note escaping, 'or' is a C++ keyword .term() @@ -1218,7 +1216,7 @@ The following sections show how to use the `Not` operator in the different langu To create a query with `Not` terms, set `oper` to `EcsNot`: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsNot } @@ -1230,7 +1228,7 @@ ecs_filter_t *f = ecs_filter(world, { To create a query with `Not` terms, use the `oper` method with `flecs::Not`: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().oper(flecs::Not) .build(); @@ -1239,7 +1237,7 @@ flecs::filter<> f = world.filter_builder() The builder API has a `not_` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().not_(); // note escaping, 'not' is a C++ keyword .build(); @@ -1273,18 +1271,18 @@ The following sections show how to use the `Optional` operator in the different To create a query with `Optional` terms, set `oper` to `EcsOptional`: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity), .oper = EcsOptional } } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - if (ecs_field_is_set(&it, 2)) { - Velocity *v = ecs_field(&it, Velocity, 2); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + if (ecs_field_is_set(&it, 1)) { + Velocity *v = ecs_field(&it, Velocity, 1); // iterate as usual } else { // iterate as usual @@ -1296,16 +1294,16 @@ while (ecs_filter_next(&it)) { To create a query with `Optional` terms, call the `oper` method with `flecs::Optional`: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().oper(flecs::Optional) .build(); f.iter([&](flecs::iter& it) { - auto p = it.field(1); + auto p = it.field(0); - if (it.is_set(2)) { - auto v = it.field(2); + if (it.is_set(1)) { + auto v = it.field(1); // iterate as usual } else if (vs_id == world.id()) { // iterate as usual @@ -1316,7 +1314,7 @@ f.iter([&](flecs::iter& it) { The builder API has an `optional` convenience method: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().optional(); .build(); @@ -1362,7 +1360,7 @@ Terms with equality operators return no data. #### Query Descriptor (C) ```c -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = { // $this == Foo { .first.id = EcsPredEq, .second.id = Foo }, @@ -1378,7 +1376,7 @@ ecs_rule_t *r = ecs_rule(world, { #### Query Builder (C++) ```cpp -world.rule_builder() +world.query_builder() // $this == Foo .with(flecs::PredEq, Foo) // $this != Foo @@ -1419,7 +1417,7 @@ ecs_entity_t type_list = ecs_new_w_id(world, EcsPrefab); ecs_add(world, type_list, Position); ecs_add(world, type_list, Velocity); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { type_list, .oper = EcsAndFrom }, // match Position, Velocity { type_list, .oper = EcsOrFrom }, // match Position || Velocity @@ -1436,7 +1434,7 @@ flecs::entity type_list = world.prefab() .add() .add(); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(type_list).oper(flecs::AndFrom) // match Position, Velocity .term(type_list).oper(flecs::OrFrom) // match Position || Velocity .term(type_list).oper(flecs::NotFrom) // match !Position, !Velocity @@ -1446,7 +1444,7 @@ flecs::filter<> f = world.filter_builder() The builder API has the `and_from`, `or_from` and `not_from` convenience methods: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term(type_list).and_from() .term(type_list).or_from() .term(type_list).not_from() @@ -1504,7 +1502,7 @@ The following examples show how to use scopes in the different language bindings #### Query Descriptor (C) ```c -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = { // Position, !{ Velocity || Speed } { .id = ecs_id(Position) }, @@ -1518,7 +1516,7 @@ ecs_rule_t *r = ecs_rule(world, { #### Query Builder (C++) ```cpp -world.rule_builder() +world.query_builder() // Position, !{ Velocity || Speed } .with() .scope_open().not_() @@ -1536,7 +1534,7 @@ Position, !{ Velocity || Speed } ### Source > *Supported by: filters, cached queries, rules* -Source is a property of a term that specifies the entity on which the term should be matched. Queries support two kinds of sources: static and variable. A static source is known when the query is created (for example: match `SimTime` on entity `Game`), whereas a variable source is resolved while the query is evaluated. When no explicit source is specified, a default variable source called `$This` is used (see [Variables](#variables)). +Source is a property of a term that specifies the entity on which the term should be matched. Queries support two kinds of sources: static and variable. A static source is known when the query is created (for example: match `SimTime` on entity `Game`), whereas a variable source is resolved while the query is evaluated. When no explicit source is specified, a default variable source called `$this` is used (see [Variables](#variables)). When a query only has terms with fixed sources, iterating the query will return a result at least once when it matches, and at most once if the query terms do not match wildcards. If a query has one or more terms with a fixed source that do not match the entity, the query will return no results. A source does not need to match the query when the query is created. @@ -1548,22 +1546,22 @@ The following sections show how to use variable and fixed sources with the diffe To specify a fixed source, set the `src.id` member to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: ```c -ecs_entity_t Game = ecs_new_id(world); +ecs_entity_t Game = ecs_new(world); ecs_add(world, Game, SimTime); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { - { ecs_id(Position) }, // normal term, uses $This source - { ecs_id(Velocity) }, // normal term, also uses $This source + { ecs_id(Position) }, // normal term, uses $this source + { ecs_id(Velocity) }, // normal term, also uses $this source { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game } }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Velocity *v = ecs_field(&it, Velocity, 2); - SimTime *st = ecs_field(&it, SimTime, 3); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); + SimTime *st = ecs_field(&it, SimTime, 2); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x * st[i].value; @@ -1582,36 +1580,36 @@ A source may also be specified by name by setting the `src.name` member: ecs_entity_t Game = ecs_entity(world, { .name = "Game" }); ecs_add(world, Game, SimTime); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(SimTime), .src.name = "Game" } } }); ``` -This examples shows how to access the entities matched by the default `$This` source and a fixed source: +This examples shows how to access the entities matched by the default `$this` source and a fixed source: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { - { ecs_id(Position) }, // normal term, uses $This source + { ecs_id(Position) }, // normal term, uses $this source { ecs_id(SimTime), .src.id = Game } // fixed source, match SimTime on Game } }); -while (ecs_filter_next(&it)) { - ecs_entity_t src_1 = ecs_field_src(&it, 1); // Returns 0, meaning entity is stored in it.entities - ecs_entity_t src_2 = ecs_field_src(&it, 2); // Returns Game +while (ecs_query_next(&it)) { + ecs_entity_t src_1 = ecs_field_src(&it, 0); // Returns 0, meaning entity is stored in it.entities + ecs_entity_t src_2 = ecs_field_src(&it, 1); // Returns Game for (int i = 0; i < it.count; i ++) { - printf("$This = %s, src_2 = %s\n", + printf("$this = %s, src_2 = %s\n", ecs_get_name(world, it.entities[i]), ecs_get_name(world, src_2)); } } ``` -The `entities` and `count` member are solely populated by the number of entities matched by the default `$This` source. If a query only contains fixed sources, `count` will be set to 0. This is important to keep in mind, as the inner for loop from the last example would never be iterated for a query that only has fixed sources. +The `entities` and `count` member are solely populated by the number of entities matched by the default `$this` source. If a query only contains fixed sources, `count` will be set to 0. This is important to keep in mind, as the inner for loop from the last example would never be iterated for a query that only has fixed sources. #### Query Builder (C++) To specify a fixed source, call the `src` method to the entity to match. The following example shows how to set a source, and how to access the value provided by a term with a fixed source: @@ -1620,16 +1618,16 @@ To specify a fixed source, call the `src` method to the entity to match. The fol flecs::entity Game = world.entity() .add(); -flecs::filter<> f = world.filter_builder() - .term() // normal term, uses $This source - .term() // normal term, also uses $This source +flecs::query<> f = world.query_builder() + .term() // normal term, uses $this source + .term() // normal term, also uses $this source .term().src(Game) // fixed source, match SimTime on Game .build(); f.iter([](flecs::iter& it) { - auto p = it.field(1); - auto v = it.field(2); - auto st = it.field(3); + auto p = it.field(0); + auto v = it.field(1); + auto st = it.field(2); for (auto i : it) { p[i].x += v[i].x * st[i].value; @@ -1642,12 +1640,12 @@ Note how in this example all components can be accessed as arrays. When a query Returning entities one at a time can negatively affect performance, especially for large tables. To learn more about why this behavior exists and how to ensure that mixed results use table-based iteration, see [Instancing](#instancing). -The next example shows how queries with mixed `$This` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `iter` example, as it uses [instancing](#instancing) by default. +The next example shows how queries with mixed `$this` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `iter` example, as it uses [instancing](#instancing) by default. ```cpp -flecs::filter f = - world.filter_builder() - .arg(3).src(Game) // set fixed source for 3rd template argument (SimTime) +flecs::query f = + world.query_builder() + .term_at(2).src(Game) // set fixed source for 3rd template argument (SimTime) .build(); // Because all components are now part of the filter type, we can use each @@ -1657,13 +1655,13 @@ f.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { }); ``` -When a query has no terms for the `$This` source, it must be iterated with the `iter` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: +When a query has no terms for the `$this` source, it must be iterated with the `iter` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: ```cpp -flecs::filter f = - world.filter_builder() - .arg(1).src(Cfg) - .arg(2).src(Game) +flecs::query f = + world.query_builder() + .term_at(0).src(Cfg) + .term_at(1).src(Game) .build(); // Ok (note that it.count() will be 0) @@ -1690,15 +1688,15 @@ f.each([](flecs::entity e, SimConfig& sc, SimTime& st) { A source may also be specified by name: ```cpp -flecs::filter f = - world.filter_builder() - .arg(1).src("Cfg") - .arg(2).src("Game") +flecs::query f = + world.query_builder() + .term_at(0).src("Cfg") + .term_at(1).src("Game") .build(); ``` #### Query DSL -To specify a source in the DSL, use parenthesis after the component identifier. The following example uses the default `$This` source for `Position` and `Velocity`, and `Game` as source for `SimTime`. +To specify a source in the DSL, use parenthesis after the component identifier. The following example uses the default `$this` source for `Position` and `Velocity`, and `Game` as source for `SimTime`. ``` Position, Velocity, SimTime(Game) @@ -1707,10 +1705,10 @@ Position, Velocity, SimTime(Game) In the previous example the source for `Position` and `Velocity` is implicit. The following example shows the same query with explicit sources for all terms: ``` -Position($This), Velocity($This), SimTime(Game) +Position($this), Velocity($this), SimTime(Game) ``` -To specify a source for a pair, the second element of the pair is placed inside the parenthesis after the source. The following query uses the default `$This` source for the `(Color, Diffuse)` pair, and `Game` as source for the `(Color, Sky)` pair. +To specify a source for a pair, the second element of the pair is placed inside the parenthesis after the source. The following query uses the default `$this` source for the `(Color, Diffuse)` pair, and `Game` as source for the `(Color, Sky)` pair. ``` (Color, Diffuse), Color(Game, Sky) @@ -1719,7 +1717,7 @@ To specify a source for a pair, the second element of the pair is placed inside In the previous example the source for `(Color, Diffuse)` is implicit. The following example shows the same query with explicit sources for all terms: ``` -Color($This, Diffuse), Color(Game, Sky) +Color($this, Diffuse), Color(Game, Sky) ``` ### Singletons @@ -1733,7 +1731,7 @@ The following sections show how to use singletons in the different language bind A singleton query is created by specifying the same id as component and source: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { Player }, { ecs_id(Position) }, @@ -1748,7 +1746,7 @@ The singleton component data is accessed in the same way a component from a stat A singleton query can be created by specifying the same id as component and source: ```cpp -flecs::filter f = world.filter_builder() +flecs::query f = world.query_builder() .term().src() // match Input on itself .build(); ``` @@ -1756,7 +1754,7 @@ flecs::filter f = world.filter_builder() The builder API provides a `singleton` convenience function: ```cpp -flecs::filter f = world.filter_builder() +flecs::query f = world.query_builder() .term().singleton() // match Input on itself .build(); ``` @@ -1800,8 +1798,6 @@ Traversal behavior can be customized with the following bitflags, in addition to |----------|----------------|---------------|-------------------|-------------| | Self | `self` | `EcsSelf` | `flecs::Self` | Match self | | Up | `up` | `EcsUp` | `flecs::Up` | Match by traversing upwards | -| Down | `down` | `EcsDown` | `flecs::Down` | Match by traversing downwards (derived, cannot be set) | -| Parent | `parent` | `EcsParent` | `flecs::Parent` | Short for up(ChildOf) | | Cascade | `cascade` | `EcsCascade` | `flecs::Cascade` | Same as Up, but iterate in breadth-first order | | Desc | `desc` | `EcsDesc` | `flecs::Desc` | Combine with Cascade to iterate hierarchy bottom to top | @@ -1820,7 +1816,7 @@ flecs::entity child = world.entity() .add(flecs::ChildOf, parent); // This filter matches 'child', because it has a parent that inherits Mass -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().up(flecs::ChildOf) .build(); ``` @@ -1850,10 +1846,10 @@ The following sections show how to use traversal in the different language bindi By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: ```c -ecs_entity_t base = ecs_new(world, Position); +ecs_entity_t base = ecs_new_w(world, Position); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); // Inherits Position -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) } } @@ -1863,7 +1859,7 @@ ecs_filter_t *f = ecs_filter(world, { Implicit traversal can be disabled by setting the `flags` member to `EcsSelf`. The following example only matches `base`: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position), .src.flags = EcsSelf } } @@ -1873,12 +1869,12 @@ ecs_filter_t *f = ecs_filter(world, { To use a different relationship for traversal, use the `trav` member in combination with the `EcsUp` flag. The following example only matches `child`: ```c -ecs_entity_t parent = ecs_new(world, Position); -ecs_entity_t child = ecs_new(world, Position); +ecs_entity_t parent = ecs_new_w(world, Position); +ecs_entity_t child = ecs_new_w(world, Position); ecs_add_pair(world, child, EcsChildOf, parent); -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { // term matches parent & child { ecs_id(Position) }, @@ -1891,7 +1887,7 @@ ecs_filter_t *f = ecs_filter(world, { The `EcsParent` flag can be used which is shorthand for `EcsUp` with `EcsChildOf`. The query in the following example is equivalent to the one in the previous example: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Position), .src.flags = EcsParent } @@ -1902,7 +1898,7 @@ ecs_filter_t *f = ecs_filter(world, { If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Position), .src.flags = EcsParent, .oper = EcsOptional } @@ -1914,7 +1910,7 @@ By adding the `EcsCascade` flag, a query will iterate the hierarchy top-down. Th ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { ecs_id(Position) }, { ecs_id(Position), .src.flags = EcsCascade|EcsParent, .oper = EcsOptional } } @@ -1924,7 +1920,7 @@ ecs_query_t *q = ecs_query(world, { Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Window), .src.flags = EcsParent, .src.id = my_widget } } @@ -1934,17 +1930,17 @@ ecs_filter_t *f = ecs_filter(world, { The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: ```cpp -ecs_filter_t *f_1 = ecs_filter(world, { +ecs_query_t *f_1 = ecs_query(world, { .terms = { { ecs_id(Position) } } }); -ecs_filter_t *f_2 = ecs_filter(world, { +ecs_query_t *f_2 = ecs_query(world, { .terms = {{ .id = ecs_id(Position), // match Position .src.flags = EcsSelf | EcsUp // first match self, traverse upwards while not found - .src.trav = EcsIsA, // traverse using the IsA relationship + .trav = EcsIsA, // traverse using the IsA relationship }} }); ``` @@ -1959,13 +1955,13 @@ flecs::entity base = world.entity() flecs::entity inst = world.entity() .is_a(base); // short for .add(flecs::IsA, base) -flecs::filter<> f = world.filter(); +flecs::query<> f = world.query(); ``` Implicit traversal can be disabled by calling the `self` method for the term. The following example only matches `base`: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().self() .build(); ``` @@ -1979,7 +1975,7 @@ flecs::entity parent = world.entity() flecs::entity child = world.entity() .child_of(parent); // short for .add(flecs::ChildOf, parent) -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() // term matches parent & child .term() // term just matches child, parent does not have a parent with Position @@ -1990,7 +1986,7 @@ flecs::filter<> f = world.filter_builder() The `parent` method can be used which is shorthand for `up(flecs::ChildOf)`. The query in the following example is equivalent to the one in the previous example: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().parent() .build(); @@ -1999,7 +1995,7 @@ flecs::filter<> f = world.filter_builder() If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() .term().parent().optional() .build(); @@ -2017,7 +2013,7 @@ flecs::query<> q = world.query_builder() Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: ```cpp -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term().src(my_widget).parent() .build(); ``` @@ -2025,9 +2021,9 @@ flecs::filter<> f = world.filter_builder() The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: ```cpp -flecs::filter<> f = world.filter(); +flecs::query<> f = world.query(); -flecs::filter<> f = world.filter_builder() +flecs::query<> f = world.query_builder() .term() // match Position .self() // first match self .up(flecs::IsA) // traverse IsA upwards while not found @@ -2068,7 +2064,7 @@ Position, ?Position(parent) By adding the `cascade` keyword, a query will iterate the `ChildOf` hierarchy top-down. The `cascade` feature is only supported by cached queries: ``` -Position, ?Position(parent|cascade) +Position, ?Position(cascade) ``` Relationship traversal can be combined with fixed [source](#source) terms, by using a colon (`:`) to separate the traversal flags and the source identifier. The following query matches if the `my_widget` entity has a parent with the `Window` component: @@ -2121,7 +2117,7 @@ flecs::entity ent_2 = world.entity("ent_2") .set({30}) .set({50, 60}); -flecs::filter f = world.filter(); +flecs::query f = world.query(); ``` The filter in this example will match both `inst_1`, `inst_2` because they inherit `Mass`, and `ent_1` and `ent_2` because they own `Mass`. The following example shows an example of code that iterates the filter: @@ -2199,7 +2195,7 @@ The following sections show how to use instancing in the different language bind Queries can be instanced by setting the `instanced` member to true: ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms = { { ecs_id(Position), src.flags = EcsSelf }, // Never inherit Position { ecs_id(Mass) } @@ -2210,13 +2206,13 @@ ecs_filter_t *f = ecs_filter(world, { .instanced = true }); -ecs_iter_t it = ecs_filter_iter(world, &it); -while (ecs_filter_next(&it)) { +ecs_iter_t it = ecs_query_iter(world, &it); +while (ecs_query_next(&it)) { // Fetch components as usual - Position *p = ecs_field(&it, Position, 1); - Mass *m = ecs_field(&it, Mass, 2); + Position *p = ecs_field(&it, Position, 0); + Mass *m = ecs_field(&it, Mass, 1); - if (ecs_field_is_self(&it, 2)) { + if (ecs_field_is_self(&it, 1)) { // Mass is matched on self, access as array for (int i = 0; i < it.count; i ++) { p[i].x += 1.0 / m[i].value; @@ -2238,16 +2234,16 @@ Note how the `ecs_field_is_self` test is moved outside of the for loops. This ke Queries can be instanced by calling the `instanced` method: ```cpp -flecs::filter f = world.filter_builder() +flecs::query f = world.query_builder() // Never inherit Position - .arg(1).self() + .term_at(0).self() // Instancing is a property of the iterator, but by setting it on the query // all iterators created for the query will be instanced. .instanced() .build(); f.iter([](flecs::iter& it, Position *p, Mass *v) { - if (it.is_self(2)) { + if (it.is_self(1)) { // Mass is matched on self, access as array for (auto i : it) { p[i].x += 1.0 / m[i].value; @@ -2273,12 +2269,12 @@ Query variables represent the state of a query while it is being evaluated. The Consider this query example, written down with explicit term [sources](#source): ``` -Position($This), Velocity($This) +Position($this), Velocity($this) ``` The first term to encounter a variable is usually the one to populate it with all candidates that could match that term. Subsequent terms then use the already populated variable to test if it matches. If the condition matches, the query moves on to the next term. If the condition fails, the query moves back to the previous term and, if necessary, populates the variable with the next candidate. These kinds of conditions are usually referred to as [predicates](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)), and this evaluation process is called [backtracking](https://en.wikipedia.org/wiki/Backtracking). -This process effectively _constrains_ the possible results that a term could yield. By itself, the `Velocity` term would return all entities with the `Velocity` component, but because `$This` has been assigned already with entities that have `Position`, the term only feeds forward entities that have both `Position` and `Velocity`. +This process effectively _constrains_ the possible results that a term could yield. By itself, the `Velocity` term would return all entities with the `Velocity` component, but because `$this` has been assigned already with entities that have `Position`, the term only feeds forward entities that have both `Position` and `Velocity`. While using variables as [source](#source) is the most common application for variables, variables can be used in any part of the term. Consider constructing a query for all spaceships that are docked to a planet. A first attempt could look like this: @@ -2289,24 +2285,24 @@ SpaceShip, (DockedTo, *) When rewritten with explicit sources, the query looks like this: ``` -SpaceShip($This), DockedTo($This, *) +SpaceShip($this), DockedTo($this, *) ``` This returns all spaceships that are docked to _anything_, instead of docked to planets. To constrain the result of this query, the wildcard used as target for the `DockedTo` relationship can be replaced with a variable. An example: ``` -SpaceShip($This), DockedTo($This, $Location) +SpaceShip($this), DockedTo($this, $Location) ``` When the second term is evaluated for the first time, `$Location` will not yet be populated. This causes the term to do two things: -1. Test if the entity/table populated in `$This` has `(DockedTo, *)` +1. Test if the entity/table populated in `$this` has `(DockedTo, *)` 2. If so, populate `$Location` with the id matched by `*`. After evaluating the second term, the `$Location` variable is populated with the location the spaceship is docked to. We can now use this variable in a new term, that constrains the location to only entities that have `Planet`: ``` -SpaceShip($This), DockedTo($This, $Location), Planet($Location) +SpaceShip($this), DockedTo($this, $Location), Planet($Location) ``` This query returns the desired result ("return all spaceships docked to a planet"). @@ -2314,7 +2310,7 @@ This query returns the desired result ("return all spaceships docked to a planet Variables can also be used to constrain matched components. Consider the following example query: ``` -Serializable($Component), $Component($This) +Serializable($Component), $Component($this) ``` This query returns serializable components for all entities that have at least one. @@ -2323,19 +2319,19 @@ This query returns serializable components for all entities that have at least o By default variables are assigned while the query is being iterated, but variables can be set before query iteration to constrain the results of a query. Consider the previous example: ``` -SpaceShip($This), DockedTo($This, $Location) +SpaceShip($this), DockedTo($this, $Location) ``` -An application can set the `$This` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. +An application can set the `$this` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. -The following sections show how to use queries in the different language bindings. The code examples use rules queries, which currently are the only queries that support using variables other than `$This`. +The following sections show how to use queries in the different language bindings. The code examples use rules queries, which currently are the only queries that support using variables other than `$this`. #### Query Descriptor (C) Query variables can be specified by setting the `name` member in combination with setting the `EcsIsVariable` bit in the `flags` member: ```c // SpaceShip, (DockedTo, $Location), Planet($Location) -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = { { .id = SpaceShip }, { @@ -2359,13 +2355,13 @@ ecs_rule_t *r = ecs_rule(world, { An application can constrain the results of the query by setting the variable before starting iteration: ```c -ecs_entity_t earth = ecs_new(world, Planet); +ecs_entity_t earth = ecs_new_w(world, Planet); // Find index for Location variable -int32_t location_var = ecs_rule_find_var(r, "Location"); +int32_t location_var = ecs_query_find_var(r, "Location"); // Constrain results of iterator to return spaceships docked to Earth -ecs_iter_t it = ecs_rule_iter(world, r); +ecs_iter_t it = ecs_query_iter(world, r); ecs_iter_set_var(&it, location_var, earth); // Iterate as usual @@ -2375,7 +2371,7 @@ ecs_iter_set_var(&it, location_var, earth); Query variables can be specified by specifying a name with a `$` prefix: ```cpp -auto r = world.rule_builder() +auto r = world.query_builder() .term() .term().second("$Location") .term().src("$Location") @@ -2385,7 +2381,7 @@ auto r = world.rule_builder() Alternatively, variables can also be specified using the `var` method: ```cpp -auto r = world.rule_builder() +auto r = world.query_builder() .term() .term().second().var("Location") .term().src().var("Location") @@ -2450,13 +2446,13 @@ The following example shows how the change detection API is used in C: ```c // Query used for change detection. Note that change detection is not enabled on // the query itself, but by calling change detection functions for the query. -ecs_query_t *q_read = ecs_query(world, { - .filter.terms = {{ .id = ecs_id(Position), .inout = EcsIn }} +ecs_query_cache_t *q_read = ecs_query(world, { + .terms = {{ .id = ecs_id(Position), .inout = EcsIn }} }); // Query used to create changes -ecs_query_t *q_write = ecs_query(world, { - .filter.terms = {{ .id = ecs_id(Position) }} // defaults to inout +ecs_query_cache_t *q_write = ecs_query(world, { + .terms = {{ .id = ecs_id(Position) }} // defaults to inout }); // Test if changes have occurred for anything matching the query. If this is the @@ -2464,7 +2460,7 @@ ecs_query_t *q_write = ecs_query(world, { bool changed = ecs_query_changed(q_read, NULL); // Setting a component will update the changed state -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_set(world, e, Position, {10, 20}); // Iterating a query with inout/out terms will update the change state @@ -2473,7 +2469,7 @@ while (ecs_query_next(&it)) { if (dont_change) { // If no changes are made to the iterated table, the skip function can be // called to prevent marking the matched components as dirty. - ecs_query_skip(&it); + ecs_iter_skip(&it); } else { // Iterate as usual. It does not matter whether the code actually writes the // components or not: when a table is not skipped, components matched with @@ -2579,12 +2575,12 @@ The following example shows how to use sorted queries in C: ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { // Use readonly term for component used for sorting { ecs_id(Depth), .inout = EcsIn } }, - .order_by_component = ecs_id(Depth), // The component to use for sorting - .order_by = compare_depth, + .order_by = ecs_id(Position), // The component to use for sorting + .order_by_callback = compare_position, }); ``` @@ -2598,14 +2594,14 @@ int compare_depth(ecs_entity_t e1, const void *v1, ecs_entity_t e2, const void * } ``` -A query may only use entity identifiers to sort by not setting the `order_by_component` member: +A query may only use entity identifiers to sort by not setting the `order_by` member: ```c ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { ecs_id(Position) }, }, - .order_by = compare_entity, + .order_by_callback = compare_entity, }); ``` @@ -2680,11 +2676,11 @@ The following sections show how to use sorting in the different language binding The following example shows how grouping can be used to group entities that are in the same game region. ```c -ecs_entity_t Region = ecs_new_id(world); -ecs_entity_t Unit = ecs_new_id(world); +ecs_entity_t Region = ecs_new(world); +ecs_entity_t Unit = ecs_new(world); -ecs_entity_t Region_01 = ecs_new_id(world); -ecs_entity_t Region_02 = ecs_new_id(world); +ecs_entity_t Region_01 = ecs_new(world); +ecs_entity_t Region_02 = ecs_new(world); // Example of entities created in different regions ecs_entity_t unit_01 = ecs_new_w_id(world, Unit); @@ -2695,9 +2691,9 @@ ecs_add_pair(world, unit_02, Region, Region_02); // Create query that groups entities that are in the same region ecs_query(world, { - .filter.terms = {{ Unit }}, - .group_by = group_by_target, // function that groups by relationship target - .group_by_id = Region // optional, passed to group_by function + .terms = {{ Unit }}, + .group_by_callback = group_by_target, // function that groups by relationship target + .group_by = Region // optional, passed to group_by function }); ``` @@ -2773,7 +2769,7 @@ The following sections show how to use component inheritance in the different la The following example shows a rule that uses component inheritance to match entities: ```c -ecs_entity_t Unit = ecs_new_id(world); +ecs_entity_t Unit = ecs_new(world); ecs_entity_t MeleeUnit = ecs_new_w_pair(world, EcsIsA, Unit); ecs_entity_t RangedUnit = ecs_new_w_pair(world, EcsIsA, Unit); @@ -2781,7 +2777,7 @@ ecs_entity_t unit_01 = ecs_new_w_id(world, MeleeUnit); ecs_entity_t unit_02 = ecs_new_w_id(world, RangedUnit); // Matches entities with Unit, MeleeUnit and RangedUnit -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = {{ Unit }} }); @@ -2792,7 +2788,7 @@ ecs_rule_t *r = ecs_rule(world, { The following example shows a rule that uses component inheritance to match entities: ```cpp -flecs::entity Unit = ecs_new_id(world); +flecs::entity Unit = ecs_new(world); flecs::entity MeleeUnit = world.entity().is_a(Unit); flecs::entity RangedUnit = world.entity().is_a(Unit); @@ -2800,7 +2796,7 @@ flecs::entity unit_01 = world.entity().add(MeleeUnit); flecs::entity unit_02 = world.entity().add(RangedUnit); // Matches entities with Unit, MeleeUnit and RangedUnit -flecs::rule r = world.rule(); +flecs::query r = world.query(); // Iterate as usual ``` @@ -2827,13 +2823,13 @@ The following example shows a rule that uses transitivity to match entities that // Create LocatedIn relationship with transitive property ecs_entity_t LocatedIn = ecs_new_w_id(world, EcsTransitive); -ecs_entity_t NewYork = ecs_new_id(world); +ecs_entity_t NewYork = ecs_new(world); ecs_entity_t Manhattan = ecs_new_w_pair(world, LocatedIn, NewYork); ecs_entity_t CentralPark = ecs_new_w_pair(world, LocatedIn, Manhattan); ecs_entity_t Bob = ecs_new_w_pair(world, LocatedIn, CentralPark); // Matches ManHattan, CentralPark, Bob -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = {{ ecs_pair(LocatedIn, NewYork) }} }); @@ -2847,7 +2843,7 @@ Queries for transitive relationships can be compared with variables. This query // - ManHattan (Place = NewYork) // - CentralPark (Place = ManHattan, NewYork) // - Bob (Place = CentralPark, ManHattan, NewYork) -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = {{ .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }} }); ``` @@ -2856,14 +2852,14 @@ Variables can be used to constrain the results of a transitive query. The follow ```c // Add City property to NewYork -ecs_entity_t City = ecs_new_id(world); +ecs_entity_t City = ecs_new(world); ecs_add_id(world, NewYork, City); // Matches: // - ManHattan (Place = NewYork) // - CentralPark (Place = NewYork) // - Bob (Place = NewYork) -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = { { .first.id = LocatedIn, .second.name = "Place", .second.flags = EcsIsVariable }, { .id = City, .src.name = "Place", .src.flags = EcsIsVariable } @@ -2886,7 +2882,7 @@ flecs::entity CentralPark = world.entity().add(Manhattan); flecs::entity Bob = world.entity().add(CentralPark); // Matches ManHattan, CentralPark, Bob -flecs::rule<> r = world.rule_builder() +flecs::query<> r = world.query_builder() .term(NewYork) .build(); @@ -2900,7 +2896,7 @@ Queries for transitive relationships can be compared with variables. This query // - ManHattan (Place = NewYork) // - CentralPark (Place = ManHattan, NewYork) // - Bob (Place = CentralPark, ManHattan, NewYork) -flecs::rule<> r = world.rule_builder() +flecs::query<> r = world.query_builder() .term().second("$Place") .build(); ``` @@ -2917,7 +2913,7 @@ NewYork.add(City); // - ManHattan (Place = NewYork) // - CentralPark (Place = NewYork) // - Bob (Place = NewYork) -flecs::rule<> r = world.rule_builder() +flecs::query<> r = world.query_builder() .term().second("$Place") .term().src("$Place") .build(); @@ -2959,11 +2955,11 @@ The following sections show how to use transitive relationships in the different The following example shows a rule that uses the `IsA` reflexive relationship: ```c -ecs_entity_t Tree = ecs_new_id(world); +ecs_entity_t Tree = ecs_new(world); ecs_entity_t Oak = ecs_new_w_pair(world, EcsIsA, Tree); // Matches Tree, Oak -ecs_rule_t *r = ecs_rule(world, { +ecs_query_impl_t *r = ecs_query(world, { .terms = {{ ecs_pair(EcsIsA, Tree )}} }); ``` @@ -2976,7 +2972,7 @@ flecs::entity Tree = world.entity(); flecs::entity Oak = world.entity().is_a(Tree); // Matches Tree, Oak -flecs::rule<> r = world.rule_builder() +flecs::query<> r = world.query_builder() .term(flecs::IsA, Tree) .build(); ``` @@ -2991,10 +2987,10 @@ Reflexivity in a query is enabled by adding the `Reflexive` property to a relati ## Performance This section describes performance characteristics for each query type. -### Filter -A filter is a data type that describes the structure of a query in Flecs. Filters are the cheapest to create, as creating one just requires initializing the value of the filter object. +### Query +A filter is a data type that describes the structure of a query in Flecs. Querys are the cheapest to create, as creating one just requires initializing the value of the filter object. -Filters can serve different purposes, like iterating all matching entities, or testing if a specific entity matches with a filter. Filters can also be used to filter the output of other queries, including other filters. +Querys can serve different purposes, like iterating all matching entities, or testing if a specific entity matches with a filter. Querys can also be used to filter the output of other queries, including other filters. While the exact way a filter is evaluated depends on what kind of filter it is, almost all filter evaluation follows steps similar to the ones in this diagram: @@ -3017,24 +3013,24 @@ When a table reaches the `yield` node it means that it matched all parts of the Because filters are fast to create, have low overhead, and are reasonably efficient to iterate, they are the goto solution for when an application cannot know in advance what it needs to query for, like finding all children for a specific entity: ```c -ecs_filter_f fs = ECS_FILTER_INIT; -ecs_filter_t *f = ecs_filter(world, { +ecs_query_f fs = ECS_FILTER_INIT; +ecs_query_t *f = ecs_query(world, { .storage = &fs, // optional, but prevents allocation .terms = {{ ecs_childof(e) }} }); -ecs_iter_t child_it = ecs_filter_iter(f); -while (ecs_filter_next(&child_it)) { +ecs_iter_t child_it = ecs_query_iter(f); +while (ecs_query_next(&child_it)) { for (int c = 0; c < child_it.count; c ++) { printf("child %s\n", ecs_get_name(world, child_it.entities[c])); } } -ecs_filter_fini(f); +ecs_query_fini(f); ``` ```cpp -auto f = world.filter_builder() +auto f = world.query_builder() .term(flecs::ChildOf, e) .build(); @@ -3063,22 +3059,22 @@ The following example shows how cached queries are used: ```c // Creates query, populates the cache with existing tables ecs_query_t *q = ecs_query(world, { - .filter.terms = { + .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); // When a new table is created that matches the query, it is // added to the cache -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); // Iterate the tables in the cache 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); + Position *p = ecs_field(&it, Position, 0); + Velocity *v = ecs_field(&it, Velocity, 1); for (int i = 0; i < it.count; i ++) { p[i].x += v[i].x; diff --git a/docs/Quickstart.md b/docs/Quickstart.md index 065996d44..be2bf0878 100644 --- a/docs/Quickstart.md +++ b/docs/Quickstart.md @@ -114,17 +114,13 @@ Addon | Description | Define [Pipeline](/flecs/group__c__addons__pipeline.html) | Automatically schedule & multithread systems | FLECS_PIPELINE | [Timer](/flecs/group__c__addons__timer.html) | Run systems at time intervals or at a rate | FLECS_TIMER | [Meta](/flecs/group__c__addons__meta.html) | Flecs reflection system | FLECS_META | -[Meta_C](/flecs/group__c__addons__meta__c.html) | (C) Utilities for auto-inserting reflection data | FLECS_META_C | [Units](/flecs/group__c__addons__units.html) | Builtin unit types | FLECS_UNITS | -[Expr](/flecs/group__c__addons__expr.html) | String format optimized for ECS data | FLECS_EXPR | [JSON](/flecs/group__c__addons__json.html) | JSON format | FLECS_JSON | [Doc](/flecs/group__c__addons__doc.html) | Add documentation to components, systems & more | FLECS_DOC | [Http](/flecs/group__c__addons__http.html) | Tiny HTTP server for processing simple requests | FLECS_HTTP | [Rest](/flecs/group__c__addons__rest.html) | REST API for showing entities in the browser | FLECS_REST | -[Parser](/flecs/group__c__addons__parser.html) | Create entities & queries from strings | FLECS_PARSER | -[Plecs](/flecs/group__c__addons__plecs.html) | Small utility language for asset/scene loading | FLECS_PLECS | +[Script](/flecs/group__c__addons__script.html) | DSL for assets, scenes and configuration | FLECS_SCRIPT | [Rules](/flecs/group__c__addons__rules.html) | Powerful prolog-like query language | FLECS_RULES | -[Snapshot](/flecs/group__c__addons__snapshot.html) | Take snapshots of the world & restore them | FLECS_SNAPSHOT | [Stats](/flecs/group__c__addons__stats.html) | Functions for collecting statistics | FLECS_STATS | [Monitor](/flecs/group__c__addons__monitor.html) | Periodically collect & store flecs statistics | FLECS_MONITOR | [Metrics](/flecs/group__c__addons__metrics.html) | Create metrics from user-defined components | FLECS_METRICS | @@ -179,7 +175,7 @@ An entity is a unique thing in the world, and is represented by a 64 bit id. Ent
  • C ```c -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_is_alive(world, e); // true! ecs_delete(world, e); @@ -279,7 +275,7 @@ A component is a type of which instances can be added and removed to entities. E ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); // Add a component. This creates the component in the ECS storage, but does not // assign it with a value. @@ -428,10 +424,10 @@ A tag is a component that does not have any data. In Flecs tags can be either em ```c // Create Enemy tag -ecs_entity_t Enemy = ecs_new_id(world); +ecs_entity_t Enemy = ecs_new(world); // Create entity, add Enemy tag -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add_id(world, e, Enemy); ecs_has_id(world, e, Enemy); // true! @@ -505,11 +501,11 @@ A pair is a combination of two entity ids. Pairs can be used to store entity rel ```c // Create Likes relationship -ecs_entity_t Likes = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); // Create a small graph with two entities that like each other -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Alice = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Alice = ecs_new(world); ecs_add_pair(world, Bob, Likes, Alice); // Bob likes Alice ecs_add_pair(world, Alice, Likes, Bob); // Alice likes Bob @@ -697,7 +693,7 @@ Flecs has builtin support for hierarchies with the builtin `EcsChildOf` (or `fle
  • C ```c -ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t parent = ecs_new(world); // ecs_new_w_pair is the same as ecs_new_id + ecs_add_pair ecs_entity_t child = ecs_new_w_pair(world, EcsChildOf, parent); @@ -784,8 +780,8 @@ Queries (see below) can use hierarchies to order data breadth-first, which can c
  • C ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { +ecs_query_t *q = ecs_query(world, { + .terms = { { ecs_id(Position) }, { ecs_id(Position), .src = { .flags = EcsCascade, // Breadth-first order @@ -796,8 +792,8 @@ ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - Position *p = ecs_field(&it, Position, 1); - Position *p_parent = ecs_field(&it, Position, 2); + Position *p = ecs_field(&it, Position, 0); + Position *p_parent = ecs_field(&it, Position, 1); for (int i = 0; i < it.count; i++) { // Do the thing } @@ -808,7 +804,7 @@ while (ecs_query_next(&it)) { ```cpp auto q = world.query_builder() - .term_at(2).parent().cascade() + .term_at(1).parent().cascade() .build(); q.each([](Position& p, Position& p_parent) { @@ -840,7 +836,7 @@ Flecs has builtin support for instancing (sharing a single component with multip ```c // Shortcut to create entity & set a component -ecs_entity_t base = ecs_set(world, 0, Triangle, {{0, 0}, {1, 1}, {-1, -1}}); +ecs_entity_t base = ecs_insert(world, ecs_value(Triangle, {{0, 0}, {1, 1}, {-1, -1}})); // Create entity that shares components with base ecs_entity_t e = ecs_new_w_pair(world, EcsIsA, base); @@ -909,7 +905,7 @@ The type (often referred to as "archetype") is the list of ids an entity has. Ty ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Velocity); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); ecs_add(world, e, Position); ecs_add(world, e, Velocity); @@ -1060,7 +1056,7 @@ The following examples show how to query for a singleton component: ```c // Create query that matches Gravity as singleton ecs_query_t *q = ecs_query(ecs, { - .filter.terms = { + .terms = { // Regular component { .id = ecs_id(Velocity) }, // A singleton is a component matched on itself @@ -1076,7 +1072,7 @@ ECS_SYSTEM(world, ApplyGravity, EcsOnUpdate, Velocity, Gravity($)); ```cpp world.query_builder() - .term_at(2).singleton() + .term_at(1).singleton() .build(); ```
  • @@ -1091,15 +1087,20 @@ world.QueryBuilder() +<<<<<<< HEAD ### Filter -Filters are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter: +Queries are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter:
    • C +======= +### Query +Querys are a kind of uncached query that are cheap to create. This makes them a good fit for scenarios where an application doesn't know in advance what it has to query for, like when finding the children for a parent. The following example shows a simple filter: +>>>>>>> 45a6e85b8 (v4) ```c // Initialize a filter with 2 terms on the stack -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *f = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { ecs_id(Position) }, { ecs_pair(EcsChildOf, parent) } @@ -1109,10 +1110,10 @@ ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ // Iterate the filter results. Because entities are grouped by their type there // are two loops: an outer loop for the type, and an inner loop for the entities // for that type. -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { // Each type has its own set of component arrays - Position *p = ecs_field(&it, Position, 1); + Position *p = ecs_field(&it, Position, 0); // Iterate all entities for the type for (int i = 0; i < it.count; i++) { @@ -1121,7 +1122,7 @@ while (ecs_filter_next(&it)) { } } -ecs_filter_fini(f); +ecs_query_fini(f); ```
    • C++ @@ -1134,7 +1135,7 @@ world.each([](Position& p, Velocity& v) { // flecs::entity argument is optional }); // More complex filters can first be created, then iterated -auto f = world.filter_builder() +auto f = world.query_builder() .term(flecs::ChildOf, parent) .build(); @@ -1184,7 +1185,7 @@ f.Iter((Iter it, Field p) =>
    -Filters can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs. +Querys can use operators to exclude components, optionally match components or match one out of a list of components. Additionally filters may contain wildcards for terms which is especially useful when combined with pairs. The following example shows a filter that matches all entities with a parent that do not have `Position`:
    @@ -1192,7 +1193,7 @@ The following example shows a filter that matches all entities with a parent tha
  • C ```c -ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){ +ecs_query_t *f = ecs_query_init(world, &(ecs_query_desc_t){ .terms = { { ecs_pair(EcsChildOf, EcsWildcard) } { ecs_id(Position), .oper = EcsNot }, @@ -1205,7 +1206,7 @@ ecs_filter_t *f = ecs_filter_init(world, &(ecs_filter_desc_t){
  • C++ ```cpp -auto f = world.filter_builder<>() +auto f = world.query_builder<>() .term(flecs::ChildOf, flecs::Wildcard) .term().oper(flecs::Not) .build(); @@ -1237,8 +1238,8 @@ The API for queries is similar to filters: ```c // Create a query with 2 terms -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { +ecs_query_t *q = ecs_query(world, { + .terms = { { ecs_id(Position) }, { ecs_pair(EcsChildOf, EcsWildcard) } } @@ -1292,7 +1293,7 @@ ecs_run(world, Move, delta_time, NULL); // Run system // Option 2, use the ecs_system_init function/ecs_system macro ecs_entity_t move_sys = ecs_system(world, { - .query.filter.terms = { + .query.terms = { {ecs_id(Position)}, {ecs_id(Velocity)}, }, @@ -1303,8 +1304,8 @@ ecs_run(world, move_sys, delta_time, NULL); // Run system // The callback code (same for both options) void Move(ecs_iter_t *it) { - Position *p = ecs_field(it, Position, 1); - Velocity *v = ecs_field(it, Velocity, 2); + Position *p = ecs_field(it, Position, 0); + Velocity *v = ecs_field(it, Velocity, 1); for (int i = 0; i < it->count; i++) { p[i].x += v[i].x * it->delta_time; @@ -1507,14 +1508,14 @@ An example of an observer with two components: ```c ecs_observer(world, { - .filter.terms = { { ecs_id(Position) }, { ecs_id(Velocity) }}, + .terms = { { ecs_id(Position) }, { ecs_id(Velocity) }}, .event = EcsOnSet, .callback = OnSetPosition }); // Callback code is same as system -ecs_entity_t e = ecs_new_id(world); // Doesn't invoke the observer +ecs_entity_t e = ecs_new(world); // Doesn't invoke the observer ecs_set(world, e, Position, {10, 20}); // Doesn't invoke the observer ecs_set(world, e, Velocity, {1, 2}); // Invokes the observer ecs_set(world, e, Position, {20, 40}); // Invokes the observer diff --git a/docs/Relationships.md b/docs/Relationships.md index b535b50c5..47676754f 100644 --- a/docs/Relationships.md +++ b/docs/Relationships.md @@ -32,9 +32,9 @@ The following code is a simple example that uses relationships:
  • C ```c -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Alice = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Alice = ecs_new(world); // Bob Likes Alice ecs_add_pair(world, Bob, Likes, Alice); @@ -86,10 +86,10 @@ The same relationship can be added multiple times to an entity, as long as its t
  • C ```c -ecs_entity_t Bob = ecs_new_id(world); -ecs_entity_t Eats = ecs_new_id(world); -ecs_entity_t Apples = ecs_new_id(world); -ecs_entity_t Pears = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); +ecs_entity_t Eats = ecs_new(world); +ecs_entity_t Apples = ecs_new(world); +ecs_entity_t Pears = ecs_new(world); ecs_add_pair(world, Bob, Eats, Apples); ecs_add_pair(world, Bob, Eats, Pears); @@ -142,14 +142,14 @@ An application can query for relationships with the `(Relationship, Target)` not ```c // Find all entities that eat apples -ecs_query_t *q = ecs_query_new(world, "(Eats, Apples)"); +ecs_query_t *q = ecs_query(world, { .expr = "(Eats, Apples)" }); // Find all entities that eat anything -ecs_query_t *q = ecs_query_new(world, "(Eats, *)"); +ecs_query_t *q = ecs_query(world, { .expr = "(Eats, *)" }); -// Or with the ecs_query_init function: -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = {{ecs_pair(Eats, Apples)}} +// Or with the ecs_query_cache_init function: +ecs_query_t *q = ecs_query(world, { + .terms = {{ecs_pair(Eats, Apples)}} }); ``` @@ -437,25 +437,25 @@ Bob.Each((Id id) =>
  • C ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms[0] = ecs_pair(Eats, Apples) }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { for (int i = 0; i < it.count; i ++) { // Iterate as usual } } -ecs_filter_fini(f); +ecs_query_fini(f); ```
  • C++ ```cpp -world.filter_builder() +world.query_builder() .term(Eats, Apples) .build() .each([](flecs::entity e) { @@ -487,30 +487,30 @@ world.FilterBuilder()
  • C ```c -ecs_filter_t *f = ecs_filter(world, { +ecs_query_t *f = ecs_query(world, { .terms[0] = ecs_pair(Eats, EcsWildcard) }); -ecs_iter_t it = ecs_filter_iter(world, f); -while (ecs_filter_next(&it)) { - ecs_id_t pair_id = ecs_field_id(&it, 1); +ecs_iter_t it = ecs_query_iter(world, f); +while (ecs_query_next(&it)) { + ecs_id_t pair_id = ecs_field_id(&it, 0); ecs_entity_t food = ecs_pair_second(world, pair_id); // Apples, ... for (int i = 0; i < it.count; i ++) { // Iterate as usual } } -ecs_filter_fini(f); +ecs_query_fini(f); ```
  • C++ ```cpp -world.filter_builder() +world.query_builder() .term(Eats, flecs::Wildcard) .build() .each([](flecs::iter& it, size_t i) { - flecs::entity food = it.pair(1).second(); // Apples, ... + flecs::entity food = it.pair(0).second(); // Apples, ... flecs::entity e = it.entity(i); // Iterate as usual }); @@ -604,12 +604,12 @@ ECS_COMPONENT(world, Position); ECS_COMPONENT(world, Eats); // Tags -ecs_entity_t Likes = ecs_new_id(world); -ecs_entity_t Begin = ecs_new_id(world); -ecs_entity_t End = ecs_new_id(world); -ecs_entity_t Apples = ecs_new_id(world); +ecs_entity_t Likes = ecs_new(world); +ecs_entity_t Begin = ecs_new(world); +ecs_entity_t End = ecs_new(world); +ecs_entity_t Apples = ecs_new(world); -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); // Both Likes and Apples are tags, so (Likes, Apples) is a tag ecs_add_pair(world, e, Likes, Apples); @@ -712,11 +712,11 @@ typedef struct { float y; } Position; -ecs_entity_t e = ecs_new_id(world); +ecs_entity_t e = ecs_new(world); -ecs_entity_t first = ecs_new_id(world); -ecs_entity_t second = ecs_new_id(world); -ecs_entity_t third = ecs_new_id(world); +ecs_entity_t first = ecs_new(world); +ecs_entity_t second = ecs_new(world); +ecs_entity_t third = ecs_new(world); // Add component position 3 times, for 3 different objects ecs_add_pair(world, e, Position, first, {1, 2}); @@ -775,8 +775,8 @@ When querying for relationship pairs, it is often useful to be able to find all
  • C ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.terms = { +ecs_query_t *q = ecs_query(world, { + .terms = { {ecs_pair(Likes, EcsWildcard)} } }); @@ -784,7 +784,7 @@ ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { - ecs_id_t id = ecs_field_id(&it, 1); // Obtain pair id + ecs_id_t id = ecs_field_id(&it, 0); // Obtain pair id // Get relationship & target ecs_entity_t rel = ecs_pair_first(world, id); @@ -808,7 +808,7 @@ auto q = world.query_builder() .build(); q.iter([](flecs::iter& it) { - auto id = it.pair(1); + auto id = it.pair(0); for (auto i : it) { cout << "entity " << it.entity(i) << " has relationship " @@ -846,8 +846,8 @@ Wildcards may appear in query expressions, using the `*` character:
  • C ```c -ecs_query_t *q = ecs_query_init(world, &(ecs_query_desc_t){ - .filter.expr = "(Likes, *)" +ecs_query_t *q = ecs_query(world, { + .query.expr = "(Likes, *)" }); ``` @@ -886,11 +886,11 @@ An application can use pair wildcard expressions to find all instances of a rela ```c // Bob eats apples and pears -ecs_entity_t Eats = ecs_new_entity(world, "Eats"); -ecs_entity_t Apples = ecs_new_entity(world, "Apples"); -ecs_entity_t Pears = ecs_new_entity(world, "Pears"); +ecs_entity_t Eats = ecs_entity(world, { .name = "Eats" }); +ecs_entity_t Apples = ecs_entity(world, { .name = "Apples" }); +ecs_entity_t Pears = ecs_entity(world, { .name = "Pears" }); -ecs_entity_t Bob = ecs_new_id(world); +ecs_entity_t Bob = ecs_new(world); ecs_add_pair(world, Bob, Eats, Apples); ecs_add_pair(world, Bob, Eats, Pears); @@ -966,8 +966,8 @@ The `IsA` relationship is a builtin relationship that allows applications to exp
  • C ```c -ecs_entity_t Apple = ecs_new_id(world); -ecs_entity_t Fruit = ecs_new_id(world); +ecs_entity_t Apple = ecs_new(world); +ecs_entity_t Fruit = ecs_new(world); ecs_add_pair(world, Apple, EcsIsA, Fruit); ``` @@ -1023,7 +1023,7 @@ We can also add `IsA` relationships to `Apple`:
  • C ```c -ecs_entity_t GrannySmith = ecs_new_id(world); +ecs_entity_t GrannySmith = ecs_new(world); ecs_add_pair(world, GrannySmith, EcsIsA, Apple); ``` @@ -1057,11 +1057,11 @@ An entity with an `IsA` relationship to another entity is equivalent to the othe
  • C ```c -ecs_entity_t Spaceship = ecs_new_id(world); +ecs_entity_t Spaceship = ecs_new(world); ecs_set(world, Spaceship, MaxSpeed, {100}); ecs_set(world, SpaceShip, Defense, {50}); -ecs_entity_t Frigate = ecs_new_id(world); +ecs_entity_t Frigate = ecs_new(world); ecs_add(world, Frigate, EcsIsA, Spaceship); ecs_set(world, Frigate, Defense, {100}); ``` @@ -1170,7 +1170,7 @@ The ability to share components is also applied transitively, so `Frigate` could
  • C ```c -ecs_entity_t FastFrigate = ecs_new_id(world); +ecs_entity_t FastFrigate = ecs_new(world); ecs_add(world, FastFrigate, EcsIsA, Frigate); ecs_set(world, FastFrigate, MaxSpeed, {200}); @@ -1231,8 +1231,8 @@ The `ChildOf` relationship is the builtin relationship that allows for the creat
  • C ```c -ecs_entity_t Spaceship = ecs_new_id(world); -ecs_entity_t Cockpit = ecs_new_id(world); +ecs_entity_t Spaceship = ecs_new(world); +ecs_entity_t Cockpit = ecs_new(world); ecs_add_pair(world, Cockpit, EcsChildOf, Spaceship); ``` @@ -1346,13 +1346,13 @@ In some scenarios a number of entities all need to be created with the same pare
  • C ```c -ecs_entity_t parent = ecs_new_id(world); +ecs_entity_t parent = ecs_new(world); ecs_entity_t prev = ecs_set_scope(world, parent); // Note that we're not using the ecs_new_id function for the children. This // function only generates a new id, and does not add the scope to the entity. -ecs_entity_t child_a = ecs_new(world, 0); -ecs_entity_t child_b = ecs_new(world, 0); +ecs_entity_t child_a = ecs_new(world); +ecs_entity_t child_b = ecs_new(world); // Restore the previous scope ecs_set_scope(world, prev); @@ -1663,7 +1663,7 @@ world.Component().Entity.Destruct(); ECS_TAG(world, ChildOf); ecs_add_pair(world, ChildOf, EcsOnDeleteTarget, EcsDelete); -ecs_entity_t p = ecs_new_id(world); +ecs_entity_t p = ecs_new(world); ecs_entity_t e = ecs_new_w_pair(world, ChildOf, p); // This will delete both p and e @@ -1760,10 +1760,10 @@ To understand why some ways to organize entities work better than others, having 1. **Find all root entities** World teardown starts by finding all root entities, which are entities that do not have the builtin `ChildOf` relationship. Note that empty entities (entities without any components) are not found during this step. -2. **Filter out modules, components, observers and systems** +2. **Query out modules, components, observers and systems** This ensures that components are not cleaned up before the entities that use them, and triggers, observers and systems are not cleaned up while there are still conditions under which they could be invoked. -3. **Filter out entities that have no children** +3. **Query out entities that have no children** If entities have no children they cannot cause complex cleanup logic. This also decreases the likelihood of initiating cleanup actions that could impact other entities. 4. **Delete root entities** @@ -1947,9 +1947,8 @@ Entity e = ecs.Entity()
  • - -### Tag property -A relationship can be marked as a tag in which case it will never contain data. By default the data associated with a pair is determined by whether either the relationship or target are components. For some relationships however, even if the target is a component, no data should be added to the relationship. Consider the following example: +### PairIsTag property +A relationship can be marked with PairIsTag in which case a pair with the relationship will never contain data. By default the data associated with a pair is determined by whether either the relationship or target are components. For some relationships however, even if the target is a component, no data should be added to the relationship. Consider the following example: