bevy-tnua 0.29.0

A floating character controller for Bevy
Documentation
# Migrating to Tnua 0.28

* Instead of adding the configuration with `TnuaController::new`, add it as a
  new component - `TnuaConfig`:
  ```rust
  // Old way
  cmd.insert(
      TnuaController::<ControlScheme>::new(asset_server.load("configuration.ron")),
  );

  // New way
  cmd.insert((
      TnuaController::<ControlScheme>::default(),
      TnuaConfig::<ControlScheme>(asset_server.load("configuration.ron")),
  ));
  ```
* The `sensors_entities` field of the controller was moved to a new component -
  `TnuaSensorsEntities`. This component is added automatically via Bevy's
  required components mechanism, so you don't need to add it yourself, but if
  you access it in your system you need to request it separately in the query.

# Migrating to Tnua 0.27 ("Schemes")

## Static definition of supported basis and actions

The biggest change is that instead of dynamically feeding basis and actions to
the controller, you must now define a control scheme which statically defines
the basis and actions your controller can use. This is done like so:

```rust
#[derive(TnuaScheme)]
#[scheme(basis = TnuaBuiltinWalk)]
enum ControlScheme {
    Jump(TnuaBuiltinJump),
    Crouch(TnuaBuiltinCrouch),
    DifferentKindOfJump(TnuaBuiltinJump),
}
```

Both `TnuaController` and `TnuaControllerPlugin` are parameterized by the
control scheme. `TnuaController` no longer implement `Default` - instead it has
a `new` method that accepts an handle to a config asset handle. The
configuration struct is defined automatically by the `TnuaScheme` macro, and
you can either load it from a file (it implements `Serialize` and
`Deserialize`, but it's up to you to handle the loading itself - e.g. with
another plugin) or inject it directly to the `Assets` resource. The generated
configuration struct's name is the name of the control scheme enum plus the
`Config` suffix.

```rust
cmd.insert(TnuaController::<ControlScheme>::new(
    control_scheme_configs.add(ControlSchemeConfig {
        basis: TnuaBuiltinWalkConfig {
            ..Default::default()
        },
        jump: TnuaBuiltinJumpConfig {
            ..Default::default()
        },
        crouch: TnuaBuiltinCrouchConfig {
            ..Default::default()
        },
        different_kind_of_jump: TnuaBuiltinJumpConfig {
            ..Default::default()
        },
    })
));
```

Feeding the basis is now done by setting a `pub` field of the controller named
`basis`. Feeding the action is still done with the `action` method, but instead
of the action type itself it receives an instance of the control scheme type
(the variant decieds the action)

## The schedules of action feeding

Tnua no longer demands for user input to be done in the same schedule as the
Tnua systems (though `TnuaControllerPlugin` itself still needs to run in a
schedule of the same frequency as the physics backend). **The downside** of
this is that Tnua now expects to be informed about this by invoking, in each
frame before feeding any action, the controller's `initiate_action_feeding`
method. Otherwise `action` will panic. This method must be invoked regardless
of whether or not actions are actually fed, because Tnua uses it to know that
an action was not fed and it can finish it.

This is an issue for actions like `TnuaBuiltinKnockback` that are invoked as a
response for external events. For this, other action feeding methods can be
used:

* `action_trigger` - for actions that don't care how long the button is
  pressed. For example - a dash.
* `action_interrupt` - similar to `action_trigger`, but will override any other
  action contender (in the future maybe even running actions) and cannot be
  overridden itself. This is for things like `TnuaBuiltinKnockback` that are
  forced upon the character.
* `action_start` and `action_end` - for feeding when the button is pressed and
  released, without worrying about the frames in-between.

## Other actionable changes of note

### Basis and action structure and terminology

- The configuration was used to be part of the action struct. Now it is split
  to a `Config` struct, and the action struct is called "input".
- The `State` property of basis and actions is now named `Memory`. This is
  mostly important for those writing custom basis/actions, but also for those
  who inspect or modify that internal data (e.g. to decide the animation)
- The term `State` now means the triplet of input, config and memory (and
  sometimes it contains payloads)

### Accessing current basis and action data

- Basis and actions no longer have `&'static str` names - the basis will not
  change and the action is identified by the variant. The `TnuaScheme` derive
  generates an enum with the name of the control scheme enum plus the
  `ActionDiscriminant` suffix, and there are methods like `discriminant` on the
  input/state and `action_discriminant` on the controller for querying it.
