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

Respect pivot in tile layers #162

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

geieredgar
Copy link
Contributor

This should fix #152.

Tiles which are not aligned to the grid are put into own layers, one layer for each encountered alignment offset.
They also only span the part of the grid in which tiles are found.
Another separate layer is added that contains the entities for the int grid cells.

@Trouv
Copy link
Owner

Trouv commented Mar 18, 2023

Finally starting to look at this. It's kinda scary changing stuff like this. Here's an example why - check out the platformer example, the tops of the ladders don't seem to be acting as ladders. Not sure why yet

@geieredgar
Copy link
Contributor Author

I've found the bug, it was this line:

max: IVec2::new(layer_instance.c_wid, layer_instance.c_hei),

It should have been

max: IVec2::new(layer_instance.c_wid - 1, layer_instance.c_hei - 1),

During debugging I've tried out several things and am now wondering if it would be better to have one entity that corresponds to the layer (like before, but without the TileMapBundle, because we may need several of those).
Then the tile-maps for each sub-layer would be a children of the layer entity, and the tile entities would also be children of the layer entity instead of the tile map entity. This would restore the previous behavior that all tiles of a layer are children of the same entity.

I am also wondering if we should add spatial bundles only to tile entities created from the IntGridCell, because all other entities should probably only be used for rendering.

@Trouv
Copy link
Owner

Trouv commented Mar 24, 2023

During debugging I've tried out several things and am now wondering if it would be better to have one entity that corresponds to the layer (like before, but without the TileMapBundle, because we may need several of those).
Then the tile-maps for each sub-layer would be a children of the layer entity, and the tile entities would also be children of the layer entity instead of the tile map entity. This would restore the previous behavior that all tiles of a layer are children of the same entity.

This makes sense to me - though I'm not sure off the top of my head if bevy_ecs_tilemap requires tile entities to be children of tilemaps?

If that hierarchy is required, I think that's fine. That would be a breaking change for many users, but it sounds like a logical hierarchy to me and it's not like we're a stable library.

I am also wondering if we should add spatial bundles only to tile entities created from the IntGridCell, because all other entities should probably only be used for rendering.

IntGridCells and tiles w/ tile metadata. I think giving it to EVERY tile is a holdover from when we used batch-spawning, since they all had to have the same bundle. So, unless there's some other use case for that I'm forgetting, that makes sense to me too.

Copy link
Owner

@Trouv Trouv left a comment

Choose a reason for hiding this comment

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

Finally got around to reviewing this again - thanks for your patience

IVec2::new(a.px.x.div_euclid(grid_size), a.px.y.div_euclid(grid_size))
}

struct SubLayer {
Copy link
Owner

Choose a reason for hiding this comment

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

Could you add some docs for this type? Mostly for maintenance purposes - what its purpose is, why it's designed like this, etc.

I'm also tempted to ask you to move it to a new private sub_layer module - but I will likely refactor a lot of this level-spawning code later so I'll say that's optional

}

#[derive(Default)]
struct SubLayers {
Copy link
Owner

Choose a reason for hiding this comment

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

Some basic docs here would be nice too

}

#[derive(Default)]
struct SubLayers {
Copy link
Owner

Choose a reason for hiding this comment

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

NIT: this is much more than just a collection of sublayers, and I think the name could reflect that. Maybe like TileLayerBuilder?

})
}

fn unwrap(self) -> Vec<SubLayer> {
Copy link
Owner

Choose a reason for hiding this comment

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

NIT: Could we call this something other than unwrap? That term in my mind is associated with unsafe error handling, or mutexes. Maybe finish()?

false
}

fn insert(&mut self, tile: TileInstance, grid_size: i32) {
Copy link
Owner

Choose a reason for hiding this comment

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

It'd be nice to get some tests for these new methods you've added. This one especially is not trivial.

overflow.push(tile);
} else {
layer.push(tile);
layers.insert(tile, grid_size);
Copy link
Owner

Choose a reason for hiding this comment

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

I think it would be cool if the insert method guaranteed that the no intersections occurred itself. That way, there would be no human error in the case that some future dev tries to run insert before checking intersects first.

It's already looping through all the layers to check their offsets, so it wouldn't be very costly to also check the intersection within that loop, rather than out here. Then instead of insert returning nothing, maybe it could return something like:

enum SubLayersInsert {
    Success,
    Overflow(TileInstance),
}

If you went ahead with moving the sub layer logic to another module, then you could help regulate these type guarantees further by keeping the internal field private and only publicize insert and finish to the rest of the crate. However, like I said, I will be doing some refactoring soon so this is definitely optional.

.collect::<Vec<_>>()
})
.enumerate()
if layer_instance.layer_instance_type == Type::IntGrid
Copy link
Owner

Choose a reason for hiding this comment

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

After some thought, I have to say that I'm not entirely convinced that the intgrid sub-layer should be completely separate from the autotile sub-layers that render it. Much of the point of using bevy_ecs_tilemap is that each tile is an entity, and with ECS we can easily tack-on the intgrid values to these tile entities. I think it's more in the spirit of ECS to not provide multiple purposes to these entities by making them siblings, but instead to just give the same entity more components. Though I do admit it has its limitations w/ this PR in particular, and it definitely complicates things. Thoughts?

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

Successfully merging this pull request may close these issues.

Rules based tiles don't use pivot
2 participants