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

Hierarchy Child Global Transforms are Not Updated When Spawned as a Child #1354

Closed
zicklag opened this issue Jan 30, 2021 · 7 comments
Closed
Labels
A-Hierarchy Parent-child entity hierarchies A-Transform Translations, rotations and scales C-Usability A targeted quality-of-life change that makes Bevy easier to use S-User-Error This issue was caused by a mistake in the user's approach

Comments

@zicklag
Copy link
Member

zicklag commented Jan 30, 2021

Bevy version

af67231

Operating system & version

Pop!_OS ( Ubuntu ) 20.04

What you did

Here is a complete, simple demonstration program. We spawn a parent sprite in the setup system, then in the load_child system, we spawn a child sprite with a (-50, -50) offset translation. We also have the move_parent system, which just moves the parent sprite.

use bevy::prelude::*;

fn main() {
    App::build()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup.system())
        // Comment out the move_parent line below to witness the bugginess 😜
        .add_system(move_parent.system())
        .add_system(load_child.system())
        .run();
}

struct TheParentEnt;
struct Loaded;

fn setup(
    commands: &mut Commands,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    // Spawn camera
    commands.spawn(Camera2dBundle::default());

    // Load parent sprite
    let texture = asset_server.load("textures/rpg/mobs/boss_bee.png");
    // Spawn parent
    commands
        .spawn(SpriteBundle {
            material: materials.add(ColorMaterial::texture(texture)),
            ..Default::default()
        })
        .with(TheParentEnt);
}

// Move the parent slowly to the left
fn move_parent(mut query: Query<&mut Transform, With<TheParentEnt>>) {
    for mut trans in query.iter_mut() {
        trans.translation.x -= 0.1;
    }
}

// Load the child sprite
fn load_child(
    commands: &mut Commands,
    query: Query<Entity, (Without<Loaded>, With<TheParentEnt>)>,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for ent in query.iter() {
        // Spawn the child sprite
        let texture = asset_server.load("textures/rpg/chars/hat-guy/hat-guy.png");
        commands
            .spawn(SpriteBundle {
                material: materials.add(ColorMaterial::texture(texture)),
                transform: Transform::from_translation(Vec3::new(-50., -50., 0.)),
                ..Default::default()
            })
            .with(Parent(ent));

        // Prevent processing parent sprite again by marking it loaded
        commands.insert_one(ent, Loaded);
    }
}

What you expected to happen

When running the example below without changes, everything goes as expected, you have the child sprite moving along with the parent, but offset to the left and down from the parent sprite.

When commenting out the move_parent system, the only difference should be that the sprites aren't moving. The child sprite should still be offset to the left and down from the parent sprite.

What actually happened

When you comment out the move_parent system, when the child sprite is spawned, instead of being offset from the parent, it actually ends up in the center of the world.

Additional information

It seems that the GlobalTransform of the child sprite is not updated when it is spawned as a child, until the parent transform is actually changed. Thus, when the move_parent system is active, the child's transform is properly updated, but if the parent doesn't move, then the child's GlobalTransform is never updated and it remains in the center of the universe!

@zicklag
Copy link
Member Author

zicklag commented Jan 30, 2021

I verified that it is an update issue by modifying the transform propagation system to always consider a transform component as having "changed" and it fixes the issue. Now I just have to figure out why the added child is not being considdered as a Changed<Transform> or a Changed<Parent>.

@Davier
Copy link
Contributor

Davier commented Jan 30, 2021

I think the issue comes from adding the Parent component directly. The system that automatically adds the corresponding Children component probably runs after the system that propagates the global transform, and that system relies on Children being properly set up.
When spawning a child entity, try to use the with_children function instead of spawn.

@tigregalis
Copy link
Contributor

tigregalis commented Jan 30, 2021

You could do this:

// Load the child sprite
fn load_child(
    commands: &mut Commands,
    query: Query<Entity, (Without<Loaded>, With<TheParentEnt>)>,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for ent in query.iter() {
        // Spawn the child sprite
        let texture = asset_server.load("textures/rpg/chars/hat-guy/hat-guy.png");
        let child = commands
            .spawn(SpriteBundle {
                material: materials.add(ColorMaterial::texture(texture)),
                transform: Transform::from_translation(Vec3::new(-50., -50., 0.)),
                ..Default::default()
            })
            .with(Parent(ent))
            .current_entity()
            .unwrap();

        // as you've manually added the Parent component to the child, then
        // manually add the Children component to the parent
        commands.insert_one(ent, Children::with(&[child]));

        // Prevent processing parent sprite again by marking it loaded
        commands.insert_one(ent, Loaded);
    }
}

However, I believe the canonical way is:

// Load the child sprite
fn load_child(
    commands: &mut Commands,
    query: Query<Entity, (Without<Loaded>, With<TheParentEnt>)>,
    asset_server: Res<AssetServer>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for ent in query.iter() {
        // Spawn the child sprite
        let texture = asset_server.load("textures/rpg/chars/hat-guy/hat-guy.png");
        let child = commands
            .spawn(SpriteBundle {
                material: materials.add(ColorMaterial::texture(texture)),
                transform: Transform::from_translation(Vec3::new(-50., -50., 0.)),
                ..Default::default()
            })
//            .with(Parent(ent))
            .current_entity()
            .unwrap();
        
        // Pushing takes a slice of children to add:
        // where ent: Entity, child: Entity
        commands.push_children(ent, &[child]);

        // Prevent processing parent sprite again by marking it loaded
        commands.insert_one(ent, Loaded);
    }
}

@MinerSebas
Copy link
Contributor

I think the issue comes from adding the Parent component directly. The system that automatically adds the corresponding Children component probably runs after the system that propagates the global transform, and that system relies on Children being properly set up.
When spawning a child entity, try to use the with_children function instead of spawn.

Almost, the problem is, that the two are in the same Stage: See here for further Information #1198

@zicklag
Copy link
Member Author

zicklag commented Jan 30, 2021

@tigregalis that worked!!! Thanks a lot! I spent all day yesterday wrestling with this, so it's really nice to get it working.

I think I'll go ahead and close this because there is a solution and the weirdness should be worked out with the new hierarchy system comming in: #1198 (comment).

@karroffel karroffel added A-ECS Entities, components, systems, and events S-User-Error This issue was caused by a mistake in the user's approach labels Jan 30, 2021
@alice-i-cecile alice-i-cecile added the C-Usability A targeted quality-of-life change that makes Bevy easier to use label Dec 12, 2021
@alice-i-cecile alice-i-cecile added A-Hierarchy Parent-child entity hierarchies A-Transform Translations, rotations and scales and removed A-ECS Entities, components, systems, and events labels Apr 4, 2022
@SUPERCILEX
Copy link
Contributor

@alice-i-cecile This can probably be closed in favor of #1807 or #4197

@james7132
Copy link
Member

Is this still an issue? #4197 has been merged. @zicklag if this is still happening and easy to run into despite these changes, please comment, otherwise please close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Hierarchy Parent-child entity hierarchies A-Transform Translations, rotations and scales C-Usability A targeted quality-of-life change that makes Bevy easier to use S-User-Error This issue was caused by a mistake in the user's approach
Projects
None yet
Development

No branches or pull requests

8 participants