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

Set z transform of each tile in one layer #276

Closed
PraxTube opened this issue Dec 4, 2023 · 4 comments
Closed

Set z transform of each tile in one layer #276

PraxTube opened this issue Dec 4, 2023 · 4 comments

Comments

@PraxTube
Copy link

PraxTube commented Dec 4, 2023

Is it possible to set the z transform for each tile in a given layer? For example, say we have a layer were we define objects like pots that we would want to include in a Y-sorting method. Could we set the z transform for each pot in this one layer? It doesn't even need to be dynamic, setting it once on creation would also work.

Relates to #95 and #117, but they are both about the layers as a whole not for the tiles inside of the layer. It's quite likely that this doesn't work at all due to the way the tiles are rendered. In that case, is it best to render the objects that need y sorting manually?

@Trouv
Copy link
Owner

Trouv commented Dec 5, 2023

Actually, bevy_ecs_tilemap does support y-sorting. You'll need to depend on bevy_ecs_tilemap separately, but you can enable it using the TilemapRenderSettings resource: https://docs.rs/bevy_ecs_tilemap/latest/bevy_ecs_tilemap/map/struct.TilemapRenderSettings.html#structfield.y_sort

Theoretically you wouldn't need to edit the z-value of the tiles at all.
You'd probably need to adjust the player's z-value to be at the same level as the objects you'd like to y-sort it against.

I haven't actually tried doing this, so I'd be curious to hear if you try it and how it goes.

@PraxTube
Copy link
Author

PraxTube commented Dec 5, 2023

Thanks for the pointer. I tried to implement it but there seem to be some issues (or I am just hugely misunderstanding something). I created an issue in the bevy_ecs_tilemap repo.

Though even if that issue would be resolved, there is another problem:
LDtk gives each tile a z-value to order them in the same layer, this value is always the same unless you have tiles that stack. In this case it will add 1.0 to each tile on top, even if they are siblings (same y value, different x value). This makes it really annoying to work with y sorting as you would need to account for this as well (or restrict yourself to non overlapping tiles).

There is another issue: If you have multiple layers like the background and collidables, then there is no way to disable the y-sorting for the background but enable it for the collidables (at least as far as I am aware). So you would also have to account for that.

All in all this is much more complex then I initially thought, and I am certainly not smart enough to figure out how to implement this with the built-in y-sort of bevy_ecs_tilemap. The only alternative I see is to implement the sprites you want to y-sort manually and have them completely separate from LDtk (or use some entities or similar in LDtk and then spawn the appropriate sprites later in bevy, but this seems like quite the hassle).

@Trouv
Copy link
Owner

Trouv commented Dec 6, 2023

Hmm yeah my knowledge of bevy_ecs_tilemap's y-sorting is iffy.

If you're doing it w/ sprites spawned by the plugin, I guess the way to go would be to adjust their z value (based on their y value) after they spawn. Trying to do it within LdtkEntity or something like that wouldn't work because that process is designed to add a Transform after the user-defined bundle is added.

Maybe something like

#[derive(Component, Default)]
struct YSort;

#[derive(Component, Default)]
struct YSortOriginalZ(f32);

#[derive(Bundle, Default, LdtkEntity)]
struct PotBundle {
    #[sprite_sheet_bundle]
    sprite: SpriteSheetBundle,
    y_sort: YSort,
}

fn initialize_y_sort_original_z(y_sorts_without_original_z: Query<(Entity, &Transform), (With<YSort>, Without<YSortOriginalZ>)>, mut commands: Commands) {
     for (entity, transform) in y_sorts_without_original_z.iter() {
        commands.entity(entity).insert(YSortOriginalZ(transform.translation.z));
    }
}

fn calculate_z_for_y_sort(mut y_sorted_transforms: Query<(&mut Transform, YSortOriginalZ), Changed<Transform>>) {
     for (mut transform, original_z) in y_sorted_transforms.iter_mut() {
        let new_z = original_z.0 - (transform.translation.y / 100.0);
        if transform.translation.z != new_z { // extra layer of change detection to avoid mutable access maybe idk
            transform.translation.z = new_z;
        }
    }
}

Once again I haven't tried this, but that might be a good starting point

PraxTube added a commit to PraxTube/magus-parvus that referenced this issue Dec 6, 2023
This is achieved by using `YSort` and properly offsetting the value
(taking into account the LDtk layer index and the parent z transform).

Note that overlapping tiles in the same layer is not supported yet. The
reason is simple: If you have a tile in layer A, which has an index of
3, then this tile will have a z value of 3 in bevy. However, if you have
two tiles that are overlapping each other, one of them will have value 3
while the other will have value 4.

That's why we are currently restricted to only non-overlapping tiles
(which is totally fine for this project).

More info at Trouv/bevy_ecs_ldtk#276.
@PraxTube
Copy link
Author

PraxTube commented Dec 6, 2023

That is actually exactly what I ended up trying. I was able to get it to work now. I also used the field instaces as a guiding example. There are some things to be aware of though, like the fact that the entities are children of the level (which will change their Transform) and that the translation.z will be changed according to the order of the layers in LDtk. Overlapping tiles in the same layer is also a problem (which I don't account for in my solution as I don't need to as of now). This is how I implemented.

EDIT: The parent issue can be partially resolved by using Worldly. In my case it's also better to spawn the SpriteSheetBundle separately, see here for these changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants