Skip to content

Jitter when making the camera follow a physics object #211

@umut-sahin

Description

@umut-sahin

Hi, I'm trying to make the camera follow a physics object using this system:

pub fn camera_follow_player(
    mut camera_query: Query<&mut Transform, With<MainCamera>>,
    player_query: Query<&Transform, (With<Player>, Changed<Transform>, Without<MainCamera>)>,
) {
    if let Ok(player_transform) = player_query.get_single() {
        let mut camera_transform = camera_query.single_mut();
        camera_transform.translation.x = player_transform.translation.x;
        camera_transform.translation.y = player_transform.translation.y;
    }
}

which is added to the application using:

.add_systems(PostUpdate, camera_follow_player)

but I end up getting jitters. What's the proper way to solve this?

Here is the issue visualized:
jitter

(it's even worse in the actual game that I'm working on)

And the minimal reproduction code:

use bevy::prelude::*;
use bevy::sprite::MaterialMesh2dBundle;
use bevy_xpbd_2d::{math::*, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .insert_resource(Gravity::ZERO)
        .add_plugins(PhysicsPlugins::default())
        .add_systems(Startup, setup)
        .add_systems(PostUpdate, camera_follow_player)
        .run()
}

#[derive(Component)]
pub struct Player;

#[derive(Component)]
pub struct MainCamera;

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    commands.spawn((MainCamera, Camera2dBundle::default()));
    commands.spawn((
        Player,
        RigidBody::Dynamic,
        Collider::ball(30.00),
        Position(Vector::new(0.00, 0.00)),
        LinearVelocity(Vector::new(80.00, 0.00)),
        MaterialMesh2dBundle {
            mesh: meshes.add(shape::Circle::new(30.00).into()).into(),
            material: materials.add(ColorMaterial::from(Color::BLACK)),
            transform: Transform::from_translation(Vec3::new(0.00, 0.00, 2.00)),
            ..default()
        },
    ));

    const MAP_SIZE: u8 = 50;
    const GRID_SPACING: f32 = 50.00;
    const GRID_WIDTH: f32 = 2.00;

    // Spawn horizontal lines.
    for i in 0..=MAP_SIZE {
        commands.spawn((
            Name::new(format!("Horizontal Line {}", i + 1)),
            SpriteBundle {
                transform: Transform::from_translation(Vec3::new(
                    0.0,
                    (((MAP_SIZE as f32) / 2.0) - (i as f32)) * GRID_SPACING,
                    0.0,
                )),
                sprite: Sprite {
                    color: Color::rgb(0.27, 0.27, 0.27),
                    custom_size: Some(Vec2::new(MAP_SIZE as f32 * GRID_SPACING, GRID_WIDTH)),
                    ..default()
                },
                ..default()
            },
        ));
    }
    // Spawn vertical lines.
    for i in 0..=MAP_SIZE {
        commands.spawn((
            Name::new(format!("Vertical Line {}", i + 1)),
            SpriteBundle {
                transform: Transform::from_translation(Vec3::new(
                    ((i as f32) - ((MAP_SIZE as f32) / 2.0)) * GRID_SPACING,
                    0.0,
                    0.0,
                )),
                sprite: Sprite {
                    color: Color::rgb(0.27, 0.27, 0.27),
                    custom_size: Some(Vec2::new(GRID_WIDTH, MAP_SIZE as f32 * GRID_SPACING)),
                    ..default()
                },
                ..default()
            },
        ));
    }
}

pub fn camera_follow_player(
    mut camera_query: Query<&mut Transform, With<MainCamera>>,
    player_query: Query<&Transform, (With<Player>, Changed<Transform>, Without<MainCamera>)>,
) {
    if let Ok(player_transform) = player_query.get_single() {
        let mut camera_transform = camera_query.single_mut();
        camera_transform.translation.x = player_transform.translation.x;
        camera_transform.translation.y = player_transform.translation.y;
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions