Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,251 @@
//! A freecam-style camera controller plugin.
//! To use in your own application:
//! - Copy the code for the [`CameraControllerPlugin`] and add the plugin to your App.
//! - Attach the [`CameraController`] component to an entity with a [`Camera3d`].
//!
//! Unlike other examples, which demonstrate an application, this demonstrates a plugin library.
use bevy::{
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseScrollUnit},
prelude::*,
window::CursorGrabMode,
};
use std::{f32::consts::*, fmt};
/// A freecam-style camera controller plugin.
pub struct CameraControllerPlugin;
impl Plugin for CameraControllerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, run_camera_controller);
}
}
/// Based on Valorant's default sensitivity, not entirely sure why it is exactly 1.0 / 180.0,
/// but I'm guessing it is a misunderstanding between degrees/radians and then sticking with
/// it because it felt nice.
pub const RADIANS_PER_DOT: f32 = 1.0 / 180.0;
/// Camera controller [`Component`].
#[derive(Component)]
pub struct CameraController {
/// Enables this [`CameraController`] when `true`.
pub enabled: bool,
/// Indicates if this controller has been initialized by the [`CameraControllerPlugin`].
pub initialized: bool,
/// Multiplier for pitch and yaw rotation speed.
pub sensitivity: f32,
/// [`KeyCode`] for forward translation.
pub key_forward: KeyCode,
/// [`KeyCode`] for backward translation.
pub key_back: KeyCode,
/// [`KeyCode`] for left translation.
pub key_left: KeyCode,
/// [`KeyCode`] for right translation.
pub key_right: KeyCode,
/// [`KeyCode`] for up translation.
pub key_up: KeyCode,
/// [`KeyCode`] for down translation.
pub key_down: KeyCode,
/// [`KeyCode`] to use [`run_speed`](CameraController::run_speed) instead of
/// [`walk_speed`](CameraController::walk_speed) for translation.
pub key_run: KeyCode,
/// [`MouseButton`] for grabbing the mouse focus.
pub mouse_key_cursor_grab: MouseButton,
/// [`KeyCode`] for grabbing the keyboard focus.
pub keyboard_key_toggle_cursor_grab: KeyCode,
/// Multiplier for unmodified translation speed.
pub walk_speed: f32,
/// Multiplier for running translation speed.
pub run_speed: f32,
/// Multiplier for how the mouse scroll wheel modifies [`walk_speed`](CameraController::walk_speed)
/// and [`run_speed`](CameraController::run_speed).
pub scroll_factor: f32,
/// Friction factor used to exponentially decay [`velocity`](CameraController::velocity) over time.
pub friction: f32,
/// This [`CameraController`]'s pitch rotation.
pub pitch: f32,
/// This [`CameraController`]'s yaw rotation.
pub yaw: f32,
/// This [`CameraController`]'s translation velocity.
pub velocity: Vec3,
}
impl Default for CameraController {
fn default() -> Self {
Self {
enabled: true,
initialized: false,
sensitivity: 1.0,
key_forward: KeyCode::KeyW,
key_back: KeyCode::KeyS,
key_left: KeyCode::KeyA,
key_right: KeyCode::KeyD,
key_up: KeyCode::KeyE,
key_down: KeyCode::KeyQ,
key_run: KeyCode::ShiftLeft,
mouse_key_cursor_grab: MouseButton::Left,
keyboard_key_toggle_cursor_grab: KeyCode::KeyM,
walk_speed: 5.0,
run_speed: 15.0,
scroll_factor: 0.1,
friction: 0.5,
pitch: 0.0,
yaw: 0.0,
velocity: Vec3::ZERO,
}
}
}
impl fmt::Display for CameraController {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"
Freecam Controls:
Mouse\t- Move camera orientation
Scroll\t- Adjust movement speed
{:?}\t- Hold to grab cursor
{:?}\t- Toggle cursor grab
{:?} & {:?}\t- Fly forward & backwards
{:?} & {:?}\t- Fly sideways left & right
{:?} & {:?}\t- Fly up & down
{:?}\t- Fly faster while held",
self.mouse_key_cursor_grab,
self.keyboard_key_toggle_cursor_grab,
self.key_forward,
self.key_back,
self.key_left,
self.key_right,
self.key_up,
self.key_down,
self.key_run,
)
}
}
fn run_camera_controller(
time: Res<Time>,
mut windows: Query<&mut Window>,
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
mouse_button_input: Res<ButtonInput<MouseButton>>,
key_input: Res<ButtonInput<KeyCode>>,
mut toggle_cursor_grab: Local<bool>,
mut mouse_cursor_grab: Local<bool>,
mut query: Query<(&mut Transform, &mut CameraController), With<Camera>>,
) {
let dt = time.delta_secs();
let Ok((mut transform, mut controller)) = query.single_mut() else {
return;
};
if !controller.initialized {
let (yaw, pitch, _roll) = transform.rotation.to_euler(EulerRot::YXZ);
controller.yaw = yaw;
controller.pitch = pitch;
controller.initialized = true;
info!("{}", *controller);
}
if !controller.enabled {
return;
}
let mut scroll = 0.0;
let amount = match accumulated_mouse_scroll.unit {
MouseScrollUnit::Line => accumulated_mouse_scroll.delta.y,
MouseScrollUnit::Pixel => accumulated_mouse_scroll.delta.y / 16.0,
};
scroll += amount;
controller.walk_speed += scroll * controller.scroll_factor * controller.walk_speed;
controller.run_speed = controller.walk_speed * 3.0;
// Handle key input
let mut axis_input = Vec3::ZERO;
if key_input.pressed(controller.key_forward) {
axis_input.z += 1.0;
}
if key_input.pressed(controller.key_back) {
axis_input.z -= 1.0;
}
if key_input.pressed(controller.key_right) {
axis_input.x += 1.0;
}
if key_input.pressed(controller.key_left) {
axis_input.x -= 1.0;
}
if key_input.pressed(controller.key_up) {
axis_input.y += 1.0;
}
if key_input.pressed(controller.key_down) {
axis_input.y -= 1.0;
}
let mut cursor_grab_change = false;
if key_input.just_pressed(controller.keyboard_key_toggle_cursor_grab) {
*toggle_cursor_grab = !*toggle_cursor_grab;
cursor_grab_change = true;
}
if mouse_button_input.just_pressed(controller.mouse_key_cursor_grab) {
*mouse_cursor_grab = true;
cursor_grab_change = true;
}
if mouse_button_input.just_released(controller.mouse_key_cursor_grab) {
*mouse_cursor_grab = false;
cursor_grab_change = true;
}
let cursor_grab = *mouse_cursor_grab || *toggle_cursor_grab;
// Apply movement update
if axis_input != Vec3::ZERO {
let max_speed = if key_input.pressed(controller.key_run) {
controller.run_speed
} else {
controller.walk_speed
};
controller.velocity = axis_input.normalize() * max_speed;
} else {
let friction = controller.friction.clamp(0.0, 1.0);
controller.velocity *= 1.0 - friction;
if controller.velocity.length_squared() < 1e-6 {
controller.velocity = Vec3::ZERO;
}
}
let forward = *transform.forward();
let right = *transform.right();
transform.translation += controller.velocity.x * dt * right
+ controller.velocity.y * dt * Vec3::Y
+ controller.velocity.z * dt * forward;
// Handle cursor grab
if cursor_grab_change {
if cursor_grab {
for mut window in &mut windows {
if !window.focused {
continue;
}
window.cursor_options.grab_mode = CursorGrabMode::Locked;
window.cursor_options.visible = false;
}
} else {
for mut window in &mut windows {
window.cursor_options.grab_mode = CursorGrabMode::None;
window.cursor_options.visible = true;
}
}
}
// Handle mouse input
if accumulated_mouse_motion.delta != Vec2::ZERO && cursor_grab {
// Apply look update
controller.pitch = (controller.pitch
- accumulated_mouse_motion.delta.y * RADIANS_PER_DOT * controller.sensitivity)
.clamp(-PI / 2., PI / 2.);
controller.yaw -=
accumulated_mouse_motion.delta.x * RADIANS_PER_DOT * controller.sensitivity;
transform.rotation = Quat::from_euler(EulerRot::ZYX, 0.0, controller.yaw, controller.pitch);
}
}

