Skip to content

Commit

Permalink
Merge pull request #106 from Trouv/refactor/tilemap-rewrite
Browse files Browse the repository at this point in the history
refactor!: update to bevy 0.8 and bevy_ecs_tilemap rewrite
  • Loading branch information
Trouv committed Aug 14, 2022
2 parents 8a56caf + 056a4e3 commit 1d9f0a4
Show file tree
Hide file tree
Showing 17 changed files with 990 additions and 896 deletions.
908 changes: 485 additions & 423 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ exclude = ["assets/*", "repo/*", "scripts/*"]

[dependencies]
bevy_ecs_ldtk_macros = { version = "0.3", optional = true, path = "macros" }
bevy_ecs_tilemap = "0.6"
bevy = { version = "0.7", default-features = false }
bevy_ecs_tilemap = { version = "0.7.0" }
bevy = { version = "0.8", default-features = false, features = ["bevy_sprite"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.5"
regex = "1"
hex = "0.4"
anyhow = "1.0"
thiserror = "1.0"

[dev-dependencies]
bevy = "0.7"
heron = { version = "3.0", features = ["2d"] }
bevy = "0.8"
heron = { version = "4.0.0-alpha.4", features = ["2d"] }
rand = "0.8"

[features]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ fn main() {
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(Camera2dBundle::default());

commands.spawn_bundle(LdtkWorldBundle {
ldtk_handle: asset_server.load("my_project.ldtk"),
Expand Down
5 changes: 3 additions & 2 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use bevy::prelude::*;
use bevy::{prelude::*, render::texture::ImageSettings};
use bevy_ecs_ldtk::prelude::*;

fn main() {
App::new()
.insert_resource(ImageSettings::default_nearest()) // prevents blurry sprites
.add_plugins(DefaultPlugins)
.add_plugin(LdtkPlugin)
.add_startup_system(setup)
Expand All @@ -12,7 +13,7 @@ fn main() {
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(Camera2dBundle::default());

commands.spawn_bundle(LdtkWorldBundle {
ldtk_handle: asset_server.load("my_project.ldtk"),
Expand Down
2 changes: 1 addition & 1 deletion examples/field_instances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn main() {
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(Camera2dBundle::default());

asset_server.watch_for_changes().unwrap();

Expand Down
2 changes: 1 addition & 1 deletion examples/level_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const LEVEL_IIDS: [&str; 8] = [
];

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(Camera2dBundle::default());

let iids: HashSet<String> = LEVEL_IIDS.into_iter().map(|s| s.to_string()).collect();

Expand Down
3 changes: 2 additions & 1 deletion examples/platformer/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This example shows off a more in-depth implementation of a game with `bevy_ecs_ldtk`.
// Please run with `--release`.

use bevy::prelude::*;
use bevy::{prelude::*, render::texture::ImageSettings};
use bevy_ecs_ldtk::prelude::*;

use heron::prelude::*;
Expand All @@ -11,6 +11,7 @@ mod systems;

fn main() {
App::new()
.insert_resource(ImageSettings::default_nearest()) // prevents blurry sprites
.add_plugins(DefaultPlugins)
.add_plugin(LdtkPlugin)
.add_plugin(PhysicsPlugin::default())
Expand Down
115 changes: 67 additions & 48 deletions examples/platformer/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet};
use heron::prelude::*;

pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let camera = OrthographicCameraBundle::new_2d();
let camera = Camera2dBundle::default();
commands.spawn_bundle(camera);

asset_server.watch_for_changes().unwrap();
Expand Down Expand Up @@ -105,16 +105,31 @@ pub fn spawn_wall_collision(
right: i32,
}

// consider where the walls are
/// A simple rectangle type representing a wall of any size
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default, Hash)]
struct Rect {
left: i32,
right: i32,
top: i32,
bottom: i32,
}

// Consider where the walls are
// storing them as GridCoords in a HashSet for quick, easy lookup
//
// The key of this map will be the entity of the level the wall belongs to.
// This has two consequences in the resulting collision entities:
// 1. it forces the walls to be split along level boundaries
// 2. it lets us easily add the collision entities as children of the appropriate level entity
let mut level_to_wall_locations: HashMap<Entity, HashSet<GridCoords>> = HashMap::new();

wall_query.for_each(|(&grid_coords, &Parent(parent))| {
// the intgrid tiles' direct parents will be bevy_ecs_tilemap chunks, not the level
// To get the level, you need their grandparents, which is where parent_query comes in
if let Ok(&Parent(level_entity)) = parent_query.get(parent) {
wall_query.for_each(|(&grid_coords, parent)| {
// An intgrid tile's direct parent will be a layer entity, not the level entity
// To get the level entity, you need the tile's grandparent.
// This is where parent_query comes in.
if let Ok(grandparent) = parent_query.get(parent.get()) {
level_to_wall_locations
.entry(level_entity)
.entry(grandparent.get())
.or_insert(HashSet::new())
.insert(grid_coords);
}
Expand Down Expand Up @@ -165,15 +180,15 @@ pub fn spawn_wall_collision(
}

// combine "plates" into rectangles across multiple rows
let mut wall_rects: Vec<Rect<i32>> = Vec::new();
let mut previous_rects: HashMap<Plate, Rect<i32>> = HashMap::new();
let mut wall_rects: Vec<Rect> = Vec::new();
let mut previous_rects: HashMap<Plate, Rect> = HashMap::new();

// an extra empty row so the algorithm "terminates" the rects that touch the top
// edge
plate_stack.push(Vec::new());

for (y, row) in plate_stack.iter().enumerate() {
let mut current_rects: HashMap<Plate, Rect<i32>> = HashMap::new();
let mut current_rects: HashMap<Plate, Rect> = HashMap::new();
for plate in row {
if let Some(previous_rect) = previous_rects.remove(plate) {
current_rects.insert(
Expand Down Expand Up @@ -201,38 +216,41 @@ pub fn spawn_wall_collision(
previous_rects = current_rects;
}

// spawn colliders for every rectangle
for wall_rect in wall_rects {
commands
.spawn()
.insert(CollisionShape::Cuboid {
half_extends: Vec3::new(
(wall_rect.right as f32 - wall_rect.left as f32 + 1.)
* grid_size as f32
commands.entity(level_entity).with_children(|level| {
// Spawn colliders for every rectangle..
// Making the collider a child of the level serves two purposes:
// 1. Adjusts the transforms to be relative to the level for free
// 2. the colliders will be despawned automatically when levels unload
for wall_rect in wall_rects {
level
.spawn()
.insert(CollisionShape::Cuboid {
half_extends: Vec3::new(
(wall_rect.right as f32 - wall_rect.left as f32 + 1.)
* grid_size as f32
/ 2.,
(wall_rect.top as f32 - wall_rect.bottom as f32 + 1.)
* grid_size as f32
/ 2.,
0.,
),
border_radius: None,
})
.insert(RigidBody::Static)
.insert(PhysicMaterial {
friction: 0.1,
..Default::default()
})
.insert(Transform::from_xyz(
(wall_rect.left + wall_rect.right + 1) as f32 * grid_size as f32
/ 2.,
(wall_rect.top as f32 - wall_rect.bottom as f32 + 1.)
* grid_size as f32
(wall_rect.bottom + wall_rect.top + 1) as f32 * grid_size as f32
/ 2.,
0.,
),
border_radius: None,
})
.insert(RigidBody::Static)
.insert(PhysicMaterial {
friction: 0.1,
..Default::default()
})
.insert(Transform::from_xyz(
(wall_rect.left + wall_rect.right + 1) as f32 * grid_size as f32 / 2.,
(wall_rect.bottom + wall_rect.top + 1) as f32 * grid_size as f32 / 2.,
0.,
))
.insert(GlobalTransform::default())
// Making the collider a child of the level serves two purposes:
// 1. Adjusts the transforms to be relative to the level for free
// 2. the colliders will be despawned automatically when levels unload
.insert(Parent(level_entity));
}
))
.insert(GlobalTransform::default());
}
});
}
});
}
Expand Down Expand Up @@ -407,18 +425,19 @@ pub fn update_level_selection(
) {
for (level_handle, level_transform) in level_query.iter() {
if let Some(ldtk_level) = ldtk_levels.get(level_handle) {
let level_bounds = Rect {
bottom: level_transform.translation.y,
top: level_transform.translation.y + ldtk_level.level.px_hei as f32,
left: level_transform.translation.x,
right: level_transform.translation.x + ldtk_level.level.px_wid as f32,
let level_bounds = bevy::sprite::Rect {
min: Vec2::new(level_transform.translation.x, level_transform.translation.y),
max: Vec2::new(
level_transform.translation.x + ldtk_level.level.px_wid as f32,
level_transform.translation.y + ldtk_level.level.px_hei as f32,
),
};

for player_transform in player_query.iter() {
if player_transform.translation.x < level_bounds.right
&& player_transform.translation.x > level_bounds.left
&& player_transform.translation.y < level_bounds.top
&& player_transform.translation.y > level_bounds.bottom
if player_transform.translation.x < level_bounds.max.x
&& player_transform.translation.x > level_bounds.min.x
&& player_transform.translation.y < level_bounds.max.y
&& player_transform.translation.y > level_bounds.min.y
&& !level_selection.is_match(&0, &ldtk_level.level)
{
*level_selection = LevelSelection::Iid(ldtk_level.level.iid.clone());
Expand Down
5 changes: 3 additions & 2 deletions examples/traitless.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Roughly equivalent to the "basic" example, except it doesn't use the LdtkEntity convenience
// trait. As a result, you can run this example with --no-default-features

use bevy::prelude::*;
use bevy::{prelude::*, render::texture::ImageSettings};
use bevy_ecs_ldtk::prelude::*;

fn main() {
App::new()
.insert_resource(ImageSettings::default_nearest()) // prevents blurry sprites
.add_plugins(DefaultPlugins)
.add_plugin(LdtkPlugin)
.add_startup_system(setup)
Expand All @@ -15,7 +16,7 @@ fn main() {
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(Camera2dBundle::default());

commands.spawn_bundle(LdtkWorldBundle {
ldtk_handle: asset_server.load("my_project.ldtk"),
Expand Down
1 change: 1 addition & 0 deletions macros/src/ldtk_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ fn expand_sprite_sheet_bundle_attribute(
asset_server.load(#asset_path).into(),
bevy::prelude::Vec2::new(#tile_width, #tile_height),
#columns, #rows, bevy::prelude::Vec2::splat(#padding),
Vec2::ZERO,
)
),
sprite: bevy::prelude::TextureAtlasSprite {
Expand Down
2 changes: 1 addition & 1 deletion src/app/ldtk_int_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pub trait LdtkIntCell {
/// Note: whether or not the entity is registered to the app, the plugin will insert [Transform],
/// [GlobalTransform], and [Parent] components to the entity **after** this bundle is inserted.
/// So, any custom implementations of these components within this trait will be overwritten.
/// Furthermore, a [bevy_ecs_tilemap::TileBundle] will be inserted **before** this bundle, so
/// Furthermore, a [bevy_ecs_tilemap::tiles::TileBundle] will be inserted **before** this bundle, so
/// be careful not to overwrite the components provided by that bundle.
fn bundle_int_cell(int_grid_cell: IntGridCell, layer_instance: &LayerInstance) -> Self;
}
Expand Down
40 changes: 18 additions & 22 deletions src/components.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! [Component]s and [Bundle]s used by the plugin.

pub use crate::ldtk::{EntityInstance, LayerInstance, Type};
pub use crate::ldtk::EntityInstance;
use crate::ldtk::{LayerInstance, Type};
use bevy::prelude::*;

use std::{
Expand All @@ -16,10 +17,7 @@ use crate::{
utils::ldtk_grid_coords_to_grid_coords,
};

use bevy_ecs_tilemap::{TileBundle, TileBundleTrait, TileParent, TilePos};

#[allow(unused_imports)]
use bevy_ecs_tilemap::Map;
use bevy_ecs_tilemap::tiles::{TileBundle, TilePos};

/// [Component] added to any `IntGrid` tile by default.
///
Expand Down Expand Up @@ -118,15 +116,15 @@ impl From<GridCoords> for IVec2 {
impl From<TilePos> for GridCoords {
fn from(tile_pos: TilePos) -> Self {
GridCoords {
x: tile_pos.0 as i32,
y: tile_pos.1 as i32,
x: tile_pos.x as i32,
y: tile_pos.y as i32,
}
}
}

impl From<GridCoords> for TilePos {
fn from(grid_coords: GridCoords) -> Self {
TilePos(grid_coords.x as u32, grid_coords.y as u32)
TilePos::new(grid_coords.x as u32, grid_coords.y as u32)
}
}

Expand Down Expand Up @@ -315,23 +313,13 @@ impl From<&LayerInstance> for LayerMetadata {
}
}

#[derive(Clone, Default, Bundle)]
#[derive(Copy, Clone, Debug, Default, Bundle)]
pub(crate) struct TileGridBundle {
#[bundle]
pub tile_bundle: TileBundle,
pub grid_coords: GridCoords,
}

impl TileBundleTrait for TileGridBundle {
fn get_tile_pos_mut(&mut self) -> &mut TilePos {
self.tile_bundle.get_tile_pos_mut()
}

fn get_tile_parent(&mut self) -> &mut TileParent {
self.tile_bundle.get_tile_parent()
}
}

#[derive(Clone, Default, Bundle)]
pub(crate) struct IntGridCellBundle {
pub int_grid_cell: IntGridCell,
Expand All @@ -347,13 +335,21 @@ pub(crate) struct EntityInstanceBundle {
/// After the ldtk file is done loading, the levels you've chosen with [LevelSelection] or
/// [LevelSet] will begin to spawn.
/// Each level is its own entity, with the [LdtkWorldBundle] as its parent.
/// Each level has `Handle<LdtkLevel>`, [Map], [Transform], and [GlobalTransform] components.
/// Finally, all tiles and entities in the level are spawned as children to the level unless marked
/// by a [Worldly] component.
/// Each level has a `Handle<LdtkLevel>` component.
///
/// All non-Entity layers (IntGrid, Tile, and AutoTile) will also spawn as their own entities.
/// Each layer's parent will be the level entity.
/// Each layer will have a [LayerMetadata] component, and are [bevy_ecs_tilemap::TilemapBundle]s.
/// Each tile in these layers will have the layer entity as its parent.
///
/// For Entity layers, all LDtk entities in the level are spawned as children to the level entity,
/// unless marked by a [Worldly] component.
#[derive(Clone, Default, Bundle)]
pub struct LdtkWorldBundle {
pub ldtk_handle: Handle<crate::assets::LdtkAsset>,
pub level_set: LevelSet,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub visibility: Visibility,
pub computed_visibility: ComputedVisibility,
}
Loading

0 comments on commit 1d9f0a4

Please sign in to comment.