Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: incorrect sprite size for aabb when sprite has rect and no custom_size #12738

Conversation

MarcoMeijer
Copy link
Contributor

Objective

Fixes #12736

Solution

Use sprite rect to calculate sprite size for aabb when custom_size is None

@ItsDoot ItsDoot added C-Bug An unexpected or incorrect behavior A-Rendering Drawing game state to the screen labels Mar 26, 2024
@mgi388
Copy link
Contributor

mgi388 commented Mar 26, 2024

Out of interest is there any way to write or update a unit test to prove the existing bug and that this change fixes it?

@MarcoMeijer
Copy link
Contributor Author

Sure I will add a unit test to it

@MarcoMeijer MarcoMeijer changed the title fix: using sprite rect to calculate sprite size for aabb when custom_size is None fix: incorrect sprite size for aabb when sprite has rect and no custom_size Mar 28, 2024
@mgi388
Copy link
Contributor

mgi388 commented Apr 12, 2024

Here's some testing of this PR which I think shows that it fixes the issue as expected. I think it's worth someone eye-balling the results to check that they match their expectations, but they seem to AFAICT.

In the original issue #12736 the reporter mentions anchor being "outside of the range -0.5..0.5", but per the results below the bug appears to exist with standard anchors (also, FWIW, does an anchor outside that coordinate system have undefined behavior or are they also valid?)

Examples below run using cargo run --example sprite where I replaced the built-in sprite.rs example with the code for each test:

Note

Image is 256x256

With Bevy 5c3ae32 (more or less main due to #12935)

Only setting an anchor

Code

//! Modified sprite.ts.

use bevy::{color::palettes::tailwind::AMBER_400, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, show_bounding_boxes))
        .add_systems(Update, (show_transform_gizmo, update_camera))
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/bevy_bird_dark.png"),
        sprite: Sprite {
            anchor: bevy::sprite::Anchor::TopLeft,
            ..default()
        },
        ..default()
    });
}

fn show_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
    config.config_mut::<AabbGizmoConfigGroup>().1.draw_all = true;
}

fn show_transform_gizmo(mut gizmos: Gizmos, query: Query<&Transform, With<Sprite>>) {
    for transform in query.iter() {
        gizmos.sphere(transform.translation, Quat::IDENTITY, 3.5, AMBER_400);
    }
}

fn update_camera(
    keyboard: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut listeners: Query<&mut Transform, With<Camera>>,
) {
    let mut transform = listeners.single_mut();

    let speed = 200.;

    if keyboard.pressed(KeyCode::ArrowRight) {
        transform.translation.x += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowLeft) {
        transform.translation.x -= speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowUp) {
        transform.translation.y += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowDown) {
        transform.translation.y -= speed * time.delta_seconds();
    }
}

✅ Result

image

Only setting a rect

Code

//! Modified sprite.ts.

use bevy::{color::palettes::tailwind::AMBER_400, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, show_bounding_boxes))
        .add_systems(Update, (show_transform_gizmo, update_camera))
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/bevy_bird_dark.png"),
        sprite: Sprite {
            rect: Some(Rect::new(0.0, 0.0, 150., 150.)),
            ..default()
        },
        ..default()
    });
}

fn show_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
    config.config_mut::<AabbGizmoConfigGroup>().1.draw_all = true;
}

fn show_transform_gizmo(mut gizmos: Gizmos, query: Query<&Transform, With<Sprite>>) {
    for transform in query.iter() {
        gizmos.sphere(transform.translation, Quat::IDENTITY, 3.5, AMBER_400);
    }
}

fn update_camera(
    keyboard: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut listeners: Query<&mut Transform, With<Camera>>,
) {
    let mut transform = listeners.single_mut();

    let speed = 200.;

    if keyboard.pressed(KeyCode::ArrowRight) {
        transform.translation.x += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowLeft) {
        transform.translation.x -= speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowUp) {
        transform.translation.y += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowDown) {
        transform.translation.y -= speed * time.delta_seconds();
    }
}

❌ Result

image

Anchor and rect

Code

//! Modified sprite.ts.

use bevy::{color::palettes::tailwind::AMBER_400, prelude::*};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, show_bounding_boxes))
        .add_systems(Update, (show_transform_gizmo, update_camera))
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/bevy_bird_dark.png"),
        sprite: Sprite {
            anchor: bevy::sprite::Anchor::TopLeft,
            rect: Some(Rect::new(0.0, 0.0, 150., 150.)),
            ..default()
        },
        ..default()
    });
}

fn show_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
    config.config_mut::<AabbGizmoConfigGroup>().1.draw_all = true;
}