- `concrete_basis` is no longer needed - instead, to access the basis' state,
  the controller has the direct fields `basis` (the input), `basis_config` (a
  copy of the configuration part from the asset) and `basis_memory`.
- `concrete_action` no longer exists - instead access the `current_action`
  field directly. That field is of an enum type generated by the `TnuaScheme`
  derive, named after the control scheme enum plus the `ActionState` suffix. It
  contains the action's state (input, config, and memory)

### Sensors (and ghost sensors)

- Sensors are no longer on the same entity as the controller. To get the entity
  that represents a sensor, use the `entities` field of the controller.
  Different basis may result in different structure of sensors and thus
  different sensors `Entities` struct. These entities will get created
  automatically by the controller based on the basis' definition.
- `TnuaGhostSensor` needs to be on the sensor entity, not the controller
  entity. If you add the `TnuaGhostOverwrites` component to the controller
  entity, it'll automatically add `TnuaGhostSensor` to its (relevant) sensors.
- Overwriting the `output` field of the `TnuaProximitySensor` no longer works.
  Instead, use the `set` method of `TnuaGhostOverwrite` (the fields of
  `TnuaGhostOverwrites` should be of that type)

### Various helpers

- `TnuaCrouchEnforcer` is gone. `TnuaBuiltinCrouch` can do what it did by
  itself now - but only if the `headroom` field is properly configured in
  `TnuaBuiltinWalkConfig`.
- `TnuaSimpleAirActionsCounter` now accepts the control scheme as a generic
  parameter - and you need to implement `TnuaAirActionDefinition` for your
  scheme.
- `TnuaBlipReuseAvoidance` now accepts the control scheme as a generic
  parameter - and you need to implement `TnuaHasTargetEntity` for your scheme.
  Note that the entities are no longer part of the actions - they should be
  part of the payloads.

# Migrating to Tnua 0.16

All plugins now support specifying a schedule, which means that they are no
longer unit structs. They all now implement `Default` to retain the same
behavior, and have a `new()` method that accepts a Bevy `ScheduleLabel`.

Only use `new()` if you are changing the scheduling of of the physics backend:

- When using Rapier's `in_fixed_schedule()` or XPBD's equivalent
  `PhysicsPlugins::new(FixedUpdate)`, use `new(FixedUpdate)` for the Tnua
  plugins.
- When using XPBD's `Physics::fixed_hz` timer, use `new(PhysicsSchedule)` for
  the Tnua plugins.
- Either way - add the player controller system in the same schedule as the
  Tnua plugins (defaults to `Update`)

# Migrating to Tnua 0.13

- Instead of using the `rapier_2d` or the `rapier_3d` features, add the
  `bevy-tnua-rapier2d` or the `bevy-tnua-rapier3d` crate.
- The `TnuaRapier2d*` or `TnuaRapier3d*` components are no longer in
  `bevy_tnua::prelude`. They should be imported from the new crates.

# Migrating to Tnua 0.10

## Basic ECS initialization

- Instead of `TnuaPlatformerPlugin`, add `TnuaControllerPlugin`.
- Instead of `TnuaPlatformerBundle`, add `TnuaControllerBundle`. Note that
  `TnuaControllerBundle` does not need to be configured - the entire
  configuration is passed with the basis and the actions.

## Character control

Controls should still be passed every frame, preferably
`in_set(TnuaUserControlsSystemSet)`. Instead of using the
`TnuaPlatformerControls` component, the controls should be passed to the
`TnuaController` component.

### Base movement

Instead of changing `TnuaPlatformerControls::desired_velocity`, pass the
`TnuaBuiltinWalk` basis to `TnuaController` and set the `desired_velocity`
there:
```rust
controller.basis(TnuaBuiltinWalk {
    desired_velocity: the_desired_velocity,
    float_height: 2.0, // must be passed as well in this new scheme
    ..Default::default()
});
```
* Note that there is no longer a separate `full_speed` configuration field.
  `TnuaBuiltinWalk::desired_velocity` represents the speed as well as the
  direction.

### Jumping

Instead of changing `TnuaPlatformerControls::jump`, use the `TnuaBuiltinJump`
action:
```rust
if should_jump {
    controller.action(TnuaBuiltinJump {
        height: 4.0,
        ..Default::default()
    });
}
```
* Note that there is no longer a separate `full_jump_height` configuration
  field. `TnuaBuiltinJump::height`, which is part of the action and gets passed
  on each frame, controls it.
