bevy_entitiles 0.3.0

A 2d tilemap library for bevy. With many useful algorithms/tools built in.
use bevy::{
    ecs::{
        entity::Entity,
        system::{ParallelCommands, Query},
    },
    math::UVec2,
};
use bevy_xpbd_2d::components::{Collider, Friction, RigidBody};

use crate::{
    math::aabb::IAabb2d,
    tilemap::{
        coordinates,
        map::{TilePivot, TilemapSlotSize, TilemapTransform, TilemapType},
    },
};

use super::{DataPhysicsTilemap, PhysicsTilemap};

pub fn spawn_colliders(
    commands: ParallelCommands,
    mut tilemaps_query: Query<(
        &mut PhysicsTilemap,
        &TilemapType,
        &TilemapTransform,
        &TilePivot,
        &TilemapSlotSize,
    )>,
) {
    tilemaps_query.par_iter_mut().for_each(
        |(mut physics_tilemap, ty, transform, tile_pivot, slot_size)| {
            let physics_tiles = physics_tilemap.spawn_queue.drain(..).collect::<Vec<_>>();
            physics_tiles.into_iter().for_each(|(aabb, physics_tile)| {
                commands.command_scope(|mut c| {
                    let mut tile_entity = c.spawn_empty();

                    let vertices = coordinates::get_tile_collider_world(
                        aabb.min,
                        ty,
                        aabb.size().as_uvec2(),
                        transform,
                        tile_pivot,
                        slot_size,
                    );
                    let collider = match ty {
                        TilemapType::Square | TilemapType::Isometric => {
                            Collider::convex_hull(vertices).unwrap()
                        }
                        TilemapType::Hexagonal(_) => Collider::polyline(vertices, None),
                    };

                    if physics_tile.rigid_body {
                        tile_entity.insert((collider, RigidBody::Static));
                    } else {
                        tile_entity.insert(collider);
                    }

                    if let Some(coe) = physics_tile.friction {
                        tile_entity.insert(Friction::new(coe));
                    }

                    physics_tilemap.storage.set_elem(aabb.min, tile_entity.id());
                });
            });
        },
    );
}

pub fn data_physics_tilemap_analyzer(
    commands: ParallelCommands,
    mut tilemaps_query: Query<(Entity, &mut DataPhysicsTilemap, Option<&mut PhysicsTilemap>)>,
) {
    tilemaps_query
        .par_iter_mut()
        .for_each(|(entity, mut data_tilemap, mut physics_tilemap)| {
            let mut aabbs = Vec::new();
            let size = data_tilemap.size;
            let air = data_tilemap.air;

            for y in 0..size.y {
                for x in 0..size.x {
                    let cur = UVec2 { x, y };

                    let cur_i = {
                        let i = data_tilemap.get_or_air(cur);
                        if i == air {
                            continue;
                        }
                        i
                    };

                    let mut d = UVec2 {
                        x: if x == size.x - 1 { 0 } else { 1 },
                        y: if y == size.y - 1 { 0 } else { 1 },
                    };
                    let mut dst = cur;
                    while d.x != 0 || d.y != 0 {
                        for t_x in cur.x..=dst.x {
                            if data_tilemap.get_or_air(UVec2::new(t_x, dst.y + d.y)) != cur_i {
                                d.y = 0;
                                break;
                            }
                        }

                        for t_y in cur.y..=dst.y {
                            if data_tilemap.get_or_air(UVec2::new(dst.x + d.x, t_y)) != cur_i {
                                d.x = 0;
                                break;
                            }
                        }

                        if d == UVec2::ONE
                            && data_tilemap.get_or_air(UVec2::new(dst.x + 1, dst.y + 1)) != cur_i
                        {
                            d.y = 0;
                        }

                        dst += d;
                    }

                    for y in cur.y..=dst.y {
                        for x in cur.x..=dst.x {
                            data_tilemap.set(UVec2 { x, y }, air);
                        }
                    }

                    aabbs.push((
                        IAabb2d {
                            min: cur.as_ivec2() + data_tilemap.origin,
                            max: dst.as_ivec2() + data_tilemap.origin,
                        },
                        data_tilemap.get_tile(cur_i).unwrap_or_default(),
                    ));
                }
            }

            commands.command_scope(|mut c| {
                if let Some(physics_tilemap) = &mut physics_tilemap {
                    physics_tilemap.spawn_queue.extend(aabbs);
                } else {
                    c.entity(entity).insert(PhysicsTilemap {
                        storage: Default::default(),
                        spawn_queue: aabbs,
                    });
                }

                c.entity(entity).remove::<DataPhysicsTilemap>();
            });
        });
}