bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Non-painful sprite hierarchy flipping

Open FraserLee opened this issue 2 years ago • 4 comments

What problem does this solve or what need does it fill?

I've have character created out of a hierarchy of sprites, to allow me to procedurally animate by moving individual pieces.

I want some way to flip the whole character, left to right.

What solution would you like?

I'd love to just negate the x-scale on the transform, which would require disabling back-face culling on sprites, and possibly some work to get negative transform scales working correctly.

What alternative(s) have you considered?

Because of the hierarchy approach, just toggling flip_x on every sub-sprite isn't enough (they stay in place relative to their own transforms), and rotating the character by pi on the y-axis means all the tiny z-offsets I have to set the sub-sprite rendering order are reversed.

There's always the long-way workaround of just flipping each sub-sprite and recalculating position, but that's not great.

FraserLee avatar Jun 04 '22 23:06 FraserLee

I'd love to just negate the x-scale on the transform

If we do this, could we just remove the flip_x and flip_y fields on Sprite?

alice-i-cecile avatar Jun 04 '22 23:06 alice-i-cecile

I took a look at implementing this only to find out that sprite back-culling was already disabled on this PR, so this should already work.

I've made a simple example doing what I think @FraserLee is talking about that works as I would expect on main.

//! Displays two [`Sprite`]s with a parent child relationship, and the effect
//! of negating the scale of the parent.

use bevy::prelude::*;

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

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn_bundle(Camera2dBundle::default());
    let child_logo = commands.spawn_bundle(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        sprite: Sprite {
            ..default()
        },
        transform: Transform::from_xyz(100.0, 0.0, 1.0)
            .with_scale(Vec3::new(0.75, 0.75, 1.0)),
        ..default()
    })
    .id();

    commands.spawn_bundle(SpriteBundle {
        texture: asset_server.load("branding/icon.png"),
        sprite: Sprite {
            ..default()
        },
        transform: Transform::from_scale(Vec3::new(-0.75, 0.75, 1.0)),
        ..default()
    })
    .add_child(child_logo);
}

I'm happy to put up a PR to remove flip_x and flip_y from Sprites if people are happy with that idea, but they were added after the removal of culling, so I'm curious as to whether there are some use cases I'm not thinking of. It does seem like using scale as the standard way to flip sprites would simplify the interface and make things more consistent in addition to behaving better in hierarchies.

edwardvear avatar Jun 21 '22 03:06 edwardvear

I think we should open a draft PR for those changes, and then pull in the rendering experts for a final review pass.

alice-i-cecile avatar Jun 21 '22 15:06 alice-i-cecile

I think this could be quite straightforward to do with a command for mirror_recursive. I'm going to add the Good-First-Issue tag to this; it's not trivial but should be fun to tackle.

alice-i-cecile avatar Jul 30 '22 18:07 alice-i-cecile

Throwing my 0.02 in here since this seems most related, but I think flipping could be further improved if it used the sprite Anchor as its axis. I have a 32px wide sprite that is asymmetric, with the body taking up the right half and a tail taking up the left half. When I've moved the anchor to center on the body, simply flipping it down the center causes the body to "jump" significantly whenever the sprite changes direction. This also moves the body a good distance outside of the collider's position.

Additionally, Anchor::Custom feels unintuitive to use. The coordinate system runs from -0.5 to 0.5 on each axis no matter the sprite's size, so if I want to move e.g. a 32px sprite's anchor 2px to the right I have to say (1.0 / 32.0) * 2.0. I don't know why or how Bevy's renderer needs a coordinate system like that under the hood, but I think it would be better if it were abstracted away and accepted pixels instead.

zikes avatar Jun 05 '23 17:06 zikes