Commit Graph

23 Commits

Author SHA1 Message Date
Bevy Auto Releaser a74009b22e chore: Release 2026-06-21 16:55:28 +00:00
codecnotsupported f01eff8b55 Fix PanCamera mouse button check (#24659)
# Objective

Fix PanCamera to check which that the correct mouse button is used, not
just any.
Change default pan mouse button to middle mouse button.

## Solution

Add check.

## Testing

- Did you test these changes? If so, how?
- Tested in local test environment, then like a cave man, copy & pasted
the changes into github's text editor of the fork.

---------

Co-authored-by: N/A <N/A>
2026-06-19 04:00:00 +00:00
Azural Champagne 6561ee9d46 Replace MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR with a Resource (#24512)
# Objective

Fixes #24508

## Solution

Replace `MouseScrollUnit` with a Resource of the same name and remove it
from the structs and events related to scrolling. More work still needs
to be done to make sure that the value in `MouseScrollUnit` is actually
respected

## Testing

- Running standard test suite/ci as well as related examples to make
sure that they haven't broken
2026-06-09 04:48:31 +00:00
TinyGodzilla 51a80d0a14 free camera controller add local space Y controls (#24161)
# Objective

- Fixes https://github.com/bevyengine/bevy/issues/24141

## Solution

- Added Two additional keys (R & F) for local space Y velocity. 
- following the [Blender Walk/Fly
Mode](https://docs.blender.org/manual/en/latest/editors/3dview/navigate/walk_fly.html)
controls.
- Added `FreeCamera::invert_world_local_control()` for alternative
keybinds where Q/E is local Space instead, and R/F is world Space.
    - Some user probably more familiar with Q/E as local space up.
- Updated Examples to resolve key conflict with scroll factor controls
    - from F/G to G/H 

Note: This is my first contribution. If there's anything I am missing
please point it out :)

## Testing

- Did you test these changes? If so, how?
  - I tested with `free_camera_controller` example
- Are there any parts that need more testing?
  - Don't think so.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - Don't think so. Straightforward change.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - tested on Windows 11. It's not platform specific so should be ok.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2026-05-08 18:40:00 +00:00
Carter Anderson aadbf46184 Scene Components (#24008)
# Objective

It is essentially a Bevy rite of passage to define a `Player` component
and then ask "ok when I spawn `Player`, how do I spawn all of the other
entities / components / scenes it needs to function with it?".

In Bevy, components are the "unit of behavior". The presence of a
component indicates that the entity should behave a certain way. Scenes
are _inextricable_ from behaviors, yet currently there is no way to tie
Components (bevy's unit of behavior) to scenes. _This is a fundamental
gap in the Bevy data model_. It is a problem that developers expect
their engine to solve for them.

It is high time that we had an official solution to this problem. BSN
was built to be that solution, and it is now time to do the last-mile
work to facilitate the `Component <-> Scene` relationship

## Solution

It is now possible to define "scene components", where `SceneComponent:
Component`:

```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
    score: usize
}

impl Player {
    fn scene() -> impl Scene {
        bsn! {
            Transform { translation: Vec3 { x: 10. } }
            Children [
                LeftHand,
                RightHand,
            ]
        }
    }
}
```

This enables inheriting from the Player component as a scene:

```rust
world.spawn_scene(bsn! {
    :Player { score: 0 }
})
```

After the inherited `Player` scene component is spawned, the _entire_
scene is available. This means that you can `Query<&Player>` in your ECS
systems, and _trust_ that the entire scene is also present.

### Inheritance Syntax vs Patch Syntax

Notice that this uses inheritance syntax in BSN (`:`), rather than
normal "component patch" syntax (ex: `bsn! { Player { score: 0 } }`.
Semantically these are different things:
- Scene inheritance syntax: constructs the full scene and inherits from
it
- Component patch syntax: _Just_ patches the component directly and
creates it if it doesn't exist. This will not do any scene inheritance.
You can still patch scene components this way as long as the scene
component is inherited somewhere "earlier" in the inheritance hierarchy.

Attempting to spawn a scene component on its own _without_ the scene
(ex: `commands.spawn(Player::default())`) will log an error, as Scene
Components should never exist without their scene.

### Custom Scene Functions

When deriving `SceneComponent`, it defaults to using `Self::scene` as
the "scene function". Scene functions can also be manually specified:

```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(player)]
struct Player {
    score: usize
}

fn player() -> impl Scene {
   bsn! { /* scene here */}
}
```

### Inherit BSN Asset Paths

Alternatively, a scene asset path can be specified:

```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.bsn")]
struct Player {
    score: usize
}
```

Once we port the glTF loader to BSN, this will also be supported:
```rust
#[derive(SceneComponent, Default, Clone)]
#[scene("player.gltf")]
struct Player {
    score: usize
}
```

### Scene Props

Sometimes it is desirable to "parameterize" a scene: pass in values to
the scene which determine what the scene outputs are.

The answer to this in BSN is "scene props":

```rust
/// A UI widget that repeats "hello" text a given number of times.
#[derive(SceneComponent, Default, Clone)]
#[scene(HelloRepeaterProps)]
struct HelloRepeater;

#[derive(Default)]
struct HelloRepeaterProps {
    repeat: usize,
}

impl HelloRepeater {
    fn scene(props: HelloRepeaterProps) -> impl Scene {
        let hellos = (0..props.repeat)
            .map(|_| bsn!{ Text("hello") })
            .collect::<Vec<_>>();
        bsn! {
            Node
            Children [
                {hellos}
            ]
        }
    }
}

world.spawn_scene(bsn! {
   :HelloRepeater {
       @repeat: 5
   } 
});
```

Notice the `@field` syntax, which specifies that a prop is being set
instead of a field. Props are evaluated "immediately" at the point of
inheritance where the scene is constructed. This means that they are not
"patchable".

You can set _both_ props and normal fields at the same time:

```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(WidgetProps)]
struct Widget {
    value: usize
}

#[derive(Default)]
struct WidgetProps {
    border: bool,
}

world.spawn_scene(bsn! {
   :Widget {
       @border: true,
       value: 10,
   } 
});
```

Scene props can be used with templates:

```rust
#[derive(SceneComponent, Default, Clone)]
#[scene(PlayerProps)]
struct Player;

#[derive(Default)]
struct PlayerProps {
    head: AssetPath<'static>,
    body: AssetPath<'static>,
}

impl Player {
    fn scene(props: PlayerProps) -> impl Scene {
        bsn! {
            Transform
            Visibility
            Children [
                (Head, Sprite { image: {props.head} }),
                (Body, Sprite { image: {props.body} }),
            ]
        }
    }
}

world.spawn_scene(bsn! {
   :Player {
       @head: "big_head.png"
       @body: "small_body.png"
   } 
});
```

### Scene Components are Template-able

```rust
#[derive(SceneComponent, FromTemplate)]
struct Player {
    image: Handle<Image>,
}

impl Player {
    fn scene() -> impl Scene {
        bsn! { /* scene here */}
    }
}

world.spawn_scene(bsn! {
   :Player { image: "player.png" } 
});
```


### The Scene Component is Always Added

Specifying the scene component manually in the scene function is not
necessary. It will be added automatically:

```rust
#[derive(SceneComponent, Default, Clone)]
struct Player {
    score: usize
}

impl Player {
    fn scene() -> impl Scene {
        bsn! {
            // No need to specify a Player component here.
            // It is implied!
        }
    }
}
```

However you _can_ patch the scene component in the scene if you would
like. This comes in handy if you would like props to contribute to the
scene component's value:

```rust
impl Player {
    fn scene(props: PlayerProps) -> impl Scene {
        bsn! {
            Player {
                size_in_meters: {props.size_in_millimeters / 1000. }
            }
        }
    }
}
```

### SceneComponent Trait

This is what SceneComponent looks like under the hood:

```rust
pub trait SceneConstructor: FromTemplate<Template: Default>
where
    <Self::Template as Template>::Output: Component,
{
    type Props: Default;
    fn scene(props: Self::Props) -> impl Scene;
}
```

In general, developers should prefer `#[derive(SceneComponent)`, which
derives this trait automatically, ensures the Component is never spawned
without its scene, and automatically initializes the `Component` even if
it isn't specified explicitly in the user-defined scene function.

### Reusable Component Derive Logic

This PR moves the component derive logic into a new
`bevy_ecs_macro_logic` crate and refactors it to be reusable and
extendable.

This enables the `SceneComponent` derive to reuse and configure the
`Component` derive. I also took the liberty of porting the redundant
`Resource` derive logic to this system, as it follows the same pattern.

## PR Todo

- [ ] Port this PR description to docs on `Scene`.

## Whats Next

- Scene Component scene caching: It might make sense to cache
unparameterized scenes (like we do for scene assets) to cut down on
spawn costs. We could even have an opt-in parameterized scene cache (ex:
hash the props).
- Reactivity: It is important to note that props _are not reactive_.
They do not exist post-spawn (aka "at runtime") and they cannot be
changed. However they could easily be _part_ of a reactivity story:
- Props as components: insert props as components and re-run the scene
constructor when they are mutated
- Signal props: Reactive "signal" types could be passed down the init
hierarchy via props.
- Simpler "prop patching" codegen: currently for simplicity props reuse
the template-patching logic, but this introduces a few constraints and
complexities that are unnecessary. We should consider writing
specialized logic for them.
2026-05-01 21:07:22 +00:00
Sigma-dev 0d68d94d37 Pan camera mouse pan (#22859)
# Objective

The current PanCamera only supports keyboard panning, when a common use
case is mouse panning.

## Solution

Add optional mouse panning with some settings

## Testing

I ran the Pan Camera example with the new settings

## Additional notes

I added settings for setting the exact mouse button that triggers the
panning, and whether or not to grab the cursor when panning.

I am unsure about the cursor grab part of this design as it may mess
with the app's existing cursor options and the window part may not work
for some setups.

It may be wiser to just omit grabbing for now, or maybe keep grabbing
but don't change visibility idk



As always I am open to any and all feedback.

---------

Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2026-04-10 03:17:39 +00:00
taishi-sama 0b49ff72d4 Snap to view (#23674)
# Objective

- Adds ``SnapToView`` camera controller as solution to #23499 

## Solution

- Adds ``snap_to_view`` module behind ``free_camera`` feature flag 
## Testing

- Manually tested with ``cargo run --example free_camera_controller
--features="free_camera bevy_dev_tools"`` and ``cargo run --example
scene_viewer --features="free_camera bevy_dev_tools"``:
  - All six hotkeys works.
    - [LCtrl +] Numpad1  - Snap to front/back
    - [LCtrl +] Numpad3  - Snap to right/left
    - [LCtrl +] Numpad7  - Snap to top/bottom
- Tested on Linux (Wayland)
---

## Showcase


https://github.com/user-attachments/assets/cca71180-c273-473f-a168-9793438d861f

Add ``SnapToViewCamera`` component to any ``Camera3d`` entity. 
Change parameters inside ``SnapToViewCamera`` to change snap speed and
hotkeys.

```rust
app.add_plugins(SnapToViewPlugin);

commands.spawn((
    Camera3d::default(),
    SnapToViewCamera::default(),
));
```

---------

Co-authored-by: taishi-sama <alexandra.2002.mikh@gmail.com>
2026-04-07 00:07:13 +00:00
marcerhans 43ab995229 Added touch input for the FreeCameraPlugin. (#23316)
# Objective

I have a laptop with a touch screen, and being able to use that rather
than the touchpad for navigating the free camera was a nice addition.

## Solution

Added touch input handling for rotating the camera.

## Testing

Tested on my laptop by running the already existing free camera example.
2026-03-11 20:05:42 +00:00
Kevin Chen 56da4bb798 Makes Free Camera speed change exponential wrt scroll (#23066)
# Objective

- Adopts and closes #21453 and goes the side of using `exp`
- Supersedes and closes #21483 
- Make free_camera more usable at very large or very small scales

## Solution

- Basically updated #21453 to use `exp` instead of `powf` with a percent
change. This means that `scroll_factor` no longer means “the additional
percentage added to/subtracted from speed."
- I made sure to add some helpful comments in `scroll_factor` for
determining an appropriate value for `scroll_factor` and what a
reasonable default is. Hopefully it’s not too wordy

## Testing

I tested with `cargo run --example free_camera_controller
--features=“free_camera”` and `cargo run --example 3d_gizmos
--features=“free_camera”` and made sure the controls feel good. Compared
to `main`, it doesn’t take too long to go really fast and decreasing
slower than the initial speed is more gradual.
2026-02-24 01:10:39 +00:00
Guillaume Gomez 28fd2cb3c0 Enable the rustdoc "--generate-macro-expansion" feature (#23075)
You can see this feature in action in the compiler docs like
[here](https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_ast_lowering/errors.rs.html#323)
or
[here](https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_ast_lowering/format.rs.html#89).
2026-02-21 00:24:25 +00:00
breakdown_dog 4bbaca8fbf Changed mouse grab button from Left to Right for FreeCamera (#22953)
# Objective

- Currently, the `FreeCamera` uses MouseButton::Left for cursor
grabbing, which risks input conflicts, for example, if scene selection
tools are introduced, as both actions would map to the same trigger.
- Remapping this to MouseButton::Right aligns with industry-standard
interaction paradigms (e.g., Unity, Unreal, Blender), where the right
mouse button is reserved for view manipulation, leaving the left button
exclusively for object selection and UI interaction."

## Solution

- Changed mouse grab button from Left to Right for `FreeCamera`

## Testing

- The `free_camera_controller` example works properly.
- CI

---
2026-02-17 00:35:23 +00:00
atlv 59a54d38af remove redundant warns (#22807)
# Objective

- we have a blanket deny on missing docs, we don't need specific warns
on it

## Solution

- yeet

## Testing

- ci
2026-02-05 01:00:19 +00:00
github-actions[bot] d772c32071 Bump Version after Release (#22498)
Bump version after release
This PR has been auto-generated

---------

Co-authored-by: Bevy Auto Releaser <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: François Mockers <francois.mockers@vleue.com>
2026-01-14 18:21:56 +00:00
Jonathan Schmittat f39178cd08 Fix typo in doc comment [minor] (#22367)
# Objective

- typo: "radiradians" instead of "radians"

## Solution

- Describe the solution used to achieve the objective above.

## Testing

- no testing needed, just a modified doc comment
2026-01-03 22:15:18 +00:00
syszery 9a9817bba1 Update PanCam and FreeCam to use full term Camera (#21592)
# Objective

This PR fixes #21569, which proposes renaming the newly introduced
camera controller modules `FreeCam` and `PanCam` to use the full term
`Camera`.

## Solution

* Renamed the `PanCam` controller, `PanCamPlugin`, and related methods
to use the full term `Camera` instead of the abbreviation `Cam`.
* Renamed the module from `pan_cam` to `pan_camera` for consistency with
naming conventions.
* Updated the example `pan_camera_controller` and adjusted usage of the
renamed controller and plugin.
* Updated documentation and release notes accordingly.


## Follow-up Work

I see two options from here:

1. **Use this PR as a reference** for renaming `FreeCam`. The process is
similar and could be a great first issue for someone looking to
contribute to the new camera modules or Bevy in general. Most of the
changes follow the same pattern, although `FreeCam` has more examples
that need updating. One could find them using `grep` (e.g., `grep
FreeCam`) or by reviewing the diff from the PR that introduced
`FreeCam`: #20215

2. **I can continue and update this PR** to also handle the `FreeCam`
renaming, if you'd prefer to resolve the entire issue in one go.

---------

Co-authored-by: syszery <syszery@users.noreply.github.com>
2025-10-20 21:49:37 +00:00
mgi388 bd3b23e8c0 Rename PanCam.enable to enabled (#21568)
I noticed in https://github.com/bevyengine/bevy/pull/21520 that it used
`enable` but `FreeCamState` uses `enabled` (as does all other Bevy
code).

**Testing**

CI
2025-10-17 00:05:35 +00:00
syszery a4404a5d29 refactor(free-cam): separate configuration settings and dynamic state for the free camera controller (#21538)
# Objective

This PR refactors the `FreeCam` controller to improve its modularity and
flexibility. Happy to adjust based on feedback or suggestions!

Fixes #21456


## Solution

* **Splits the `FreeCam` system into two components**:
* `FreeCam`: Stores the **configuration** of the camera controller,
including key bindings, movement speeds, and sensitivity. This struct is
immutable during runtime, ensuring that the core settings stay
consistent.
* `FreeCamState`: Manages the **dynamic runtime state** of the camera,
such as the current pitch, yaw, velocity, and speed multiplier. This
component allows for real-time adjustments to camera behavior without
altering the original settings.
* **Improves the example for debugging and testing**:
A simplified example (`free_cam_simple`) has been introduced, allowing
for easy testing and showcasing of the new FreeCam controller system.
* **Fixes the bug with translation speed**:
As noted in #21483, the camera’s translation speed would "stick" after
excessive scrolling. This bug was still present in the
`free_cam_controller` example. Following the approach suggested in
#21486, the bug has been resolved in the new `free_cam_simple` example
(I did not yet verify the existing ones).
* **Clarifies documentation**:
Documentation has been updated to reflect the new structure, clearly
describing the roles of both `FreeCam` (static settings) and
`FreeCamState` (dynamic state).


## Testing

* For testing, I added a simple example `free_cam_simple` as a POC (this
can be removed later if no longer needed).
* Run with: `cargo run --example free_cam_simple --features="free_cam"`

---------

Co-authored-by: syszery <syszery@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-10-16 21:37:05 +00:00
syszery 81d9c88950 feat(pan-cam): add scaffolding for 2D pan camera controller (#21520)
# Objective

Implements scaffolding for a 2D pan camera controller.

Fixes #21468 


## Solution

- Introduced a `PanCam` component with settings for panning, zooming,
and rotation via keyboard input, following the design of the existing
`FreeCam`.
- Added a `PanCamPlugin` to register the controller system.
- Implemented keyboard-based panning and rotation.


## TODOs

- Movement is currently world-axis aligned. 
  - TODO: Consider movement relative to camera rotation.
- Zooming support is scaffolded with config fields but not yet
implemented.


## Testing

Unfortunately, I was unable to fully test this implementation due to
issues running graphical output with GPU acceleration under WSL.
As a result, zoom behavior and rotation effects remain TODOs, and the
whole code could not be fully verified.

Once I resolve the GPU passthrough issues, I plan to complete and test
the remaining features (with a more meaningful example).

---

I'm happy to hear any suggestions or feedback in the meantime!

---------

Co-authored-by: syszery <syszery@users.noreply.github.com>
Co-authored-by: Janis <130913856+janis-bhm@users.noreply.github.com>
2025-10-16 21:32:24 +00:00
Janis fef6b51d59 FreeCam: stop cursor from remaining grabbed when camera controller is disabled (#21485)
# Objective
Fixes #21482

## Solution
Before exiting the system when the controller is disabled, check if
either of the cursor grab flags are set and ungrab any windows if so.
Note: I think it's a bit strange that the camera un-grabs every window,
not just the one with focus, but I've just copied this behaviour from
further down in the function. In this case, it might be more correct to
do this anyway, since the controller might be disabled when the
respective window isn't focused.

## Testing

I used #21477 to test this
2025-10-10 16:17:02 +00:00
Ross Leonardy 63a70ed030 Change friction behavior in freecam to use smooth nudge #21455 (#21464)
# Objective
- Fixes #21455 
- Change the behavior of freecam to use smooth nudge instead of an
exponential, per-frame fall off

## Solution
- As written, the freecam's velocity decays by half every loop.  
- Instead, I used smooth_nudge, which takes the time difference into
account so that its behavior isn't depended on the fixed main loop's hz.
- The freecam plugin took a f32 that defaulted to 0.5, and was written
so lower numbers would have faster decay. In smooth nudge terms, you
could describe this as 64 log 2 at the fixed time step of 64 hz, which
is a decay rate of around 44.3.
- I used 40.0 as this seems like an easily understandable default. 
- This is a breaking change because now higher numbers (instead of lower
numbers) supplied to `friction`, will increase the decay rate. However,
all of the examples that use freecamplugin relied on the default()
friction value, and it was only recently added, so I think this is okay.

## Testing

- I tested this change in the examples that use freecam; 
---
2025-10-08 20:35:29 +00:00
Alice Cecile 80e8d37d3f Reduce default sensitivity for free_cam controller (#21451)
# Objective

As pointed out by @BigWingBeat in 
https://github.com/bevyengine/bevy/pull/20215#issuecomment-3099726369
the default mouse sensitivity of the free_cam controller is crazy high.

## Solution

Divide the sensitivity value by 5. 10 was too much, but 5 feels about
right for a "careful and controlled scene editor".

## Testing

`cargo run --example 3d_gizmos --features="free_cam"`
2025-10-08 00:51:58 +00:00
Alice Cecile f578a387e1 Add a conversion factor for MouseScrollUnit and use in camera controller (#21450)
# Objective

As noted in
https://github.com/bevyengine/bevy/pull/20215#issuecomment-3099068564 by
@BigWingBeat, the conversion factor used in the free cam camera
controller code that was added in #11921 arbitrary and untested.

This is a problem that users have stumbled across elsewhere, including
in #21140 and myself when writing leafwing-input-manager!

## Solution

1. Define a constant with the correct conversion factor, at least as
reported in #21140. This goes in `bevy_input` as it's broadly useful.
2. Use that constant in bevy_camera_controller::free_cam.
3. Approximately scale the default value on the camera controller to
compensate.

## Testing

We use this controller in a number of our examples. I chose `3d_gizmos`,
which worked just about the same before and after. The scroll wheel
controls your "walking speed".
2025-10-08 00:14:04 +00:00
Alice Cecile 66e8232412 Add bevy_camera_controllers crate and move freecam implementation into it (#20215)
# Objective

Moving the camera around is really important to validate that our scenes
are rendered correctly.

Bevy engine devs currently do this via a fairly cursed "library example"
which includes a basic freecam controller (which is technically distinct
from a flycam apparently). This has three problems:

1. Oh god why are we taking pseudo-dependencies on other examples.
2. Something like this would be useful for debugging for users.
3. Something like this would be useful for the editor and other tooling.

## Solution

Create a new `bevy_camera_controllers` crate, and move the existing
freecam implementation into it, nearly verbatim.

This cleans up some ugly tech debt in how we do this, makes it easier to
add a camera controller to other examples, and also gives us a scaffold
for future camera controllers.

This PR has been marked `X-Blessed`, as I went over this plan with @cart
in a [meeting on
2025-06-23](https://discord.com/channels/691052431525675048/692572690833473578/1386847828923519038),
and we both agreed that this was the right first step.

## Testing

I've tested the examples that rely on this camera controller: they work
just like before.

## Notes for reviewers

- I don't intend to land this in Bevy 0.17: the existing example code is
not high enough quality for me to be happy shipping this
- we're also soft feature-frozen; I was just in the mood to do this
today
- this PR has some minimal cleanup: just enough naming and docs work to
make this code not an active liability. Any more will risk derailing
this PR and making it harder to review
- I've opted to expose top-level feature flags for the various camera
controllers. This is a bit noisy, but it was the only sensible way I
could see to both have "no camera controllers by default" and "still
easy to use these controllers in examples"
- this will not be the only camera controller in this crate, or the Bevy
Editor
- this is not the place to discuss "what should the default camera
controller for the Bevy Editor be"
- if you think this crate, or this particular move is the wrong strategy
for some other reason though, please do speak up :)
- my personal (unblessed) hope is that we continue to add camera
controllers to this crate as needed for various first-party tooling. I
will probably largely push-back on adding camera controllers that we do
not actively use to this crate, since I don't think they make great
libraries in general (much better to copy-paste)

## TODO

- [x] add release note. I'm waiting until after Bevy 0.17 ships to do
this
- [x] update the examples/README.md file. I was lazy and didn't want to
constantly resolve merge conflicts on that while this PR sits
- [x] change to singular crate name

## Future work

- split apart the giant camera controller component
- make keybinding better, either with or without a real input manager
solution
- split apart the system to make it more modular
- make the system handle fixed timesteps better
- use the camera controller in more examples for debugging
- add more camera controllers!

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
2025-10-07 19:39:17 +00:00