68 lines
3.9 KiB
Markdown
68 lines
3.9 KiB
Markdown
# Your first behavior
|
|
|
|
This example moves a sprite along the X axis at two metres per second. It has no rigid body: the behavior owns its simulation pose.
|
|
|
|
## Build and run the complete example
|
|
|
|
First complete [the build setup](../getting-started/build.md), including the renderer dependencies. From the repository root on Linux:
|
|
|
|
```bash
|
|
cmake -S . -B build/tutorial-moving -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Debug \
|
|
-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
|
|
-DFASET_GAMEPLAY_SOURCE_DIR="$PWD/examples/tutorials/moving"
|
|
cmake --build build/tutorial-moving --target faset_player faset_schema_exporter
|
|
build/tutorial-moving/faset_schema_exporter --output build/tutorial-moving/schema.json
|
|
build/tutorial-moving/faset_player --scene examples/tutorials/moving/scene.json
|
|
```
|
|
|
|
On Windows, use a Developer shell with `clang-cl`, the Windows SDK and the documented dependencies. Use `clang-cl` for both compiler options and an absolute path for `FASET_GAMEPLAY_SOURCE_DIR`; run `faset_player.exe` from the selected build directory.
|
|
|
|
The same directory contract is used by a project's `Scripts` folder. These commands select a complete gameplay module; they do not add its behavior to an unrelated module automatically.
|
|
|
|
## The module interface
|
|
|
|
```cpp
|
|
--8<-- "examples/tutorials/moving/Gameplay.hpp"
|
|
```
|
|
|
|
The header declares the two entry functions. `Runtime` is the game world API; its implementation and EnTT storage remain inside the engine.
|
|
|
|
## The implementation
|
|
|
|
```cpp
|
|
--8<-- "examples/tutorials/moving/Gameplay.cpp"
|
|
```
|
|
|
|
Read the callback from top to bottom:
|
|
|
|
1. `game` is the current runtime, and `self` is the object carrying `tutorial.move_x`.
|
|
2. `delta` is this frame's elapsed time in **seconds**.
|
|
3. `settings.value("speed", 2.0f)` reads configuration and supplies a fallback if the field is absent.
|
|
4. `transform` gives a pose copy. Multiplying metres per second by seconds gives a displacement in metres.
|
|
5. `setTransform` publishes the changed non-physical pose.
|
|
|
|
The `[](...) { ... }` expression is a C++ lambda: a function stored in `Behavior::update`. Empty brackets mean it captures no local variables. `registerBehavior` takes ownership of the callback object. Register before calling `load`; registration after a world has loaded is rejected.
|
|
|
|
The `schema()` function describes editable configuration. It does not create a runtime object. `tutorial.move_x` is the stable `TypeId`; `speed` is a stable `FieldId` within that type. Keep these IDs when changing a display label. Changing a field's meaning or units needs an explicit data migration, not just a new label.
|
|
|
|
## Attach the behavior
|
|
|
|
The example scene is a complete, loadable document:
|
|
|
|
```json
|
|
--8<-- "examples/tutorials/moving/scene.json"
|
|
```
|
|
|
|
The sprite is visible because it has `faset.sprite`. It moves because it also has `tutorial.move_x`. The configuration field is `speed`; the type string must match the registration exactly. `rotation` uses radians, and the default coordinate system is Y-up.
|
|
|
|
After your project's schema is exported and loaded by the Editor, the type can be described through the same authoring schema used by the Inspector. The direct Player command above is useful before building an Editor workflow around your component.
|
|
|
|
## Make a change and verify it
|
|
|
|
Change the scene's `speed` to `-2`: the object moves left. Change the C++ callback or schema: stop the Player, rebuild, regenerate the schema, then launch a new session. There is no automatic C++ hot reload.
|
|
|
|
The `tutorial_moving` CTest checks that both 30 Hz and 60 Hz frame sequences move the object two metres in one second. It checks the resulting pose, rather than only checking that the program starts.
|
|
|
|
Common mistakes are forgetting `setTransform` after editing the copy, writing the wrong component type string, and attaching a rigid body while still using `setTransform`. The last case produces a runtime diagnostic: physics owns that body's pose. Continue with [physics movement](physics.md) for the correct API.
|