* When the action is no longer fed, the jump is stopped. There is no need to
  manually nullify the command.

### Crouching

To crouch, instead of changing the
`TnuaPlatformerControls::float_height_offset`, use the `TnuaBuiltinCrouch`
action.
```rust
if should_crouch {
    controller.action(TnuaBuiltinCrouch {
        float_offset: -0.9,
        ..Default::default()
    });
}
```
* Instead of `TnuaKeepCrouchingBelowObstacles`, use `TnuaCrouchEnforcer`. See
  its documentation regarding its usage.

### Turning

Tnua no longer turns the character automatically according to the movement
direction. `TnuaBuiltinWalk::desired_forward` must be passed manually in order
to make the character turn.

`TnuaManualTurningOutput` was completely removed, without being replaced by
anything. There is no longer a reason for Tnua to manage this kind of manual
turning. Just use some tweening plugin.

The forward direction is **always** assumed to be the negative Z axis. If there
is some other forward direction, the game must do the math itself and set
`desired_forward` to set the negative Z axis of the model to the correct
direction.

## Animating

`TnuaPlatformerAnimatingOutput` is removed. Instead, the animating data should
be taken directly from the `TnuaController`.

The state of the basis can be retrieved with `TnuaController::concrete_basis`:

```rust
let Some((basis_input, basis_state)) = controller.concrete_basis::<TnuaBuiltinWalk>()
else {
    // in case some other basis is used - usually happens before the first basis is fed.
    continue;
};

// basis_input is the `TnuaBuiltinWalk` command, which is usually not very interesting.

// This replaces `let speed = animating_output.running_velocity.length();`:
let speed = basis_state.running_velocity.length();
```

Many games would only ever use one basis (probably `TnuaBuiltinWalk`), but a
game that uses multiple bases (e.g. - different bases for walking and for
swiming) can retrieve the name of the current basis by using the
`TnuaController::basis_name` method. This returns a string that can be
`match`ed on, which should be faster than trying multiple downcasts with
`concrete_basis`.

Actions, similarly, have `TnuaController::concrete_action` and
`TnuaController::action_name` (which is more useful than `basis_name`, because
it should be more common for games to use multiple actions, and because action
may play animation based on the action name only, regardless of the action
state)

For both bases and actions, when matching on `basis_name`/`action_name`, prefer
using the `NAME` constant of the basis/action over a string literal:

```rust
match controller.action_name() {
    Some(TnuaBuiltinJump::NAME) => { /* ... */ }
    Some(TnuaBuiltinCrouch::NAME) => { /* ... */ }
    Some(other) => panic!("Unknown action {other}"),
    None => { /* ... */ }
}
```

Of course, when using `named_basis`/`named_action` it is perfectly acceptable
to use the string literal.

## Storing configuration as data

There is no longer `TnuaPlatformerConfig` component, but it may still be
desirable to store a character's movement configuration in the ECS. The
simplest way to do this is to store the basis and the actions themselves in the
ECS, and clone them into the controller when they are needed.

In the examples, this is done by declaring a new component:
```rust
#[derive(Component)]
pub struct CharacterMotionConfigForPlatformerDemo {
    pub speed: f32,
    pub walk: TnuaBuiltinWalk,
    pub jump: TnuaBuiltinJump,
    pub crouch: TnuaBuiltinCrouch,
}
```

which is then added to the character entity:

```rust
cmd.insert(CharacterMotionConfigForPlatformerDemo {
    speed: 40.0,
    walk: TnuaBuiltinWalk {
        float_height: 2.0,
        ..Default::default()
    },
    jump: TnuaBuiltinJump {
        height: 4.0,
        ..Default::default()
    },
    crouch: TnuaBuiltinCrouch {
        float_offset: -0.9,
        ..Default::default()
    },
});
```

Note that since `TnuaBuiltinWalk::desired_velocity` is a vector, and the
configuration should only store its magnitude - not direction - we store the
`speed` as a separate field.

Then, in the controls system, the basis and actions can simply be `clone`d:

```rust
controller.basis(TnuaBuiltinWalk {
    desired_velocity: direction * config.speed,
    ..config.walk.clone()
});

if crouch {
    controller.action(config.crouch.clone());
}

if jump {
    controller.action(config.jump.clone());
}
```