Flecs v4.0
A fast entity component system (ECS) for C & C++
|
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:
$var + 10
)if $var > 10
)To learn Flecs Script, check out the Tutorial!
This section goes over the basic syntax over Flecs Script.
An entity is created by specifying an identifier followed by a scope. Example:
An entity scope can contain components and child entities. The following example shows how to add a child entity:
Note how a scope is also added to the child entity.
To create anonymous entities, use the _
identifier:
A tag can be added to an entity by simply specifying the tag's identifier in an entity scope. Example:
Pairs are added to entities by adding them to an entity scope, just like tags:
Components are specified like tags, but with an additional value:
For a component to be assignable 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:
Components can be defined in a script:
Components can be pairs:
When referring to child entities or components, identifiers need to include the parent path as well as the entity name. Paths are provided as lists of identifiers separated by a dot (.
):
To avoid having to repeatedly type the same paths, use the using
statement (see below).
To create a singleton component, use $
as the entity identifier:
Multiple singleton components can be specified in the same scope:
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:
This is equivalent to doing:
When using the entity kind syntax, the scope is optional:
If the specified kind is a component, a value can be specified between parentheses:
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:
Applications can specify the following builtin kinds which provide convenience shortcuts to commonly used features:
Scripts can natively specify inheritance relationships between entities, which is useful in particular for prefabs. Example:
When specifying inheritance, the scope is optional:
This is equivalent to doing:
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:
The module
statement puts all contents of a script in a module. Example:
The components.transform
entity will be created with the Module
tag.
The using
keyword imports a namespace into the current namespace. Example:
The using
keyword only applies to the scope in which it is specified. Example:
A using
statement may end with a wildcard (*
). This will import all namespaces matching the path. Example:
When you're building 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:
This is equivalent to doing:
With statements can contain multiple tags:
With statements can contain component values, specified between parentheses:
Scripts can contain variables, which are useful for often repeated values. Variables are created with the const
keyword. Example:
Variables can be combined with expressions:
In the above examples, the type of the variable is inferred. Variables can also be provided with an explicit type:
Variables can be used in component values as shown in the previous examples, or can be used directly as component. Example:
Additionally, variables can also be used in combination with with
statements:
A script can use the value of a component that is looked up on a specific entity. The following example fetches the width
and depth
members from the Level
component, that is fetched from the Game
entity:
To reduce the number of component lookups in a script, the component value can be stored in a variable:
The requested component is stored by value, not by reference. Adding or removing components to the entity will not invalidate the component data. If the requested component does not exist on the entity, script execution will fail.
Parts of a script can be conditionally executed with an if statement. Example:
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:
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:
A common use of default components is when creating structs. struct
is a component with member
as default child component. Example:
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:
Multiple statements can be combined on a single line when using the semicolon operator. Example:
The comma operator can be used as a shortcut to create multiple entities in a scope. Example:
This allows for a more natural way to describe things like enum types:
Templates are parametrized scripts that can be used to create procedural assets. Templates can be created with the template
keyword. Example:
The script contents of an template are not ran immediately. Instead they are ran whenever an template is instantiated. To instantiate an template, add it as a regular component to an entity:
Templates are commonly used in combination with the kind syntax:
Templates can be parametrized with properties. Properties are variables that are exposed as component members. To create a property, use the prop
keyword. Example:
Template scripts can do anything a regular script can do, including creating child entities. The following example shows how to create an template that uses a nested template to create children:
This section goes over how to run scripts in an application.
To run a script once, use the ecs_script_run
function. Example:
Alternatively a script can be ran directly from a file:
If a script fails, the entities created by the script will not be automatically deleted. When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted.
A script can be ran multiple times by using the ecs_script_parse
and ecs_script_eval
functions. Example:
If a script fails, the entities created by the script will not be automatically deleted. When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted.
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:
To update the code of a managed script, use the ecs_script_update
function:
When a script contains templates, script resources will not get cleaned up until the entities associated with the templates are deleted.