fn show_transform_gizmo(mut gizmos: Gizmos, query: Query<&Transform, With<Sprite>>) {
    for transform in query.iter() {
        gizmos.sphere(transform.translation, Quat::IDENTITY, 3.5, AMBER_400);
    }
}

fn update_camera(
    keyboard: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut listeners: Query<&mut Transform, With<Camera>>,
) {
    let mut transform = listeners.single_mut();

    let speed = 200.;

    if keyboard.pressed(KeyCode::ArrowRight) {
        transform.translation.x += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowLeft) {
        transform.translation.x -= speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowUp) {
        transform.translation.y += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowDown) {
        transform.translation.y -= speed * time.delta_seconds();
    }
}

❌ Result

image

With the changes from this PR

Only setting an anchor

✅ Result

image

Only setting a rect

✅ Result

image

Anchor and rect

✅ Result

image

Anchor outside of the range -0.5..0.5 and rect (to prove fix for original issue)

Code

//! Modified sprite.ts.

use bevy::{color::palettes::tailwind::AMBER_400, prelude::*, sprite::Anchor};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, show_bounding_boxes))
        .add_systems(Update, (show_transform_gizmo, update_camera))
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(SpriteBundle {
        texture: asset_server.load("branding/bevy_bird_dark.png"),
        sprite: Sprite {
            anchor: Anchor::Custom(Vec2::new(1., 1.)),
            rect: Some(Rect::new(0.0, 0.0, 150., 150.)),
            ..default()
        },
        ..default()
    });
}

fn show_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
    config.config_mut::<AabbGizmoConfigGroup>().1.draw_all = true;
}

fn show_transform_gizmo(mut gizmos: Gizmos, query: Query<&Transform, With<Sprite>>) {
    for transform in query.iter() {
        gizmos.sphere(transform.translation, Quat::IDENTITY, 3.5, AMBER_400);
    }
}

fn update_camera(
    keyboard: Res<ButtonInput<KeyCode>>,
    time: Res<Time>,
    mut listeners: Query<&mut Transform, With<Camera>>,
) {
    let mut transform = listeners.single_mut();

    let speed = 200.;

    if keyboard.pressed(KeyCode::ArrowRight) {
        transform.translation.x += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowLeft) {
        transform.translation.x -= speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowUp) {
        transform.translation.y += speed * time.delta_seconds();
    }
    if keyboard.pressed(KeyCode::ArrowDown) {
        transform.translation.y -= speed * time.delta_seconds();
    }
}

✅ Result

image

@MarcoMeijer
Copy link
Contributor Author

@mgi388 I mentioned the range -0.5..0.5 because when it is outside that range there will be an area of the sprite that is outside of the aabb box. See this picture from running your last example on bevy 0.13.2.
Screenshot from 2024-04-12 08-26-16
Which is causing the bug I was having of the sprite not being rendered.

When the origin is in the range -0.5..0.5 the aabb box is just bigger then it should be, so it is being rendered when it doesn't have to. Thats probably why it didn't get noticed before since most people have the origin within the sprite.

@mgi388
Copy link
Contributor

mgi388 commented Apr 12, 2024

@MarcoMeijer makes sense. I probably should have added the "outside the range" test against main as well. I just ran it, and it looks the same as 0.13.2 as you've just shared, so I won't bother updating my test results.

Anyway, given my test results above, do all the results when the changes in your PR is applied look expected and correct to you?

@MarcoMeijer
Copy link
Contributor Author

@mgi388 yep they look good to me

Copy link
Contributor

@mgi388 mgi388 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me, I've tested it too, so giving an approval.

@mgi388
Copy link
Contributor

mgi388 commented Apr 16, 2024

@alice-i-cecile would you be able to help direct this to the right reviewers? I picked this out and reviewed it myself because I was debugging anchor in my own thing, and I'd like to help the OP get it reviewed/merged if it's good to go.

@alice-i-cecile
Copy link
Member

@bugsweeper @lennart would the two of you be able to review this based on your work in #12769? <3 I'll take a look too.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good little fix, and a nice regression test to match.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Apr 16, 2024
@bugsweeper
Copy link
Contributor

bugsweeper commented Apr 16, 2024

I think this problem weakly corelates to my work because my changes fixed problem based on sprite subdivision. But I confirm that bounds of sprite slices similary calculated using texture size, sprite.rect.size or custom_size, but in reverse order (because of using reassignment) for same priorities

@alice-i-cecile alice-i-cecile added this pull request to the merge queue Apr 16, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Apr 16, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Apr 16, 2024
Merged via the queue into bevyengine:main with commit 7e9f632 Apr 16, 2024
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Sprite with rect and custom anchor doesn't render when it should
5 participants