-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Description
Bevy version
main 7f658ca
What you did
Apply any UiScale or window scale factor override over 1, then move a UI element smoothly over the screen.
What went wrong
Here is an example of UiScale being 20 and then moving the white square smoothly across the screen. It jumps by 20 pixel increments.
The blue square is there as a comparison to show what smooth movement looks like. GlobalTransform was modified directly to achieve that.
ui_scale.mp4
I would expect the white square position to be rounded after applying UiScale. E.g. Px(0.47) would first be scaled to Px(0.47*20) = Px(9.4), then rounded to Px(9). This would effectively round logical UI pixels to physical screen pixels, which sounds more correct to me than the current behavior.
The jittery movement is especially bad when trying to position scrolling lists or virtual cursors that need to look smooth.
Additional information
Example code used in the video.
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}
#[derive(Component)]
struct Node1;
#[derive(Component)]
struct Node2;
#[derive(Resource)]
struct NodeAnimation((f32, f32));
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
NodeBundle {
background_color: Color::WHITE.into(),
style: Style {
width: Val::Px(6.0),
height: Val::Px(6.0),
position_type: PositionType::Absolute,
..default()
},
..default()
},
Node1,
));
commands.spawn((
NodeBundle {
background_color: Srgba::BLUE.into(),
style: Style {
width: Val::Px(6.0),
height: Val::Px(6.0),
top: Val::Px(6.0),
position_type: PositionType::Absolute,
..default()
},
..default()
},
Node2,
));
commands.insert_resource(UiScale(20.0));
commands.insert_resource(NodeAnimation((0.0, 1.0)));
}
fn cubic_ease_in_out(factor: f32) -> f32 {
if factor < 0.5 {
4. * factor * factor * factor
} else {
1. - (-2. * factor + 2.).powf(3.) / 2.
}
}
fn position(factor: f32) -> f32 {
cubic_ease_in_out(factor) * 30.
}
fn update(
mut node1_q: Query<&mut Style, With<Node1>>,
mut node2_q: Query<&mut GlobalTransform, With<Node2>>,
mut node_animation: ResMut<NodeAnimation>,
time: Res<Time>,
) {
let (factor, direction) = &mut node_animation.0;
*factor += time.delta_seconds() * *direction * 0.3;
if *factor > 1. {
*direction = -1.;
} else if *factor < 0. {
*direction = 1.;
}
for mut style in node1_q.iter_mut() {
style.left = Val::Px(position(*factor));
}
for mut t in node2_q.iter_mut() {
let mut transform = t.compute_transform();
transform.translation.x = position(*factor) + 3.;
*t = transform.into();
}
}