StarArawn / bevy_ecs_tilemap

A tilemap rendering crate for bevy which is more ECS friendly.
MIT License
949 stars 198 forks source link

Moving tiles to where another tile used to be fails to render properly #559

Open LilithHohman opened 2 months ago

LilithHohman commented 2 months ago

When moving tiles as in the move_tiles.rs example, if multiple tiles are moved simultaneously and one moves to the coordinates where another previously was, it's rendered as blank space.

Here's code to reproduce this issue, it's just a slightly modified move_tiles.rs example. Two tiles are moved around a 2x2 square. Both are visible at first, but after the first move only one is visible.


use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
mod helpers;

fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());

    let texture_handle: Handle<Image> = asset_server.load("tiles.png");

    let map_size = TilemapSize { x: 2, y: 2 };

    // Create a tilemap entity a little early.
    let tilemap_entity = commands.spawn_empty().id();

    let mut tile_storage = TileStorage::empty(map_size);

    // Spawn the elements of the tilemap.

    let tile_pos = TilePos { x: 0, y: 0 };
    let tile_entity = commands
        .spawn(TileBundle {
            position: tile_pos,
            tilemap_id: TilemapId(tilemap_entity),
            ..Default::default()
        })
        .id();
    tile_storage.set(&tile_pos, tile_entity);

    let tile_pos = TilePos { x: 1, y: 0 };
    let tile_entity = commands
        .spawn(TileBundle {
            position: tile_pos,
            tilemap_id: TilemapId(tilemap_entity),
            ..Default::default()
        })
        .id();
    tile_storage.set(&tile_pos, tile_entity);

    let tile_size = TilemapTileSize { x: 16.0, y: 16.0 };
    let grid_size = tile_size.into();
    let map_type = TilemapType::default();

    commands.entity(tilemap_entity).insert(TilemapBundle {
        grid_size,
        map_type,
        size: map_size,
        storage: tile_storage,
        texture: TilemapTexture::Single(texture_handle),
        tile_size,
        transform: get_tilemap_center_transform(&map_size, &grid_size, &map_type, 0.0),
        ..Default::default()
    });
}

fn swap_pos(keyboard_input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut TilePos>) {
    if keyboard_input.just_pressed(KeyCode::Space) {
        for mut pos in query.iter_mut() {
            if pos.x == 0 && pos.y == 0 {
                pos.x = 1;
                pos.y = 0;
            } else if pos.x == 1 && pos.y == 0 {
                pos.x = 1;
                pos.y = 1;
            } else if pos.x == 1 && pos.y == 1 {
                pos.x = 0;
                pos.y = 1;
            } else {
                pos.x = 0;
                pos.y = 0;
            }
        }
    }
}

fn main() {
    App::new()
        .add_plugins(
            DefaultPlugins
                .set(WindowPlugin {
                    primary_window: Some(Window {
                        title: String::from("Update tile positions without despawning."),
                        ..Default::default()
                    }),
                    ..default()
                })
                .set(ImagePlugin::default_nearest()),
        )
        .add_plugins(TilemapPlugin)
        .add_systems(Startup, startup)
        .add_systems(Update, helpers::camera::movement)
        .add_systems(Update, swap_pos)
        .run();
}
ChristopherBiscardi commented 2 months ago

confirmed the behavior described here. haven't looked into cause yet though. Toggling visibility on the tile shows it is always in the expected location.

https://github.com/user-attachments/assets/8728d017-298a-4a4e-bdbd-413273eb60c8