195
vendor/bevy/examples/helpers/widgets.rs vendored Normal file
View File

@@ -0,0 +1,195 @@
//! Simple widgets for example UI.
//!
//! Unlike other examples, which demonstrate an application, this demonstrates a plugin library.
use bevy::{ecs::system::EntityCommands, prelude::*};
/// An event that's sent whenever the user changes one of the settings by
/// clicking a radio button.
#[derive(Clone, Event, Deref, DerefMut)]
pub struct WidgetClickEvent<T>(T);
/// A marker component that we place on all widgets that send
/// [`WidgetClickEvent`]s of the given type.
#[derive(Clone, Component, Deref, DerefMut)]
pub struct WidgetClickSender<T>(T)
where
T: Clone + Send + Sync + 'static;
/// A marker component that we place on all radio `Button`s.
#[derive(Clone, Copy, Component)]
pub struct RadioButton;
/// A marker component that we place on all `Text` inside radio buttons.
#[derive(Clone, Copy, Component)]
pub struct RadioButtonText;
/// The size of the border that surrounds buttons.
pub const BUTTON_BORDER: UiRect = UiRect::all(Val::Px(1.0));
/// The color of the border that surrounds buttons.
pub const BUTTON_BORDER_COLOR: BorderColor = BorderColor(Color::WHITE);
/// The amount of rounding to apply to button corners.
pub const BUTTON_BORDER_RADIUS_SIZE: Val = Val::Px(6.0);
/// The amount of space between the edge of the button and its label.
pub const BUTTON_PADDING: UiRect = UiRect::axes(Val::Px(12.0), Val::Px(6.0));
/// Returns a [`Node`] appropriate for the outer main UI node.
///
/// This UI is in the bottom left corner and has flex column support
pub fn main_ui_node() -> Node {
Node {
flex_direction: FlexDirection::Column,
position_type: PositionType::Absolute,
row_gap: Val::Px(6.0),
left: Val::Px(10.0),
bottom: Val::Px(10.0),
..default()
}
}
/// Spawns a single radio button that allows configuration of a setting.
///
/// The type parameter specifies the value that will be packaged up and sent in
/// a [`WidgetClickEvent`] when the radio button is clicked.
pub fn spawn_option_button<T>(
parent: &mut ChildSpawnerCommands,
option_value: T,
option_name: &str,
is_selected: bool,
is_first: bool,
is_last: bool,
) where
T: Clone + Send + Sync + 'static,
{
let (bg_color, fg_color) = if is_selected {
(Color::WHITE, Color::BLACK)
} else {
(Color::BLACK, Color::WHITE)
};
// Add the button node.
parent
.spawn((
Button,
Node {
border: BUTTON_BORDER.with_left(if is_first { Val::Px(1.0) } else { Val::Px(0.0) }),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
padding: BUTTON_PADDING,
..default()
},
BUTTON_BORDER_COLOR,
BorderRadius::ZERO
.with_left(if is_first {
BUTTON_BORDER_RADIUS_SIZE
} else {
Val::Px(0.0)
})
.with_right(if is_last {
BUTTON_BORDER_RADIUS_SIZE
} else {
Val::Px(0.0)
}),
BackgroundColor(bg_color),
))
.insert(RadioButton)
.insert(WidgetClickSender(option_value.clone()))
.with_children(|parent| {
spawn_ui_text(parent, option_name, fg_color)
.insert(RadioButtonText)
.insert(WidgetClickSender(option_value));
});
}
/// Spawns the buttons that allow configuration of a setting.
///
/// The user may change the setting to any one of the labeled `options`. The
/// value of the given type parameter will be packaged up and sent as a
/// [`WidgetClickEvent`] when one of the radio buttons is clicked.
pub fn spawn_option_buttons<T>(
parent: &mut ChildSpawnerCommands,
title: &str,
options: &[(T, &str)],
) where
T: Clone + Send + Sync + 'static,
{
// Add the parent node for the row.
parent
.spawn(Node {
align_items: AlignItems::Center,
..default()
})
.with_children(|parent| {
spawn_ui_text(parent, title, Color::BLACK).insert(Node {
width: Val::Px(125.0),
..default()
});
for (option_index, (option_value, option_name)) in options.iter().cloned().enumerate() {
spawn_option_button(
parent,
option_value,
option_name,
option_index == 0,
option_index == 0,
option_index == options.len() - 1,
);
}
});
}
/// Spawns text for the UI.
///
/// Returns the `EntityCommands`, which allow further customization of the text
/// style.
pub fn spawn_ui_text<'a>(
parent: &'a mut ChildSpawnerCommands,
label: &str,
color: Color,
) -> EntityCommands<'a> {
parent.spawn((
Text::new(label),
TextFont {
font_size: 18.0,
..default()
},
TextColor(color),
))
}
/// Checks for clicks on the radio buttons and sends `RadioButtonChangeEvent`s
/// as necessary.
pub fn handle_ui_interactions<T>(
mut interactions: Query<
(&Interaction, &WidgetClickSender<T>),
(With<Button>, With<RadioButton>),
>,
mut widget_click_events: EventWriter<WidgetClickEvent<T>>,
) where
T: Clone + Send + Sync + 'static,
{
for (interaction, click_event) in interactions.iter_mut() {
if *interaction == Interaction::Pressed {
widget_click_events.write(WidgetClickEvent((**click_event).clone()));
}
}
}
/// Updates the style of the button part of a radio button to reflect its
/// selected status.
pub fn update_ui_radio_button(background_color: &mut BackgroundColor, selected: bool) {
background_color.0 = if selected { Color::WHITE } else { Color::BLACK };
}
/// Updates the color of the label of a radio button to reflect its selected
/// status.
pub fn update_ui_radio_button_text(entity: Entity, writer: &mut TextUiWriter, selected: bool) {
let text_color = if selected { Color::BLACK } else { Color::WHITE };
writer.for_each_color(entity, |mut color| {
color.0 = text_color;
});
}