Search code examples
camerarenderingbevy

how to zoom in to a sprite in bevy


I wanted to implement zooming in my 2d bevy game. After some code browsing I found out that Camera2dBundle uses OrthographicProjection by default and can not zoom in as required.

I tried using Camera3dBundle which does define projection: PerspectiveProjection by default but my sprite seems to disappear from the scene.

Could you give me some pointers to what I'm doing wrong? I have included some test code below.

Thanks

use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .add_system(zoom_in)
        .run();
}

fn setup(
    mut commands: Commands
    ) {
        commands.spawn_bundle(Camera3dBundle {
            transform: Transform::from_xyz(0., 0., 1000.).looking_at(Vec3::ZERO, Vec3::Z),
            ..Default::default()
        });
        
        commands.spawn_bundle(SpriteBundle {
           sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
           ..Default::default()
        });
}

pub fn zoom_in(mut query: Query<&mut Transform, With<Camera>>, time: Res<Time>) {
    for mut transform in query.iter_mut() {
        transform.translation.z -= 100. * time.delta_seconds();

        warn!("{}", transform.translation.z);
    }
}

Solution

  • You do not see the sprite, because you apparently look at it from the wrong side. If you have a 2D scene I would advise you to stick to the Camera2DBundle.

    Contrary to what you stated in your question, in order to zoom you can set the scale of OrthographicProjection like so:

    use bevy::prelude::*;
    
    fn main() {
        App::new()
            .add_plugins(DefaultPlugins)
            .add_startup_system(setup)
            .add_system(zoom_in)
            .run();
    }
    
    fn setup(
        mut commands: Commands
        ) {
            commands.spawn_bundle(Camera2dBundle::default());
            commands.spawn_bundle(SpriteBundle {
               sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
               ..Default::default()
            });
    }
    
    pub fn zoom_in(mut query: Query<&mut OrthographicProjection, With<Camera>>, time: Res<Time>) {
        for mut projection in query.iter_mut() {
            projection.scale -= 0.1 * time.delta_seconds();
    
            println!("Current zoom scale: {}", projection.scale);
        }
    }
    

    Note that you might want to implement logarithmic zoom, so that your zoom does "feel" linear and does not speed up approaching infinity when the scale approaches zero.

    Here is a sample using logarithmic zoom:

    use bevy::prelude::*;
    
    fn main() {
        App::new()
            .add_plugins(DefaultPlugins)
            .add_startup_system(setup)
            .add_system(zoom_in)
            .run();
    }
    
    fn setup(
        mut commands: Commands
        ) {
            commands.spawn_bundle(Camera2dBundle::default());
            commands.spawn_bundle(SpriteBundle {
               sprite: Sprite { custom_size: Some(Vec2 { x: 50., y: 50. }), ..Default::default()},
               ..Default::default()
            });
    }
    
    pub fn zoom_in(mut query: Query<&mut OrthographicProjection, With<Camera>>, time: Res<Time>) {
        for mut projection in query.iter_mut() {
            let mut log_scale = projection.scale.ln();
            log_scale -= 0.1 * time.delta_seconds();
            projection.scale = log_scale.exp();
    
            println!("Current zoom scale: {}", projection.scale);
        }
    }