#Tilemaps

1426 messages · Page 2 of 2 (latest)

lime crest
#

A tilemap is just a more efficient way to render a grid of (generally square) sprites. Technically you could just spawn a bunch of sprites next to each other and you'd get the same visual result but performance will scale horribly. Tilemaps are a more controlled way to do that. Currently what we have in bevy is a way to render chunks but eventually we want an abstraction over this so users don't have to think about chunks and they only need to give bevy their entire map and don't need to bother splitting it in chunks.

coral harbor
#

It would be nice to be able to specify which way the tiles will be layed out

#

I have built in chunks integrated into my tile library that does infinite tilemaps and all the fancy querying I want in bevy core, but for each insertion I have to do this:
tile_i = (chunk_size - tile_i / chunk_size - 1) * chunk_size + tile_i % chunk_size;
to recalculate the chunk index.

For most other things, it makes sense to just layout the chunk similar to world coordinates, but that doesn't work with these chunks

coral harbor
coral harbor
#

Err, transform calc is wrong, gotta fix that

coral wolf
coral harbor
#

I'm only making it an option since some people are already using the current way, changing to my preferred default would be breaking

coral wolf
#

so which do you think should be the preferred default and why?

coral harbor
#

Bottom left to top right to match world coords

#

currently it's top left to bottom right because of default uv direction in shader?

lime crest
#

So, I haven't looked at the PR yet but the original goal of the TilemapChunk was to be a rendering primitive and most users shouldn't be using this directly. There should be another layer sitting on top of it that makes it more user friendly and that's probably where that kind of thing should be handled

coral harbor
#

I have my own crate that is a layer on top of this and handling it directly means special casing coordinate and index calculations for the built in chunk renderer

coral harbor
#

Also it looks like I'm not the only one to run into this

coral wolf
#

my general opinion is that it would not be great from a maintainability and general use perspective to have options that nobody is using. If there's a better default I think we should switch to it rather than adding options.

the implementation in the pr for the options looks fine

#

if we introduce options, then everyone who uses this needs to care that there are four ways to do it

rugged sapphire
#

Yeah, and even if there are edge cases like "some tools export things in different orientations", that feels like an asset-processing problem, which should be fixed by a "add a setting to the tilemaps" solution

#

It's okay to make breaking changes, really 🙂 Just clearly document why you're doing so and how to migrate

coral harbor
#

Changing the default is certainly easier if we're okay breaking things

coral harbor
#

Closing the other one

coral wolf
coral harbor
#

Idk how to word it, but in world coordinates you count right 5 than up 6

coral wolf
#

5 tiles spaces right, then 6 tile spaces up?

coral harbor
#

To get that same position in a chunk on the screen you'd have to count right 5 then down 9 spaces

coral harbor
coral wolf
#

so you could mod the tile position and get the index of a tile in a tile chunk correctly, that's pretty cool tbh

coral harbor
#

Because y 15 in the chunk is actually the bottom edge of the chunk, where as in world coordinates that would be y 0

coral harbor
#

It makes it much easier and more intuitive to calculate things

coral wolf
#

yeah, we might want to point people to rem_euclid in the migration guide

#

since -1 would be chunk_length-1 tile pos

coral wolf
#

ran it, it works as expected. I gave an approval but I'm expecting IceSentry to also take a look at some point when they have time since they have the vision for the chunk abstraction in their head.

Needs a clippy fix to pass CI

coral wolf
#

I confirmed ldtk levels are top-left 0,0, and tiled seems to also have this layout in the editor as well, fwiw. Didn't check unity/godot

coral harbor
#

Godot is going to be the same, unity bottom left is 0,0 iirc

#

But godots whole coordinate system is -y up

#

I think the engines tilemap feature should match it's coordinate system

lime crest
lime crest
coral wolf
coral harbor
lime crest
#

But at this point I might be willing to accept anything that actually works and is well written

coral harbor
#

I think we shouldn't have each tile be an entity

#

But

#

I think we should easily enable that on top of the chunk abstraction

lime crest
#

That's exactly why I want to make sure the rendering and end user api are separate. I know not everyone wants entity-per-tile, but I'm pretty sure most people want some way to render a tilemap efficiently

coral wolf
#

well, you know where I stand on that so. 😆

coral harbor
#

Basically, in my mind the Chunk is just an entity with Vec<T: TileComponent> components stored on it, so if a user wants a tile per entity, we'd have a chunk with Vec<Option<Entity>> stored on it

lime crest
#

I'm more than happy to accept various utility functions that can be used to build both approaches though

coral wolf
#

I don't think having a non-entity api is bad, but having entity as a default for people that aren't doing 14million tile single chunk tilemaps is far more useful

coral harbor
lime crest
coral harbor
coral wolf
coral harbor
#

What do you mean?

coral wolf
#

even ones 3 hundred thousand tiles off-screen

#

