2022-09-24 13:21:01 +00:00
|
|
|
//! Shows a visualization of gamepad buttons, sticks, and triggers
|
|
|
|
|
|
|
|
use std::f32::consts::PI;
|
|
|
|
|
|
|
|
use bevy::{
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
input::gamepad::{
|
|
|
|
GamepadAxisChangedEvent, GamepadButton, GamepadButtonChangedEvent, GamepadSettings,
|
|
|
|
},
|
2022-09-24 13:21:01 +00:00
|
|
|
prelude::*,
|
Remove VerticalAlign from TextAlignment (#6807)
# Objective
Remove the `VerticalAlign` enum.
Text's alignment field should only affect the text's internal text alignment, not its position. The only way to control a `TextBundle`'s position and bounds should be through the manipulation of the constraints in the `Style` components of the nodes in the Bevy UI's layout tree.
`Text2dBundle` should have a separate `Anchor` component that sets its position relative to its transform.
Related issues: #676, #1490, #5502, #5513, #5834, #6717, #6724, #6741, #6748
## Changelog
* Changed `TextAlignment` into an enum with `Left`, `Center`, and `Right` variants.
* Removed the `HorizontalAlign` and `VerticalAlign` types.
* Added an `Anchor` component to `Text2dBundle`
* Added `Component` derive to `Anchor`
* Use `f32::INFINITY` instead of `f32::MAX` to represent unbounded text in Text2dBounds
## Migration Guide
The `alignment` field of `Text` now only affects the text's internal alignment.
### Change `TextAlignment` to TextAlignment` which is now an enum. Replace:
* `TextAlignment::TOP_LEFT`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_LEFT` with `TextAlignment::Left`
* `TextAlignment::TOP_CENTER`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_CENTER` with `TextAlignment::Center`
* `TextAlignment::TOP_RIGHT`, `TextAlignment::CENTER_RIGHT`, `TextAlignment::BOTTOM_RIGHT` with `TextAlignment::Right`
### Changes for `Text2dBundle`
`Text2dBundle` has a new field 'text_anchor' that takes an `Anchor` component that controls its position relative to its transform.
2023-01-18 02:19:17 +00:00
|
|
|
sprite::{Anchor, MaterialMesh2dBundle, Mesh2dHandle},
|
2022-09-24 13:21:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const BUTTON_RADIUS: f32 = 25.;
|
|
|
|
const BUTTON_CLUSTER_RADIUS: f32 = 50.;
|
|
|
|
const START_SIZE: Vec2 = Vec2::new(30., 15.);
|
|
|
|
const TRIGGER_SIZE: Vec2 = Vec2::new(70., 20.);
|
|
|
|
const STICK_BOUNDS_SIZE: f32 = 100.;
|
|
|
|
|
|
|
|
const BUTTONS_X: f32 = 150.;
|
|
|
|
const BUTTONS_Y: f32 = 80.;
|
|
|
|
const STICKS_X: f32 = 150.;
|
|
|
|
const STICKS_Y: f32 = -135.;
|
|
|
|
|
2024-01-23 06:27:43 +00:00
|
|
|
const NORMAL_BUTTON_COLOR: Color = Color::rgb(0.3, 0.3, 0.3);
|
2022-09-24 13:21:01 +00:00
|
|
|
const ACTIVE_BUTTON_COLOR: Color = Color::PURPLE;
|
|
|
|
const LIVE_COLOR: Color = Color::rgb(0.4, 0.4, 0.4);
|
2024-01-23 06:27:43 +00:00
|
|
|
const DEAD_COLOR: Color = Color::rgb(0.13, 0.13, 0.13);
|
2022-09-24 13:21:01 +00:00
|
|
|
|
|
|
|
#[derive(Component, Deref)]
|
|
|
|
struct ReactTo(GamepadButtonType);
|
|
|
|
#[derive(Component)]
|
|
|
|
struct MoveWithAxes {
|
|
|
|
x_axis: GamepadAxisType,
|
|
|
|
y_axis: GamepadAxisType,
|
|
|
|
scale: f32,
|
|
|
|
}
|
|
|
|
#[derive(Component)]
|
|
|
|
struct TextWithAxes {
|
|
|
|
x_axis: GamepadAxisType,
|
|
|
|
y_axis: GamepadAxisType,
|
|
|
|
}
|
|
|
|
#[derive(Component, Deref)]
|
|
|
|
struct TextWithButtonValue(GamepadButtonType);
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
struct ConnectedGamepadsText;
|
|
|
|
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct ButtonMaterials {
|
|
|
|
normal: Handle<ColorMaterial>,
|
|
|
|
active: Handle<ColorMaterial>,
|
|
|
|
}
|
|
|
|
impl FromWorld for ButtonMaterials {
|
|
|
|
fn from_world(world: &mut World) -> Self {
|
|
|
|
let mut materials = world.resource_mut::<Assets<ColorMaterial>>();
|
|
|
|
Self {
|
Use `impl Into<A>` for `Assets::add` (#10878)
# Motivation
When spawning entities into a scene, it is very common to create assets
like meshes and materials and to add them via asset handles. A common
setup might look like this:
```rust
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn(PbrBundle {
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(StandardMaterial::from(Color::RED)),
..default()
});
}
```
Let's take a closer look at the part that adds the assets using `add`.
```rust
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
material: materials.add(StandardMaterial::from(Color::RED)),
```
Here, "mesh" and "material" are both repeated three times. It's very
explicit, but I find it to be a bit verbose. In addition to being more
code to read and write, the extra characters can sometimes also lead to
the code being formatted to span multiple lines even though the core
task, adding e.g. a primitive mesh, is extremely simple.
A way to address this is by using `.into()`:
```rust
mesh: meshes.add(shape::Cube { size: 1.0 }.into()),
material: materials.add(Color::RED.into()),
```
This is fine, but from the names and the type of `meshes`, we already
know what the type should be. It's very clear that `Cube` should be
turned into a `Mesh` because of the context it's used in. `.into()` is
just seven characters, but it's so common that it quickly adds up and
gets annoying.
It would be nice if you could skip all of the conversion and let Bevy
handle it for you:
```rust
mesh: meshes.add(shape::Cube { size: 1.0 }),
material: materials.add(Color::RED),
```
# Objective
Make adding assets more ergonomic by making `Assets::add` take an `impl
Into<A>` instead of `A`.
## Solution
`Assets::add` now takes an `impl Into<A>` instead of `A`, so e.g. this
works:
```rust
commands.spawn(PbrBundle {
mesh: meshes.add(shape::Cube { size: 1.0 }),
material: materials.add(Color::RED),
..default()
});
```
I also changed all examples to use this API, which increases consistency
as well because `Mesh::from` and `into` were being used arbitrarily even
in the same file. This also gets rid of some lines of code because
formatting is nicer.
---
## Changelog
- `Assets::add` now takes an `impl Into<A>` instead of `A`
- Examples don't use `T::from(K)` or `K.into()` when adding assets
## Migration Guide
Some `into` calls that worked previously might now be broken because of
the new trait bounds. You need to either remove `into` or perform the
conversion explicitly with `from`:
```rust
// Doesn't compile
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }.into()),
// These compile
let mesh_handle = meshes.add(shape::Cube { size: 1.0 }),
let mesh_handle = meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
```
## Concerns
I believe the primary concerns might be:
1. Is this too implicit?
2. Does this increase codegen bloat?
Previously, the two APIs were using `into` or `from`, and now it's
"nothing" or `from`. You could argue that `into` is slightly more
explicit than "nothing" in cases like the earlier examples where a
`Color` gets converted to e.g. a `StandardMaterial`, but I personally
don't think `into` adds much value even in this case, and you could
still see the actual type from the asset type.
As for codegen bloat, I doubt it adds that much, but I'm not very
familiar with the details of codegen. I personally value the user-facing
code reduction and ergonomics improvements that these changes would
provide, but it might be worth checking the other effects in more
detail.
Another slight concern is migration pain; apps might have a ton of
`into` calls that would need to be removed, and it did take me a while
to do so for Bevy itself (maybe around 20-40 minutes). However, I think
the fact that there *are* so many `into` calls just highlights that the
API could be made nicer, and I'd gladly migrate my own projects for it.
2024-01-08 22:14:43 +00:00
|
|
|
normal: materials.add(NORMAL_BUTTON_COLOR),
|
|
|
|
active: materials.add(ACTIVE_BUTTON_COLOR),
|
2022-09-24 13:21:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct ButtonMeshes {
|
|
|
|
circle: Mesh2dHandle,
|
|
|
|
triangle: Mesh2dHandle,
|
|
|
|
start_pause: Mesh2dHandle,
|
|
|
|
trigger: Mesh2dHandle,
|
|
|
|
}
|
|
|
|
impl FromWorld for ButtonMeshes {
|
|
|
|
fn from_world(world: &mut World) -> Self {
|
|
|
|
let mut meshes = world.resource_mut::<Assets<Mesh>>();
|
|
|
|
Self {
|
2024-02-08 18:01:34 +00:00
|
|
|
circle: meshes.add(Circle::new(BUTTON_RADIUS)).into(),
|
|
|
|
triangle: meshes.add(RegularPolygon::new(BUTTON_RADIUS, 3)).into(),
|
|
|
|
start_pause: meshes.add(Rectangle::from_size(START_SIZE)).into(),
|
|
|
|
trigger: meshes.add(Rectangle::from_size(TRIGGER_SIZE)).into(),
|
2022-09-24 13:21:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 13:33:29 +00:00
|
|
|
#[derive(Bundle)]
|
|
|
|
struct GamepadButtonBundle {
|
|
|
|
mesh_bundle: MaterialMesh2dBundle<ColorMaterial>,
|
|
|
|
react_to: ReactTo,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GamepadButtonBundle {
|
|
|
|
pub fn new(
|
|
|
|
button_type: GamepadButtonType,
|
|
|
|
mesh: Mesh2dHandle,
|
|
|
|
material: Handle<ColorMaterial>,
|
|
|
|
x: f32,
|
|
|
|
y: f32,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
mesh_bundle: MaterialMesh2dBundle {
|
|
|
|
mesh,
|
|
|
|
material,
|
|
|
|
transform: Transform::from_xyz(x, y, 0.),
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
react_to: ReactTo(button_type),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_rotation(mut self, angle: f32) -> Self {
|
|
|
|
self.mesh_bundle.transform.rotation = Quat::from_rotation_z(angle);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-24 13:21:01 +00:00
|
|
|
fn main() {
|
|
|
|
App::new()
|
|
|
|
.add_plugins(DefaultPlugins)
|
|
|
|
.init_resource::<ButtonMaterials>()
|
|
|
|
.init_resource::<ButtonMeshes>()
|
2023-03-18 01:45:34 +00:00
|
|
|
.add_systems(
|
|
|
|
Startup,
|
|
|
|
(setup, setup_sticks, setup_triggers, setup_connected),
|
|
|
|
)
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
update_buttons,
|
|
|
|
update_button_values,
|
|
|
|
update_axes,
|
|
|
|
update_connected,
|
|
|
|
),
|
|
|
|
)
|
2022-09-24 13:21:01 +00:00
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup(mut commands: Commands, meshes: Res<ButtonMeshes>, materials: Res<ButtonMaterials>) {
|
|
|
|
commands.spawn(Camera2dBundle::default());
|
|
|
|
|
|
|
|
// Buttons
|
|
|
|
|
|
|
|
commands
|
|
|
|
.spawn(SpatialBundle {
|
|
|
|
transform: Transform::from_xyz(BUTTONS_X, BUTTONS_Y, 0.),
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
.with_children(|parent| {
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::North,
|
|
|
|
meshes.circle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
0.,
|
|
|
|
BUTTON_CLUSTER_RADIUS,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::South,
|
|
|
|
meshes.circle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
0.,
|
|
|
|
-BUTTON_CLUSTER_RADIUS,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::West,
|
|
|
|
meshes.circle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
-BUTTON_CLUSTER_RADIUS,
|
|
|
|
0.,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::East,
|
|
|
|
meshes.circle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
BUTTON_CLUSTER_RADIUS,
|
|
|
|
0.,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
|
|
|
});
|
|
|
|
|
|
|
|
// Start and Pause
|
|
|
|
|
2022-10-06 13:33:29 +00:00
|
|
|
commands.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::Select,
|
|
|
|
meshes.start_pause.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
-30.,
|
|
|
|
BUTTONS_Y,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
|
|
|
|
2022-10-06 13:33:29 +00:00
|
|
|
commands.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::Start,
|
|
|
|
meshes.start_pause.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
30.,
|
|
|
|
BUTTONS_Y,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
// D-Pad
|
|
|
|
|
|
|
|
commands
|
|
|
|
.spawn(SpatialBundle {
|
|
|
|
transform: Transform::from_xyz(-BUTTONS_X, BUTTONS_Y, 0.),
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
.with_children(|parent| {
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::DPadUp,
|
|
|
|
meshes.triangle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
0.,
|
|
|
|
BUTTON_CLUSTER_RADIUS,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
2022-10-06 13:33:29 +00:00
|
|
|
parent.spawn(
|
|
|
|
GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::DPadDown,
|
|
|
|
meshes.triangle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
0.,
|
|
|
|
-BUTTON_CLUSTER_RADIUS,
|
|
|
|
)
|
|
|
|
.with_rotation(PI),
|
|
|
|
);
|
|
|
|
parent.spawn(
|
|
|
|
GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::DPadLeft,
|
|
|
|
meshes.triangle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
-BUTTON_CLUSTER_RADIUS,
|
|
|
|
0.,
|
|
|
|
)
|
|
|
|
.with_rotation(PI / 2.),
|
|
|
|
);
|
|
|
|
parent.spawn(
|
|
|
|
GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::DPadRight,
|
|
|
|
meshes.triangle.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
BUTTON_CLUSTER_RADIUS,
|
|
|
|
0.,
|
|
|
|
)
|
|
|
|
.with_rotation(-PI / 2.),
|
|
|
|
);
|
2022-09-24 13:21:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Triggers
|
|
|
|
|
2022-10-06 13:33:29 +00:00
|
|
|
commands.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::LeftTrigger,
|
|
|
|
meshes.trigger.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
-BUTTONS_X,
|
|
|
|
BUTTONS_Y + 115.,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
|
|
|
|
2022-10-06 13:33:29 +00:00
|
|
|
commands.spawn(GamepadButtonBundle::new(
|
|
|
|
GamepadButtonType::RightTrigger,
|
|
|
|
meshes.trigger.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
BUTTONS_X,
|
|
|
|
BUTTONS_Y + 115.,
|
2022-09-24 13:21:01 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_sticks(
|
|
|
|
mut commands: Commands,
|
|
|
|
meshes: Res<ButtonMeshes>,
|
|
|
|
materials: Res<ButtonMaterials>,
|
|
|
|
gamepad_settings: Res<GamepadSettings>,
|
|
|
|
) {
|
2022-10-17 14:38:55 +00:00
|
|
|
let dead_upper =
|
|
|
|
STICK_BOUNDS_SIZE * gamepad_settings.default_axis_settings.deadzone_upperbound();
|
|
|
|
let dead_lower =
|
|
|
|
STICK_BOUNDS_SIZE * gamepad_settings.default_axis_settings.deadzone_lowerbound();
|
2022-09-24 13:21:01 +00:00
|
|
|
let dead_size = dead_lower.abs() + dead_upper.abs();
|
|
|
|
let dead_mid = (dead_lower + dead_upper) / 2.0;
|
|
|
|
|
2022-10-17 14:38:55 +00:00
|
|
|
let live_upper =
|
|
|
|
STICK_BOUNDS_SIZE * gamepad_settings.default_axis_settings.livezone_upperbound();
|
|
|
|
let live_lower =
|
|
|
|
STICK_BOUNDS_SIZE * gamepad_settings.default_axis_settings.livezone_lowerbound();
|
2022-09-24 13:21:01 +00:00
|
|
|
let live_size = live_lower.abs() + live_upper.abs();
|
|
|
|
let live_mid = (live_lower + live_upper) / 2.0;
|
|
|
|
|
|
|
|
let mut spawn_stick = |x_pos, y_pos, x_axis, y_axis, button| {
|
|
|
|
commands
|
|
|
|
.spawn(SpatialBundle {
|
|
|
|
transform: Transform::from_xyz(x_pos, y_pos, 0.),
|
|
|
|
..default()
|
|
|
|
})
|
|
|
|
.with_children(|parent| {
|
|
|
|
// full extent
|
|
|
|
parent.spawn(SpriteBundle {
|
|
|
|
sprite: Sprite {
|
|
|
|
custom_size: Some(Vec2::splat(STICK_BOUNDS_SIZE * 2.)),
|
2024-01-23 06:27:43 +00:00
|
|
|
color: DEAD_COLOR,
|
2022-09-24 13:21:01 +00:00
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
// live zone
|
|
|
|
parent.spawn(SpriteBundle {
|
|
|
|
transform: Transform::from_xyz(live_mid, live_mid, 2.),
|
|
|
|
sprite: Sprite {
|
|
|
|
custom_size: Some(Vec2::new(live_size, live_size)),
|
|
|
|
color: LIVE_COLOR,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
// dead zone
|
|
|
|
parent.spawn(SpriteBundle {
|
|
|
|
transform: Transform::from_xyz(dead_mid, dead_mid, 3.),
|
|
|
|
sprite: Sprite {
|
|
|
|
custom_size: Some(Vec2::new(dead_size, dead_size)),
|
|
|
|
color: DEAD_COLOR,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
// text
|
|
|
|
let style = TextStyle {
|
|
|
|
font_size: 16.,
|
2023-04-21 22:30:18 +00:00
|
|
|
..default()
|
2022-09-24 13:21:01 +00:00
|
|
|
};
|
|
|
|
parent.spawn((
|
|
|
|
Text2dBundle {
|
|
|
|
transform: Transform::from_xyz(0., STICK_BOUNDS_SIZE + 2., 4.),
|
|
|
|
text: Text::from_sections([
|
|
|
|
TextSection {
|
|
|
|
value: format!("{:.3}", 0.),
|
|
|
|
style: style.clone(),
|
|
|
|
},
|
|
|
|
TextSection {
|
|
|
|
value: ", ".to_string(),
|
|
|
|
style: style.clone(),
|
|
|
|
},
|
|
|
|
TextSection {
|
|
|
|
value: format!("{:.3}", 0.),
|
|
|
|
style,
|
|
|
|
},
|
Remove VerticalAlign from TextAlignment (#6807)
# Objective
Remove the `VerticalAlign` enum.
Text's alignment field should only affect the text's internal text alignment, not its position. The only way to control a `TextBundle`'s position and bounds should be through the manipulation of the constraints in the `Style` components of the nodes in the Bevy UI's layout tree.
`Text2dBundle` should have a separate `Anchor` component that sets its position relative to its transform.
Related issues: #676, #1490, #5502, #5513, #5834, #6717, #6724, #6741, #6748
## Changelog
* Changed `TextAlignment` into an enum with `Left`, `Center`, and `Right` variants.
* Removed the `HorizontalAlign` and `VerticalAlign` types.
* Added an `Anchor` component to `Text2dBundle`
* Added `Component` derive to `Anchor`
* Use `f32::INFINITY` instead of `f32::MAX` to represent unbounded text in Text2dBounds
## Migration Guide
The `alignment` field of `Text` now only affects the text's internal alignment.
### Change `TextAlignment` to TextAlignment` which is now an enum. Replace:
* `TextAlignment::TOP_LEFT`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_LEFT` with `TextAlignment::Left`
* `TextAlignment::TOP_CENTER`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_CENTER` with `TextAlignment::Center`
* `TextAlignment::TOP_RIGHT`, `TextAlignment::CENTER_RIGHT`, `TextAlignment::BOTTOM_RIGHT` with `TextAlignment::Right`
### Changes for `Text2dBundle`
`Text2dBundle` has a new field 'text_anchor' that takes an `Anchor` component that controls its position relative to its transform.
2023-01-18 02:19:17 +00:00
|
|
|
]),
|
|
|
|
text_anchor: Anchor::BottomCenter,
|
2022-09-24 13:21:01 +00:00
|
|
|
..default()
|
|
|
|
},
|
|
|
|
TextWithAxes { x_axis, y_axis },
|
|
|
|
));
|
|
|
|
// cursor
|
|
|
|
parent.spawn((
|
|
|
|
MaterialMesh2dBundle {
|
|
|
|
mesh: meshes.circle.clone(),
|
|
|
|
material: materials.normal.clone(),
|
|
|
|
transform: Transform::from_xyz(0., 0., 5.)
|
2024-01-23 06:27:43 +00:00
|
|
|
.with_scale(Vec2::splat(0.15).extend(1.)),
|
2022-09-24 13:21:01 +00:00
|
|
|
..default()
|
|
|
|
},
|
|
|
|
MoveWithAxes {
|
|
|
|
x_axis,
|
|
|
|
y_axis,
|
|
|
|
scale: STICK_BOUNDS_SIZE,
|
|
|
|
},
|
|
|
|
ReactTo(button),
|
|
|
|
));
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
spawn_stick(
|
|
|
|
-STICKS_X,
|
|
|
|
STICKS_Y,
|
|
|
|
GamepadAxisType::LeftStickX,
|
|
|
|
GamepadAxisType::LeftStickY,
|
|
|
|
GamepadButtonType::LeftThumb,
|
|
|
|
);
|
|
|
|
spawn_stick(
|
|
|
|
STICKS_X,
|
|
|
|
STICKS_Y,
|
|
|
|
GamepadAxisType::RightStickX,
|
|
|
|
GamepadAxisType::RightStickY,
|
|
|
|
GamepadButtonType::RightThumb,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup_triggers(
|
|
|
|
mut commands: Commands,
|
|
|
|
meshes: Res<ButtonMeshes>,
|
|
|
|
materials: Res<ButtonMaterials>,
|
|
|
|
) {
|
|
|
|
let mut spawn_trigger = |x, y, button_type| {
|
|
|
|
commands
|
2022-10-06 13:33:29 +00:00
|
|
|
.spawn(GamepadButtonBundle::new(
|
|
|
|
button_type,
|
|
|
|
meshes.trigger.clone(),
|
|
|
|
materials.normal.clone(),
|
|
|
|
x,
|
|
|
|
y,
|
2022-09-24 13:21:01 +00:00
|
|
|
))
|
|
|
|
.with_children(|parent| {
|
|
|
|
parent.spawn((
|
|
|
|
Text2dBundle {
|
|
|
|
transform: Transform::from_xyz(0., 0., 1.),
|
|
|
|
text: Text::from_section(
|
|
|
|
format!("{:.3}", 0.),
|
|
|
|
TextStyle {
|
|
|
|
font_size: 16.,
|
2023-04-21 22:30:18 +00:00
|
|
|
..default()
|
2022-09-24 13:21:01 +00:00
|
|
|
},
|
Remove VerticalAlign from TextAlignment (#6807)
# Objective
Remove the `VerticalAlign` enum.
Text's alignment field should only affect the text's internal text alignment, not its position. The only way to control a `TextBundle`'s position and bounds should be through the manipulation of the constraints in the `Style` components of the nodes in the Bevy UI's layout tree.
`Text2dBundle` should have a separate `Anchor` component that sets its position relative to its transform.
Related issues: #676, #1490, #5502, #5513, #5834, #6717, #6724, #6741, #6748
## Changelog
* Changed `TextAlignment` into an enum with `Left`, `Center`, and `Right` variants.
* Removed the `HorizontalAlign` and `VerticalAlign` types.
* Added an `Anchor` component to `Text2dBundle`
* Added `Component` derive to `Anchor`
* Use `f32::INFINITY` instead of `f32::MAX` to represent unbounded text in Text2dBounds
## Migration Guide
The `alignment` field of `Text` now only affects the text's internal alignment.
### Change `TextAlignment` to TextAlignment` which is now an enum. Replace:
* `TextAlignment::TOP_LEFT`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_LEFT` with `TextAlignment::Left`
* `TextAlignment::TOP_CENTER`, `TextAlignment::CENTER_LEFT`, `TextAlignment::BOTTOM_CENTER` with `TextAlignment::Center`
* `TextAlignment::TOP_RIGHT`, `TextAlignment::CENTER_RIGHT`, `TextAlignment::BOTTOM_RIGHT` with `TextAlignment::Right`
### Changes for `Text2dBundle`
`Text2dBundle` has a new field 'text_anchor' that takes an `Anchor` component that controls its position relative to its transform.
2023-01-18 02:19:17 +00:00
|
|
|
),
|
2022-09-24 13:21:01 +00:00
|
|
|
..default()
|
|
|
|
},
|
|
|
|
TextWithButtonValue(button_type),
|
|
|
|
));
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
spawn_trigger(
|
|
|
|
-BUTTONS_X,
|
|
|
|
BUTTONS_Y + 145.,
|
|
|
|
GamepadButtonType::LeftTrigger2,
|
|
|
|
);
|
|
|
|
spawn_trigger(
|
|
|
|
BUTTONS_X,
|
|
|
|
BUTTONS_Y + 145.,
|
|
|
|
GamepadButtonType::RightTrigger2,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-04-21 22:30:18 +00:00
|
|
|
fn setup_connected(mut commands: Commands) {
|
2024-01-23 06:27:43 +00:00
|
|
|
let text_style = TextStyle {
|
|
|
|
font_size: 20.,
|
2023-04-21 22:30:18 +00:00
|
|
|
..default()
|
2022-09-24 13:21:01 +00:00
|
|
|
};
|
|
|
|
commands.spawn((
|
2024-01-23 06:27:43 +00:00
|
|
|
TextBundle {
|
|
|
|
text: Text::from_sections([
|
|
|
|
TextSection {
|
|
|
|
value: "Connected Gamepads:\n".to_string(),
|
|
|
|
style: text_style.clone(),
|
|
|
|
},
|
|
|
|
TextSection {
|
|
|
|
value: "None".to_string(),
|
|
|
|
style: text_style,
|
|
|
|
},
|
|
|
|
]),
|
|
|
|
style: Style {
|
|
|
|
position_type: PositionType::Absolute,
|
|
|
|
top: Val::Px(12.),
|
|
|
|
left: Val::Px(12.),
|
|
|
|
..default()
|
2022-09-24 13:21:01 +00:00
|
|
|
},
|
2024-01-23 06:27:43 +00:00
|
|
|
..default()
|
|
|
|
},
|
2022-09-24 13:21:01 +00:00
|
|
|
ConnectedGamepadsText,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_buttons(
|
|
|
|
gamepads: Res<Gamepads>,
|
2023-12-06 20:32:34 +00:00
|
|
|
button_inputs: Res<ButtonInput<GamepadButton>>,
|
2022-09-24 13:21:01 +00:00
|
|
|
materials: Res<ButtonMaterials>,
|
|
|
|
mut query: Query<(&mut Handle<ColorMaterial>, &ReactTo)>,
|
|
|
|
) {
|
|
|
|
for gamepad in gamepads.iter() {
|
|
|
|
for (mut handle, react_to) in query.iter_mut() {
|
|
|
|
if button_inputs.just_pressed(GamepadButton::new(gamepad, **react_to)) {
|
|
|
|
*handle = materials.active.clone();
|
|
|
|
}
|
|
|
|
if button_inputs.just_released(GamepadButton::new(gamepad, **react_to)) {
|
|
|
|
*handle = materials.normal.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_button_values(
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
mut events: EventReader<GamepadButtonChangedEvent>,
|
2022-09-24 13:21:01 +00:00
|
|
|
mut query: Query<(&mut Text, &TextWithButtonValue)>,
|
|
|
|
) {
|
2023-08-30 14:20:03 +00:00
|
|
|
for button_event in events.read() {
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
for (mut text, text_with_button_value) in query.iter_mut() {
|
|
|
|
if button_event.button_type == **text_with_button_value {
|
|
|
|
text.sections[0].value = format!("{:.3}", button_event.value);
|
2022-09-24 13:21:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_axes(
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
mut axis_events: EventReader<GamepadAxisChangedEvent>,
|
2022-09-24 13:21:01 +00:00
|
|
|
mut query: Query<(&mut Transform, &MoveWithAxes)>,
|
|
|
|
mut text_query: Query<(&mut Text, &TextWithAxes)>,
|
|
|
|
) {
|
2023-08-30 14:20:03 +00:00
|
|
|
for axis_event in axis_events.read() {
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
let axis_type = axis_event.axis_type;
|
|
|
|
let value = axis_event.value;
|
|
|
|
for (mut transform, move_with) in query.iter_mut() {
|
|
|
|
if axis_type == move_with.x_axis {
|
|
|
|
transform.translation.x = value * move_with.scale;
|
2022-09-24 13:21:01 +00:00
|
|
|
}
|
Gamepad events refactor (#6965)
# Objective
- Remove redundant gamepad events
- Simplify consuming gamepad events.
- Refactor: Separate handling of gamepad events into multiple systems.
## Solution
- Removed `GamepadEventRaw`, and `GamepadEventType`.
- Added bespoke `GamepadConnectionEvent`, `GamepadAxisChangedEvent`, and `GamepadButtonChangedEvent`.
- Refactored `gamepad_event_system`.
- Added `gamepad_button_event_system`, `gamepad_axis_event_system`, and `gamepad_connection_system`, which update the `Input` and `Axis` resources using their corresponding event type.
Gamepad events are now handled in their own systems and have their own types.
This allows for querying for gamepad events without having to match on `GamepadEventType` and makes creating handlers for specific gamepad event types, like a `GamepadConnectionEvent` or `GamepadButtonChangedEvent` possible.
We remove `GamepadEventRaw` by filtering the gamepad events, using `GamepadSettings`, _at the source_, in `bevy_gilrs`. This way we can create `GamepadEvent`s directly and avoid creating `GamepadEventRaw` which do not pass the user defined filters.
We expose ordered `GamepadEvent`s and we can respond to individual gamepad event types.
## Migration Guide
- Replace `GamepadEvent` and `GamepadEventRaw` types with their specific gamepad event type.
2023-01-09 19:24:52 +00:00
|
|
|
if axis_type == move_with.y_axis {
|
|
|
|
transform.translation.y = value * move_with.scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (mut text, text_with_axes) in text_query.iter_mut() {
|
|
|
|
if axis_type == text_with_axes.x_axis {
|
|
|
|
text.sections[0].value = format!("{value:.3}");
|
|
|
|
}
|
|
|
|
if axis_type == text_with_axes.y_axis {
|
|
|
|
text.sections[2].value = format!("{value:.3}");
|
2022-09-24 13:21:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_connected(
|
|
|
|
gamepads: Res<Gamepads>,
|
|
|
|
mut query: Query<&mut Text, With<ConnectedGamepadsText>>,
|
|
|
|
) {
|
|
|
|
if !gamepads.is_changed() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut text = query.single_mut();
|
|
|
|
|
|
|
|
let formatted = gamepads
|
|
|
|
.iter()
|
2022-10-24 14:33:50 +00:00
|
|
|
.map(|g| format!("- {}", gamepads.name(g).unwrap()))
|
2022-09-24 13:21:01 +00:00
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join("\n");
|
|
|
|
|
|
|
|
text.sections[1].value = if !formatted.is_empty() {
|
|
|
|
formatted
|
|
|
|
} else {
|
|
|
|
"None".to_string()
|
|
|
|
}
|
|
|
|
}
|