use bevy::{
app::{App, PluginGroup, Startup, Update},
asset::{AssetServer, Assets},
core_pipeline::core_2d::Camera2dBundle,
ecs::{
component::Component,
event::EventReader,
query::With,
system::{Commands, EntityCommands, Query, Res, ResMut},
},
input::{keyboard::KeyCode, Input},
math::Vec2,
reflect::Reflect,
render::{mesh::Mesh, texture::ImagePlugin, view::Msaa},
sprite::TextureAtlas,
utils::HashMap,
DefaultPlugins,
};
use bevy_entitiles::{
ldtk::{
app_ext::AppExt,
events::LdtkEvent,
json::{field::FieldInstance, level::EntityInstance, EntityRef},
layer::physics::LdtkPhysicsLayer,
resources::{LdtkAssets, LdtkLevelManager},
sprite::LdtkEntityMaterial,
},
tilemap::physics::PhysicsTile,
EntiTilesPlugin,
};
use bevy_entitiles_derive::{LdtkEntity, LdtkEntityTag, LdtkEnum};
use bevy_xpbd_2d::{
components::{Collider, Friction, LinearVelocity, Mass, RigidBody},
plugins::{debug::PhysicsDebugConfig, PhysicsDebugPlugin, PhysicsPlugins},
resources::Gravity,
};
use helpers::EntiTilesHelpersPlugin;
mod helpers;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(ImagePlugin::default_nearest()),
EntiTilesPlugin,
EntiTilesHelpersPlugin::default(),
PhysicsPlugins::default(),
PhysicsDebugPlugin::default(),
))
.add_systems(Startup, setup)
.add_systems(Update, (load, events, hot_reload, player_control))
.register_type::<Teleport>()
.register_type::<Player>()
.register_type::<Item>()
.insert_resource(Msaa::Off)
.insert_resource(Gravity(Vec2::new(0., -98.)))
.insert_resource(PhysicsDebugConfig::all())
.register_ldtk_entity::<Item>("Item")
.register_ldtk_entity::<Player>("Player")
.register_ldtk_entity::<Teleport>("Teleport")
.register_ldtk_entity::<Ladder>("Ladder")
.register_ldtk_entity::<SecretArea>("SecretArea")
.register_ldtk_entity_tag::<Actor>("actor")
.register_ldtk_entity_tag::<Loot>("loot")
.register_ldtk_entity_tag::<Region>("region")
.run();
}
fn setup(mut commands: Commands, mut manager: ResMut<LdtkLevelManager>) {
commands.spawn(Camera2dBundle::default());
let tocs = manager.initialize_get_tocs(
&mut commands,
"assets/ldtk/ignore grid_vania.ldtk".to_string(),
"ldtk/".to_string(),
);
println!("tocs: {:?}", tocs);
manager
.set_physics_layer(LdtkPhysicsLayer {
identifier: "PhysicsColliders".to_string(),
air: 0,
parent: "Collisions".to_string(),
tiles: Some(HashMap::from([
(
1,
PhysicsTile {
rigid_body: true,
friction: Some(0.9),
},
),
(
2,
PhysicsTile {
rigid_body: true,
friction: Some(0.1),
},
),
])),
})
.ignore_unregistered_entities()
.ignore_unregistered_entity_tags();
}
macro_rules! level_control {
($key:ident, $level:expr, $input:expr, $manager:expr, $commands:expr) => {
if $input.pressed(KeyCode::ControlLeft) {
if $input.just_pressed(KeyCode::$key) {
$manager.unload(&mut $commands, $level.to_string());
}
} else if $input.just_pressed(KeyCode::$key) {
$manager.switch_to(&mut $commands, $level.to_string(), None);
}
};
}
fn load(mut commands: Commands, input: Res<Input<KeyCode>>, mut manager: ResMut<LdtkLevelManager>) {
level_control!(Key1, "Entrance", input, manager, commands);
level_control!(Key2, "Cross_roads", input, manager, commands);
level_control!(Key3, "Water_supply", input, manager, commands);
level_control!(Key4, "Ossuary", input, manager, commands);
level_control!(Key5, "Garden", input, manager, commands);
level_control!(Key6, "Shop_entrance", input, manager, commands);
level_control!(Key7, "phantom level", input, manager, commands);
if input.just_pressed(KeyCode::Space) {
manager.unload_all(&mut commands);
}
if input.just_pressed(KeyCode::Key8) {
manager.load(&mut commands, "Entrance".to_string(), None);
}
}
fn hot_reload(
input: Res<Input<KeyCode>>,
mut manager: ResMut<LdtkLevelManager>,
mut assets: ResMut<LdtkAssets>,
asset_server: Res<AssetServer>,
mut atlas_assets: ResMut<Assets<TextureAtlas>>,
mut entity_material_assets: ResMut<Assets<LdtkEntityMaterial>>,
mut mesh_assets: ResMut<Assets<Mesh>>,
) {
if input.just_pressed(KeyCode::Return) {
manager.reload_json();
assets.initialize(
&manager,
&asset_server,
&mut atlas_assets,
&mut entity_material_assets,
&mut mesh_assets,
);
println!("Hot reloaded!")
}
}
fn events(mut ldtk_events: EventReader<LdtkEvent>) {
for event in ldtk_events.read() {
match event {
LdtkEvent::LevelLoaded(level) => {
println!("Level loaded: {}", level.identifier);
}
LdtkEvent::LevelUnloaded(level) => {
println!("Level unloaded: {}", level.identifier);
}
}
}
}
fn player_control(mut query: Query<&mut LinearVelocity, With<Player>>, input: Res<Input<KeyCode>>) {
let Ok(mut player) = query.get_single_mut() else {
return;
};
if input.pressed(KeyCode::Left) {
player.x = -30.;
}
if input.pressed(KeyCode::Right) {
player.x = 30.;
}
if input.pressed(KeyCode::Up) {
player.y = 100.;
}
}
fn player_spawn(
commands: &mut EntityCommands,
entity_instance: &EntityInstance,
_fields: &HashMap<String, FieldInstance>,
_asset_server: &AssetServer,
_ldtk_manager: &LdtkLevelManager,
_ldtk_assets: &LdtkAssets,
) {
let size = Vec2::new(entity_instance.width as f32, entity_instance.height as f32);
commands.insert((
Collider::convex_hull(vec![
Vec2::new(-0.5, 0.) * size,
Vec2::new(0.5, 0.) * size,
Vec2::new(0.5, 1.) * size,
Vec2::new(-0.5, 1.) * size,
])
.unwrap(),
RigidBody::Dynamic,
Friction {
dynamic_coefficient: 0.5,
static_coefficient: 0.5,
..Default::default()
},
Mass(100.),
));
}
#[derive(LdtkEnum, Reflect, Clone, Copy, Debug)]
#[wrapper_derive(Reflect, Default)]
pub enum ItemType {
Meat,
Gold,
GoldNuggets,
Gem,
#[ldtk_name = "Green_gem"]
GreenGem,
#[ldtk_name = "Healing_potion"]
HealingPotion,
Spell,
Armor,
Bow,
Ammo,
#[ldtk_name = "Fire_blade"]
FireBlade,
#[ldtk_name = "Vorpal_blade"]
VorpalBlade,
}
#[derive(Component, LdtkEntity, Default, Reflect)]
#[spawn_sprite]
#[global_entity]
#[callback(player_spawn)]
pub struct Player {
pub inventory: ItemTypeVec,
#[ldtk_name = "HP"]
pub hp: i32,
#[ldtk_default]
pub mp: i32,
}
#[derive(Component, LdtkEntity, Reflect)]
#[spawn_sprite]
pub struct Ladder;
#[derive(Component, LdtkEntity, Reflect)]
#[spawn_sprite]
pub struct SecretArea;
#[derive(Component, LdtkEntity, Reflect)]
#[spawn_sprite]
pub struct Item {
#[ldtk_name = "type"]
pub ty: ItemType,
pub price: i32,
pub count: i32,
}
#[derive(Component, LdtkEntity, Reflect)]
#[spawn_sprite]
pub struct Teleport {
pub destination: EntityRef,
}
#[derive(Component, LdtkEntityTag)]
pub struct Actor;
#[derive(Component, LdtkEntityTag)]
pub struct Loot;
#[derive(Component, LdtkEntityTag)]
pub struct Region;