(I'm picking arbitrary numbers, obviously)

#

but like, the entity count mattering really requires having live entities that aren't really involved in the game being spawned

lime crest
# coral harbor What do you mean?

Bevy should be able to handle really high entity count if most of these entities don't change every frame and you aren't iterating the full list every frame

#

There used to be an issue caused by the render world that would slow things down horribly when hitting a high amount of entities but that has been resolved

coral harbor
#

If you're not iterating the full list every frame and not changing them very often why do they need to be entities?

And what about accidentally iterating them every frame or proposing hierarchy changes on accident via transform?

coral wolf
#

because entities are fundamentally a useful tool for attaching data and behavior to

coral harbor
#

The chunk rendering kinda shows that for the rendering portion, what really matters is the chunk, and I think that's true for a lot of tile use cases, if you want full ecs it's better to opt in

coral harbor
coral wolf
#

they are bevy's default though, so they would be opt-out

coral harbor
#

Technically bevy today has tiles now without entity per tile

#

In the future do you want what currently exists to require an entity per tile?

coral wolf
#

I never said I wanted to require it

#

I think its the right default behavior

#

and like I was saying before, chunking usually involves despawning/respawning entities. For example: 3d terrain

lime crest
coral wolf
#

so why would a massive tilemap have every entity be always-live if it was chunked?

coral harbor
coral wolf
#

I can definitely envision edge cases (dwarf fortress maybe?), but that kind of scale isn't the default

coral harbor
#

It's actually easier

lime crest
coral harbor
#

You have to opt in to an entity per tile by spawning an entity with the tile component

#

Imo

#

The opt out is using some other api that lets you work with chunks more directly

#

But in my mind that's not having an entity per tile be the default

lime crest
#

It's the default in the sense that it would be the default end user api that is being shown/advertised/used in examples.

coral wolf
#

(also just to be clear, its fine to differ in opinion on this. "both apis" are going to exist)

lime crest
#

And people not using that end user api would be opting out of that. Part of the idea there is also that we could make changes to the rendering side without needing to impact the end users. So people not using this api would essentially opt out of that stability. (tbf, we don't have any stability guarantess right now but that's the general idea)

#

And yeah, I'm pretty sure it's possible to make the current chunk api easier to use and build the entity per tile on top of that. That way most people should have what they need.

coral harbor
#

So the problem I have in my mind is generating a physics mesh per chunk.

If entity per tile is the default, I have to do a lot of entity subqueries per map to get the entities per chunk so I know which physics shapes I need to add up.

If we have some arbitrary chunk data store, I can just query for the Chunk<CollisionInfo>(Vec<Option<CollisionInfo>>) type on each chunk pretty straightforwardwardly

#

And if it's a static level, I get no benefit from having an entity per tile, so I'd rather be able to completely side step that

#

And I don't think this is a particularly advanced use case

coral wolf
#

I don't see why those colliders wouldn't be pre-computed if it was static

#

this is a pretty common conversation with avian even for 3d applications

coral harbor
#

For the same reason you don't precompute the mesh and texture for each chunk in a mostly static tilemap, it's kinda annoying

coral wolf
#

I think you're misconstruing "default" with "will never be able to avoid"

#

you won't have to use it if you don't want to

coral harbor
#

I'm worried about entity per tile having to be "avoided"

coral wolf
#

why?

coral harbor
#

Because I think for the vast majority of use cases it's unnecessary

coral wolf
#

that's not really a reason for it not to exist, and you're allowed to have that opinion

coral harbor
#

I'm not saying it shouldnt exist, I think it absolutely should, it's a sick feature and use case

#

But I don't think it should be used to supercede other options when a not ecs integrated solution would make more sense, and I don't think it should be pushed as the default way of using tilemaps.

For instance, I don't think importing an ldtk map should automatically spawn an entity per tile

coral wolf
#

its fine to have that opinion, but the most widely used ldtk integration does exactly that, and its not being pushed at all

coral harbor
#

That's also one of the first and only maintained tilemap libraries and doesn't have any other mode of operation

coral wolf
#

to reframe it: Bevy has already made the choice to be entity-forward in contrast to other engines. It makes sense to use entities as the default API because everything else revolves around them (observers, etc). Not using entities is opting-out of the rest of bevy's offering.

coral harbor
#

Then the chunk api as today should be changed, the chunk entity should be a render world construct

#

I think it would be less useful that way, but it would be entity forward

coral wolf
#

there's a draft pr that I've already linked to you in the past that makes these changes

#

the current implementation is a foothold implementation to land something in the 0.17 cycle

coral harbor
#

And it doesn't need entity per tile

coral wolf
#

and the render chunk api isn't "going anywhere", other apis will be built on top

coral harbor
#

Just like importing an ldtk map doesn't need an entity per tile

coral wolf
#

I don't feel like this conversation is going anywhere so I'm going to step away. To be clear: nobody is going to force you to use entities, but that is the primary bevy api so makes sense as the default high-level interface.

coral harbor
#

I've looked at the PR and it has the issue I'm talking about, it's entirely focused on Entity per tile, it doesn't build out an API that Entity per tile can be built on, it makes Entity per tile the only way of doing things outside of rendering a chunk

#

So I can avoid using it, but then I have to write my own bespoke chunking impl

coral harbor
#

Also just to be clear since I know we were talking past each other, I do understand that everyone here wants the best user experience and engine possible, I don't mean to be rude if/when I am.

sinful condor
#

I integrated TilemapChunk into my game. I see that you recently inverted the y axis to match Bevy's other coordinate systems. I agree with this change. It was unexpected that 0,0 in the chunk was referencing the top-left tile.

coral harbor
#

Playing around with tilemaps between meetings

#

If we have a tilemap component on entities that enables so automatical chunk spawning/handling, what would be an elegant way of handling an insane user who decides to reinsert the map with a new chunk size?

coral harbor
rugged sapphire
lime crest
lime crest
coral harbor
#

So I imagine the size of a chunk will be uniform in a tilemap, so the best place to store the chunk size would be on a tilemap component that the user can instantiate

#

If we automatically spawn new chunks when we insert a tile, we will use that components chunk size to calculate the chunks tile storage size

#

However, since we're letting the user instantiate the tilemap component, and are letting them choose the chunk size, they can insert a new tilemap component on an entity that already has one

#

In that case, all the existing chunks in that tilemap would be the incorrect size, and we'd have to update the sizes and rearrange tiles between chunks

#

It would be a mega pain to write and a pretty slow/bad thing for the user to do, but idk if we could technically stop them

lime crest
coral harbor
#

In my mind a different layer is just a different tilemap, but yes, within a layer the chunk size is uniform

lime crest
#

Yeah, me too, but just wanted to make sure

coral harbor
#

Maybe if they change chunk size at runtime we just panic!("Please stop being cringe")

#

Anyway, just a thought I had while working on this

lime crest
#

I would prefer not using that kind of language and if possible avoid panics and just return an error, but yes, I'm fine with that kind of scenario being considered an error

wary lion
#

I think my draft PR handles this unless I’m misunderstanding

#

Handles automatic chunk creation on tile insertion I mean

wary lion
#

I also think we want TileStorage to store entities probably? I’m not sure about making it generic

coral harbor
#

It's about what happens after a chunk has been created

#

So if someone set 4x4 as the chunk size on the top level map and then created a chunk, it would be a Vec with size 16

#

But then if they reinsert the map component on the map entity with chunk size 6x6, what happens to the 4x4 chunk?

coral harbor
wary lion
#

Ah I stand corrected -- [cart before had mentioned](#1367261228459884604 message) that opt-in is worth considering. So it's more just what the final API looks like and how comfortable people are with it.

#

So I guess no explicit decision, however I think we were sort of headed in the "opt-out" direction generally.

coral harbor
#

I think the opt-in opt-out discussion is not getting at the proper issue

#

You always have to opt-in to entity per tilemap because you have to spawn a tile

#

Now when you spawn that tile, it can create the TileStorage<Entity> component on the entity chunk

#

If you don't want a tile per entity, you just don't spawn tile entities

#

You work with TileStorage<T> directly, or some other api's that work on the specific storages

#

You get tile per entity if you want it, and you don't have to sacrifice any other use case while they all use common components and functionality

coral wolf
#

The miscommunication here seems to be that @coral harbor seems to think that "the default APIs" will mean "everyone has to use entities" which is not the case. Users will be able to not use entities, and both use cases will be supported.

coral harbor
#

Correct

coral wolf
#

cool, I think we're all on the same page then now.

coral harbor
#

It's pretty straightforward imo, the only issue I see is batching large number of spawns

coral harbor
#

Basically: component hook on a TileCoord component automatically puts the tiles entity into a chunks tile storage

wary lion
#

In the example, it looks like you're spawning the tile entities in update. Would you instead want to spawn in setup and then just update the TileRenderData component?

coral harbor
#

I was just copying my non-entity example

#

But that would also work

#

(I'd need to change the component hook but this is poc level code)

coral harbor
#

Is there a way to remove a component without triggering the component hook?

coral harbor
#

Hrmm... How opposed are we to having that behind unsafe?

#

I'm investigating but I think it's necessary for automatic tile cleanup

#

Basically, if I remove a TileCoord component from an entity, I need to do some cleanup in the tile storage.

If I place a tile on top of another tile, I remove the old tile from storage, then remove the TileCoord from the replaced entity, but that will trigger the same cleanup code as above, even though the cleanup is done.

wary lion
#

I think this may be why ecs_tilemap stores the previous position as well? Correct me if I'm wrong @coral wolf

#

actually maybe ecs_tilemap doesn't handle this case

coral wolf
#

hooks are meant for this kind of index upkeep though, so I'm not sure why you'd need unsafe or to avoid triggering a hook

coral harbor
#

You'd want to avoid triggering the hook to avoid repeated work and wrong behavior:

I have a tile at (0,0), a place a new tile at (0,0), this removes the old tile and I remove the TileCoord component from it.

Removing the TileCoord component would also be a way to remove the tile from the map, so now since we removed the component from the replaced tile, we go in and remove that tile from storage... But we already did that when we placed the old tile, so really we're just removing the new tile

coral wolf
#

sounds like a problem that stems from trying to manage the index manually without the components

coral harbor
#

No, it's just a direct result of having to manage indices through hooks:

world.spawn(TileCoord(0,0))

What should happen if you call that twice?

coral wolf
#

if you have an entity with a TileCoord or whatnot, and that gets despawned or removed, then that on_remove/on_insert, etc fires, which is all the proper ordering for it right?

coral wolf
coral harbor
#

That's what I think also

#

So we spawn the first entity and insert it into some chunk as the entity at (0,0)

#

Then we go to spawn the second entity, it sees there's an entity at (0,0) and replaces it in the chunk then goes to despawn it

#

Now when despawning the first entity, we remove TileCoord

coral wolf
#

I guess the thought there is to enable sparse TileCoord entities?

coral harbor
#

Hrm?

#

In this case the spareness doesn't matter

coral harbor
coral wolf
#

tile storage here is TilemapChunkTileData? or something else?

coral harbor
#

For the sake of argument yes, it's the think that lets a chunk know an entity is in the chunk

coral wolf
#

so for the sake of argument, there's no entity->tiledata sort of index to use?

coral harbor
#

We have a Vec of entities on a chunk and we have entities with TileCoord components

coral wolf
#

seems like an error to spawn TileCoord components if one already exists

#

if we can't track the entity responsible for the tiledata

coral harbor
#

It doesn't seem like an error to me, it seems like something totally normal to do

coral wolf
#

it feels like it is, almost like these are getting treated as Resources instead

#

I can't think of another example of a second entity getting spawned with the same components that then fully replaces the first

#

is there an example I'm not thinking of?

coral harbor
#

I think replacing the first would be optional

coral wolf
#

(Resources basically having the API of being entities with component data, if you haven't been following that discussion)

coral harbor
#

But that's not the important part

#

A tile is just something that has a relationship to a chunk indexed by coordinates, something else should be able to take over that relationship without being an error

#

Putting something into a hash map that is already there isn't an error, you just replace the old thing and get the old thing back, that seems normal for tilemaps

coral wolf
#

so yeah I see what you're getting at since there won't be a hook to fire for the existing entity, if a new one is spawned in without it knowing

#

but that really feels like the original one should be replaced, not a new entity inserted

coral harbor
#

I mean the original one would be replaced, but then the original one will have things trigger that can remove the new one

#

Or there's just a bunch of weird ordering things to figure out

#

Not going to even start talking about the nightmare of multiple tile storages on a chunk

#

Which I do think is correct but requires more book keeping

#

Ecs compatible tilemaps are not straightforward

coral wolf
#

I think the "two entities should be able to exist for the same TileCoord" is the weird part here

coral harbor
#

I don't think they should

#

I think one of them should lose their TileCoord component or be despawned

coral wolf
#

yeah which is why I was referencing the Resource use case earlier, and its special set of apis for enabling that behavior

#

if you .insert_resource, for example, that results in the Resource getting replaced

#

I see the argument for using .spawn to do this, in that its familiar to users, but it is not a behavior I can think of any other spawn currently having

coral harbor
#

Sure, but in the event the user wants to keep the old tile entity, we wouldn't want to replace it in the world, just the tile storage, we'd only want to remove the TileCoord component

coral wolf
coral harbor
#

Not automating the TileCoord removal?

coral wolf
#

a custom command would potentially solve it, since that would have world access

#

but that's a departure from .spawn

coral harbor
#

Yeah

coral wolf
coral harbor
#

My issue with that is it gives the user a whole lot of book keeping to do when using this feature

#

And you don't have to do that book keeping in any other engine imo

coral wolf
#

secondary question: would the first entity despawn or just have the component removed?

#

both feel valid

coral harbor
#

I was thinking there'd be a DontDespawnOnRemoved component or vice versa

#

So you could have both

coral wolf
#

eh, I don't love that 😅

coral harbor
#

Since I think both are valid

#

I mean it's not the cleanest

#

We could also hide the flag in the InMap component

#

Imma just keep working on this PR and making big sweeping decisions so there's something to look at

#

In my bevy_tiles crates I did things via custom commands and it gets pretty ugly

#

But idk maybe that's the right way

coral wolf
#

trying to modify other entities in hooks also feels really weird

coral harbor
#

It comes with the territory of some entities being psuedo storages for other entities

coral wolf
#

I think it comes with not having an index afaict

coral harbor
#

Like secondary entity indices?

coral wolf
#

just any index at all. ie: trying to compress into just a Vec<WhateverData> without entities vs having entity keys or values around to check

#

.spawn could also be disallowed, which also solves the problem and allows hooks to only operate on the current component

#

(and I do think enabling .spawn to overwrite other data is new and strange behavior compared to what exists right now)

#

not that its Wrong or anything, I just can't think of anything else that does that

coral harbor
#

I mean don't relations have to use hooks to write to other entities?

coral wolf
#

they insert themselves into a collection, yeah. They don't despawn other entities.

#

like to remove an entity from Children you'd remove the ChildOf on that entity. Which would translate to removing TileCoord.

coral harbor
#

This would be that but one step further and it is a bit strange

spice wedge
#

And even with vertex UVs only in the range [0, 1] you can still get fragments with an input uv > 1 depending on camera position, so you get flickering seams between chunks.

#

I fixed it by doing this:

let tile_uv = clamp(in.uv, vec2<f32>(0.0), vec2<f32>(0.9999999)) * vec2<f32>(chunk_size);
var tile_coord = vec2<u32>(floor(tile_uv));

but it feels a little janky

coral harbor
#

Okay, one parental medical episode, one grandparental episode, one dog broken leg, and one family dog passing later, I have added automatic tile removal to my PoC

I'm so tired gang 🥲

Anyway, here's the problem: If you have an Tile Entity or a Non-Entity Tile, when you Despawn it, everything keeping track of the Tile will want to hear about it somehow to cleanup whatever they are tracking.

Since my approach to this problem consists of a chunk entity that stores tile data that we don't want living on an entity for whatever reason (perf opts for things like collision mesh generation for example) but still supports tiles as entities, it means that when a tile entity gets respawned, we probably want to remove that tile from each collection on the chunk, for example:

CHUNK: TILE_SPRITES [ .. ] | TILE_COLLIDER_SHAPES [ .. ] | TILE_HEALTH [ .. ] | TILE_ENTITIES [ .. ]

So say I despawn a tile entity, I also need to remove that tiles sprite, collider, and health from these collections. The issue is... well that's kind of hard to do dynamically since tiles can have arbitrary information added, but I figured it out.

Basically, the chunk now keeps track of what tile storages it has added to it, when a tile storage gets added, we also register the function for removing data at a specific index, then in the tile removal/despawn code we have the following:

for (tile_storage, tile_removal) in tile_storages.removals {
    let Ok(storage) = tile_storage_entity.get_mut_by_id(tile_storage) else {
        continue;
    };
    tile_removal(storage, tile_relative_position);
}

To automatically go and clear out the data for that tile.

I think this is the final basic functionality that is needed for spawning/despawning adding/removing, and I think it's pretty intuitive. Next up is the specialized query type for spatial info.

coral harbor
spice wedge
#

Oh discord can't convert that

#

there we go

#

Arrows or WASD to move, F3 to toggle tile (white) and chunk (magenta) debug grid

coral wolf
spice wedge
#

Does the fix I came up with look okay? I can open a PR if so

coral wolf
#

the 0.999999 looks suspicious tbh, but I wouldn't dissuade you from a PR. At least the issue/feedback/etc would be documented on github then

spice wedge
#

Yeah that's my main concern. It's 1.0 - f32::EPSILON so it's not completely arbitrary, but it might cause issues with big chunks.

coral harbor
#

Going to push another change tonight that includes a TileQuery:

fn set_tile(
    mut commands: Commands,
    mut clicks: MessageReader<Pointer<Click>>,
    camera_query: Single<(&Camera, &GlobalTransform)>,
    map: Single<Entity, With<Tilemap>>,
    mut tiles: TilemapQuery<&mut TileRenderData>,
) {
    let (camera, camera_transform) = *camera_query;
    let map = *map;

    let mut tiles = tiles.get_map_mut(map).unwrap();

    for click in clicks.read() {
        if let Ok(tile_coord) =
            camera.viewport_to_world_2d(camera_transform, click.pointer_location.position)
        {
            let tile_coord = tiles.map.get_tile_coord(tile_coord);
            info!("{}", tile_coord);
            if let Some(tile) = tiles.get_at_mut(tile_coord) {
                tile.tileset_index = (tile.tileset_index + 1) % 4;
            } else {
                commands.set_tile(
                    map,
                    tile_coord,
                    Some(TileRenderData {
                        tileset_index: 0,
                        ..Default::default()
                    }),
                );
            }
        }
    }
}

Check the 2d/tilemap example to try it, it basically lets you do positional lookups into the map via a system param, I'll do the Entity compat version tomorrow, that will let you query Entity tiles via their tile index and is built ontop of the TilemapQuery

coral harbor
#

Going to do one more push then I'm hoping I can get some eyes on this before during the spatial iterators:

Added TileEntity queries, this is an entity query for entities that have a TileCoord component and handles all the logic of indexing, so you can just look up a tile entity by it's coordinate, I also added a usage example to the breakout example:


fn on_bomb_hit(
    hit: On<BrickHit>,
    map: Single<Entity, With<Tilemap>>,
    bombs: Query<(Entity, &TileCoord), With<Bomb>>,
    bricks: TilemapEntityQuery<Entity, With<Brick>>,
    mut score: ResMut<Score>,
    mut commands: Commands,
) {
    let Ok((bomb_id, bomb_coord)) = bombs.get(hit.0) else {
        return;
    };
    commands.entity(bomb_id).despawn();
    **score += 1;

    let Some(bricks) = bricks.get_map(*map) else {
        return;
    };

    for coord in bomb_coord.adjacent() {
        if let Some(brick) = bricks.get_at(*coord) {
            commands.entity(brick).trigger(BrickHit);
        }
    }
}

It makes it pretty straightforward to start doing spatial based stuff in the grid

coral harbor
#

@coral wolf ^ when you have a chance

coral harbor
#

Updated the PR with a big ol' wall of text and example videos

thorny spruce
#

So a friend is trying out bevy, and their project involves a tilemap. (After running into perf issues caused by entity-per-tile of bevy_ecs_tilemap) They're trying the built-in bevy tilemap.
First they ran into https://github.com/bevyengine/bevy/issues/21501, which would've been difficult to debug, but luckily I knew of the issue.
Then they had issues with finding the documentation of the coordinate systems used, but that needed just some trial and error.
Now they're dealing with these one-pixel-thick line artifacts which, as far as I can tell, are caused by texture samplers sampling into neighbouring tiles of the tilemap.

I feel like this is a bug in the renderer?
And is there a workaround? (Usually I'd add padding around the sprites in the texture, but I don't see how to make the renderer skip over the margins.)

#

Oh sorry, now I found the already existing issue for it. But still, if anyone knows a workaround, that would be salient.

lime crest
thorny spruce
#

MSAA? I'm not sure how MSAA would cause these, but we can try it.

coral wolf
thorny spruce
#

I can show you the screenshot of the tracy profile I saved:

#

Some of the perfs bottlenecks are due to user code

coral wolf
thorny spruce
#

But I believe entity_sync and some of those extract schedules are out of direct control. (And by themselves are too long to fit in a frame)

coral wolf
#

yeah "entity spawn speed" similar to "transform propagation speed" is an upstream issue generally

thorny spruce
lime crest
thorny spruce
lime crest
#

MSAA is generally a good default to have except for pixel art where it's not great

thorny spruce
#

Okay so apparently disabling MSAA (by inserting Msaa::Off) just, broke rendering entirely? (Currently trying to troubleshoot it)

coral harbor
#

@thorny spruce curious, is entity per tile something they need?

thorny spruce
#

So removing the entities is a feature in this case.

thorny spruce
#

Updates from tilemap adventures:

#

Moving from bevy_ecs_tilemap to the bevy builtin greatly improved perf:

#

The game still chugs, but this time it's 95% user systems, so it's not a bevy problem

thorny spruce
#

we ended up patching the renderer

lime crest
#

oof

thorny spruce
lime crest
#

what was the patch?

thorny spruce
#

The fix was this comment

#

I am not sure I know how it works, but it seems to work.

lime crest
#

So are you running MSAA right now?

#

I'm still confused by the part where disabling msaa broke things

thorny spruce
#

Yeah Msaa is still running, I honestly have no clue what happened with the msaa, but turning it off made the screen blank (camera clear colour)

lime crest
#

That's very strange. No error messages?

#

If you can make a repro that would be really nice

thorny spruce
thorny spruce
#

Nvm, I am confused now, I'll investigate tomorrow

#

I must've made a mistake in the repro and tested the wrong thing

thorny spruce
#

Here's some feedback from the tilemap user (as of 0.17.3, maybe you've already patched some of those things):
The tilemap works pretty great, but:
Getting the texture 2Darray format on the image seems needlessly complicated, and there was no way for a newbie to figure out the bug from the wgpu errors behind the crash.
The texture bleeding is difficult to deal with, and it probably should've just worked out of the box.
They would've liked support for flipping the tiles (in all 8 possible symmetries of the square, so 2 axis flips and a diagonal one) (I've been thinking bevy_math maybe should have a symmetry type for this.)
Coordinate systems (indexing, anchor, position in local space) would warrant better docs.

coral harbor
#

Def agree on all of the above

spice wedge
#

My biggest issue is the tight coupling with TilemapMaterial. I think things would be a lot easier to tweak if we could put the tile data somewhere else. Or make TilemapMaterial extensible without messing with the tile data upload.

#

I want to see how the bevy_material split turns out before trying to implement something though. There's functionality in 3d that might be useful if 2d/3d materials can be merged.

rugged sapphire
#

Hey, do y'all have any GSoC project ideas for #1460367944193282089? This working group feels like it's prime for "high value low urgency technically crunchy" work

coral harbor
#

Very late but not that I can think of

#

It really is just, "get some base tilemaps feature" merged in

#

Then iterate and expand on it

#

Speaking of, I'd love to work more on that PR if it seems like a reasonable direction for tilemaps to go 😳

gray vault
#

also, what needs to happen on it? Do you need maintainer guidance on direction?

coral harbor
coral harbor
#

I just wanted enough code to show the idea and see if it's usable before making it a fully fledged feature

rugged sapphire
#

This is very much the sort of thing I want as a user

#

And seems nice to hook into

#

@high otter may have opinions too

high otter
#

haven’t clicked yet or dug in - is this meant to be an alternative for bevy_ecs_tilemap?

#

If so, I would echo @rugged sapphire’s positive vibes for bevy_map_editor

#

also, nothing against bevy_ecs_tilemap either - but nice to get first party support for tilemaps

coral harbor
#

Yes but not bevy_map_editor, this is just adding tilemaps with auto chunking and tile entity support to bevy

#

No auto tiling, no editors, no levels, just the basic map and utilities for working with it

#

I think auto tiling and such can be built on top, just like this is built on the chunk renderer

high otter
#

We use bevy_ecs_tilemap quite heavily

#

And it would be nice to have tilemap support without tiles being forced as entities

coral harbor
#

Do you care if the tilemap + chunks are entities?

coral harbor
high otter
high otter
#

Could be nice for infinite maps, but we don’t support infinite maps yet 🙂

lime crest
lime crest
coral harbor
#

It is, I think he's saying it's fine

high otter
lime crest
#

Ah, yeah, I misinterpreted that 😅

high otter
#

Tbh, overhead is only really important for infinite maps which we don’t really support yet, and I haven’t really performance tested hard the limits of having tiles as entities

#

I generally only work in 50x50 or 100x100 right now - but we obviously want to support much larger tilemaps

#

1000x1000 did seem ok in bevy_map_editor though

#

I would say that I’m happy to help test this heavily though whenever you need

lime crest
#

I'm pretty sure the current approach with chunks as entities will work with infinite world. No game is actually infinite and the amount of chunks you would need for the ECS part to be an issue would be absolutely giant

high otter
#

yes, i figured as much

#

I’m totally fine with that

high otter
#

@coral harbor just a question on the example snippet:

bricks: TilemapEntityQuery<Entity, With<Brick>>,

Rather than a specific Query type for Tilemaps, could we improve the ergonomics a bit to just use Query<Tilemap, With<Brick>> because presumably the Tilemap itself is an entity? Or have I misunderstood that

coral harbor
#

The breakout example can mostly be ignored, it's a bit of a cumbersome addition to an already fine example

#

The Tilemap itself is an entity, but the bricks are tiles in the tilemap

#

Tile entities specifically, so we look up the map, then can index which brick we want

#

The TilemapEntityQuery and TilemapQuery's are designed for looking up items in tilemaps and are basically nested queries with functionality for doing positional look ups

high otter
#

Yes that makes sense to me tbh

#

I’m probably not well placed to give a proper review of your implementation, but i can certainly help test it!

rugged sapphire
spice wedge
#

I think I may have just found a fun issue.

#

I was getting this on WASM while trying to load a tilemap with a texture that happened to contain 12 tiles

#

adding an empty 13th tile to the texture fixed it

thorny spruce
#

Oh wow, that is supremely annoying

quartz kestrel
coral wolf
quartz kestrel
#

oh oops, wrong thread

#

but yeah, seems like yall will need the same janky workaround

wary lion
#

I had fixed this at some point, but maybe that was part of my unmerged PR.. let me take a look

#

well, the "fix" here is to say don't do it x.x

#

There was some reason we didn't want to just increment the layer count at the time, but I don't remember why..

wary lion
#

My original thinking is that we'd have a tileset asset processor and it'd be fixed there.

sudden fable
#

Tilemaps

spice wedge
#

It might be worth logging a warning in ImageLoader or something. I could see this being a problem for sprite animations too if someone uses an array texture instead of the built-in atlases.

tall orbit
#

I've just implemented a (bad) tilemap for my needs. Which is rendering maps (actual factual maps, so each tile is ever only used once and when moving the map new tiles need to be inserted constantly). I do also need all of the data in a single texture/texture array so I can do wacky projections and such.
-> Should I expect to be able to use Bevy's tilemaps in the future? Or am I firmly in the niche category?

wary lion
#

Can you explain your use case a bit more? What do you mean by factual maps? Also, what data do you need in the texture-- the tile indices and such?

tall orbit
# wary lion Can you explain your use case a bit more? What do you mean by factual maps? Also...

by actual maps I mean that I'm drawing real world maps, so getting tiles from OpenStreetMap and similar. so unlike in the usual videogame scenario no single tile is ever rendered multiple times on the same tilemap. but i do need all of the tiles on a single texture so i can reproject them before presenting #showcase message
I'm guessing I'm in the niche category but it would be nice to be able to use bevy's implementation for my usecase too

wary lion
#

Oh yeah that's an interesting use case. I guess the fact that no tile is ever reused means it doesn't fit very well. You would have to create a massive tileset, or dynamically swap tilesets out I guess?

wary lion
tall orbit
coral harbor
fast osprey
#

so I did some thinking about tilemaps

coral wolf
#

written mostly by Claude

wary lion
#

I'm reading through this and there's some interesting points. I would say that as long as you confirmed what was written and you're standing behind it, I don't care who/what wrote it. You did mention not confirming the engine breakdown table -- it'd be nice if you were able to confirm what's there.

#

Also you admit at the end it's ai slop x.x -- I don't know if that was in jest, but please don't send ai slop.

#

These are just my opinions btw, I know Bevy has a stricter stance on ai contributed code, but I'm not really sure where it lands on ai ideation/prototyping stuff.

coral wolf
wary lion
#

I don't even think "let me google that for you" is a bad thing in some cases. Like if the person sending it knew a specific thing to query google the target wasn't aware of (like some data structure or something). I think in this case it's pretty clear @fast osprey had to write some pretty specific prompts and do some iteration on the AI response to get it to this state, so I also think it's a bit more effort than just googling something obvious.

coral wolf
coral harbor
#

But also I don't think you needed AI to make this point, I've made the argument a few times

coral harbor
fast osprey
# wary lion I'm reading through this and there's some interesting points. I would say that a...

I did read through it about dozen times and tweak it. wrt, the Engine breakdown table, I can speak to Unity being correct bc that was my engine of choice for the last decade. I think the main point I wanted to make is 'don't build a tilemap just because everyone else has a thing called tilemap, it ends up being a very leaky abstraction in practice, and there's a lot of tradeoffs involved.' ie: Unity's 'TileMap', which lets you attach gameobjects to tiles, create tileprefabs, etc, can be an immediate foot-gun for noobs trying to make a big world ("oh ill make a tile and attach my trees to it"). (and also the size of the tilemap gets serialized and doesn't update when you delete tiles in editor, so if you make some tilemap that is 100x100, delete it until its 10x10, you will have so much fun when some other part of your game wants to know the width/height of a tilemap. you just have to hit that edge case, struggle, and find out you need to call recalculatebounds sometimes)
and Unity's terrain system... is why there's multiple asset store assets selling terrain systems. And like, its also a basically a kind of tilemap.

What I'm saying is, instead of having a 'TileMap', have different versions 'grid-aligned-data-rendering-system' that are optimized for the different use-cases that come up in practice, so that where the shared APIs/features between them are, the re-use happens in a sensible way. cuz there's a lot of commonality between '2d TileMap' and '3d height field TerrainMap'.

rugged sapphire
lime crest
#

@rose bear you might have ideas about this ^

rose bear
#

oh , while I might have ideas what the bug is... this might be unrelated to what I was fixing/doing since that was alpha-compositing in nonlinear color spaces. this seems like there is a final display transform / gamma curve applied to the texture. probably texture format inconsistency between the render graph internals and what is presented on screen later. very similar to the egui blit challenges you face

#

the incorrect gradient comes from srgb being treated as linear

lime crest
#

Yeah, I wouldn't be surprised it's something similar going on

rose bear
#

I feel like our render graph needs a good way to tag passes as srgb or linear - both the inputs and outputs from shaders. that way tracking down bugs like this would be trivial.

#

I think now mostly everything assumes it's linear.

hardy surge
#

I did some brainstorming about this topic. I need to translate it to English first and type it because it's handwritten, but I'm happy to share it

hardy surge
#

maybe I put too much effort to cross validate my ideas about the tile map system, and in my research process I found this document
https://hackmd.io/@bevy/Hkum_o-xll

Is this still relevant ?
Because it looks like, I wrote more or less the same thing, but with some extra or advanced features

HackMD

Bevy Tilemap Renderer

coral harbor
#

I will check the review of the tilemap PR this week

#

Packed bit of life recently

hardy surge
#

No worries, I will write down my ideas as an extension of this document/pr

tough gust
#

I've been working on figuring out how to allow for tilemaps to use a grid arranged tileset rather than a vertically stacked one. I gathered some notes and dropped them in https://github.com/bevyengine/bevy/issues/23492#issuecomment-4188125763 Does this seem like a good plan or am I approaching this completely wrong?

GitHub

What problem does this solve or what need does it fill? It is natural that you would draw together the tiles in tileset that appear adjacent to one another on the map. That helps you to create seam...

hardy surge
wary lion
hardy surge
# wary lion Thanks for the writeup! TileEntityMode per-chunk is interesting, but I wonder if...

The idea behind this is to prevent the entity explosion by setting up some sort of limit (in reality, it's more like a QoL feature for newcomers and devs who don't want to implement all the necessary systems).
Advanced users will still be able to use the Custom option when they need to implement the related systems and the brakes on their creations.
Using multiple layers is still possible with this.

coral harbor
#

Just a quick skim this basically looks like what my PR does

#

There's just no automatic turning of data tiles into entity tiles, but that wouldn't be hard for someone to build on top

hardy surge
#

I was not aware of you PR until Conner shared with me and honestly I was lazy to read through.
But I'm happy for the similarities

coral harbor
#

Check it out and give it a review, I'll eventually address comments 🫣

wary lion
#

Who's the SME assigned to this working group?

rugged sapphire
wary lion
coral harbor
#

Add an 8th day to the week

#

I'll review soon 😭

wary lion
#

Should we try to get more reviews on it so you can batch review?

coral harbor
#

That would be good

hardy surge
#

I'm trying to find a window for review in one batch; I started yesterday, but it's difficult to manage, besides my daily work (Chaos Driven Development 4 life)

coral harbor
#

@rugged sapphire do you know what the motivaiton for this is?

rugged sapphire
sinful condor
coral harbor
#

@wary lion addressed comments

#

also fixed merge issues