feat(player): add player respawn and despawn

This commit is contained in:
Kristofers Solo 2024-12-12 20:33:26 +02:00
parent b89921dcd6
commit f4aefb00fa
19 changed files with 162 additions and 121 deletions

7
Cargo.lock generated
View File

@ -2589,12 +2589,13 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]] [[package]]
name = "hexlab" name = "hexlab"
version = "0.2.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b912e78d292803bc279aec3a4e2a0cdd0e0ac1540bcdc5d0f32cbfe9e4d234dc"
dependencies = [ dependencies = [
"bevy", "bevy",
"hexx", "hexx",
"rand", "rand",
"rand_chacha",
"thiserror 2.0.6", "thiserror 2.0.6",
] ]
@ -3076,7 +3077,7 @@ dependencies = [
[[package]] [[package]]
name = "maze-ascension" name = "maze-ascension"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"bevy", "bevy",
"bevy-inspector-egui", "bevy-inspector-egui",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "maze-ascension" name = "maze-ascension"
authors = ["Kristofers Solo <dev@kristofers.xyz>"] authors = ["Kristofers Solo <dev@kristofers.xyz>"]
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
@ -18,7 +18,7 @@ tracing = { version = "0.1", features = [
"release_max_level_warn", "release_max_level_warn",
] } ] }
hexx = { version = "0.19", features = ["bevy_reflect", "grid"] } hexx = { version = "0.19", features = ["bevy_reflect", "grid"] }
hexlab = { path = "../hexlab", features = ["bevy"] } hexlab = { version = "0.3", features = ["bevy"] }
bevy-inspector-egui = { version = "0.28", optional = true } bevy-inspector-egui = { version = "0.28", optional = true }
bevy_egui = { version = "0.31", optional = true } bevy_egui = { version = "0.31", optional = true }
thiserror = "2.0" thiserror = "2.0"

View File

@ -1,14 +1,15 @@
use std::ops::RangeInclusive; use crate::{
maze::{events::RecreateMazeEvent, MazeConfig, MazePluginLoaded},
player::events::RespawnPlayer,
};
use bevy::{prelude::*, window::PrimaryWindow}; use bevy::{prelude::*, window::PrimaryWindow};
use hexx::{Hex, HexOrientation};
use rand::{thread_rng, Rng};
use crate::maze::{events::RecreateMazeEvent, MazeConfig, MazePluginLoaded};
use bevy_egui::{ use bevy_egui::{
egui::{self, emath::Numeric, DragValue, TextEdit, Ui}, egui::{self, emath::Numeric, DragValue, TextEdit, Ui},
EguiContext, EguiContext,
}; };
use hexx::{Hex, HexOrientation};
use rand::{thread_rng, Rng};
use std::ops::RangeInclusive;
pub(crate) fn maze_controls_ui(world: &mut World) { pub(crate) fn maze_controls_ui(world: &mut World) {
if world.get_resource::<MazePluginLoaded>().is_none() { if world.get_resource::<MazePluginLoaded>().is_none() {
@ -55,6 +56,9 @@ pub(crate) fn maze_controls_ui(world: &mut World) {
{ {
event_writer.send(RecreateMazeEvent { floor: 1 }); event_writer.send(RecreateMazeEvent { floor: 1 });
} }
if let Some(mut event_writer) = world.get_resource_mut::<Events<RespawnPlayer>>() {
event_writer.send(RespawnPlayer);
}
} }
} }
}); });
@ -82,9 +86,9 @@ fn add_position_control(ui: &mut Ui, label: &str, pos: &mut Hex) -> bool {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.label(label); ui.label(label);
let response_x = ui.add(DragValue::new(&mut pos.x).speed(1).prefix("x: ")); let response_q = ui.add(DragValue::new(&mut pos.x).speed(1).prefix("q: "));
let response_y = ui.add(DragValue::new(&mut pos.y).speed(1).prefix("y: ")); let response_r = ui.add(DragValue::new(&mut pos.y).speed(1).prefix("r: "));
changed = response_x.changed() || response_y.changed(); changed = response_r.changed() || response_q.changed();
}); });
changed changed
} }

View File

@ -7,12 +7,11 @@ mod resources;
mod systems; mod systems;
pub use resources::{MazeConfig, MazePluginLoaded}; pub use resources::{MazeConfig, MazePluginLoaded};
use systems::recreation::handle_maze_recreation_event;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.init_resource::<MazeConfig>() app.init_resource::<MazeConfig>()
.add_event::<RecreateMazeEvent>() .add_event::<RecreateMazeEvent>()
.add_systems(Update, handle_maze_recreation_event); .add_plugins(systems::plugin);
} }
pub fn spawn_level_command(world: &mut World) { pub fn spawn_level_command(world: &mut World) {

View File

@ -1,28 +0,0 @@
use bevy::{
ecs::{system::RunSystemOnce, world::Command},
prelude::*,
};
use super::{
events::RecreateMazeEvent,
systems::{self, recreation::handle_maze_recreation_event},
MazeConfig, MazePluginLoaded,
};
#[derive(Default)]
pub(crate) struct MazePlugin;
impl Plugin for MazePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<MazeConfig>()
.add_event::<RecreateMazeEvent>()
.add_systems(Update, handle_maze_recreation_event);
}
}
impl Command for MazePlugin {
fn apply(self, world: &mut World) {
world.insert_resource(MazePluginLoaded);
let _ = world.run_system_once(systems::setup::setup);
}
}

View File

@ -40,8 +40,8 @@ impl MazeConfig {
let start_pos = generate_pos(radius, &mut rng)?; let start_pos = generate_pos(radius, &mut rng)?;
let end_pos = generate_pos(radius, &mut rng)?; let end_pos = generate_pos(radius, &mut rng)?;
debug!("Start pos: ({},{})", start_pos.x, start_pos.y); info!("Start pos: (q={}, r={})", start_pos.x, start_pos.y);
debug!("End pos: ({},{})", end_pos.x, end_pos.y); info!("End pos: (q={}, r={})", end_pos.x, end_pos.y);
let layout = HexLayout { let layout = HexLayout {
orientation, orientation,

View File

@ -1,3 +1,14 @@
use crate::maze::components::Floor;
use bevy::prelude::*; use bevy::prelude::*;
use crate::maze::components::MazeFloor; pub(crate) fn despawn_floor(
commands: &mut Commands,
query: &Query<(Entity, &Floor)>,
floor_num: u8,
) {
for (entity, floor) in query.iter() {
if floor.0 == floor_num {
commands.entity(entity).despawn_recursive();
}
}
}

View File

@ -1,3 +1,11 @@
pub mod despawn;
pub mod recreation; pub mod recreation;
pub mod setup; pub mod setup;
mod spawn; pub mod spawn;
use bevy::prelude::*;
use recreation::recreate_maze;
pub(super) fn plugin(app: &mut App) {
app.add_systems(Update, recreate_maze);
}

View File

@ -2,9 +2,9 @@ use bevy::prelude::*;
use crate::maze::{components::Floor, events::RecreateMazeEvent, MazeConfig}; use crate::maze::{components::Floor, events::RecreateMazeEvent, MazeConfig};
use super::setup::setup_maze; use super::{despawn::despawn_floor, spawn::spawn_floor};
pub(crate) fn handle_maze_recreation_event( pub(crate) fn recreate_maze(
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
@ -14,14 +14,6 @@ pub(crate) fn handle_maze_recreation_event(
) { ) {
for event in event_reader.read() { for event in event_reader.read() {
despawn_floor(&mut commands, &query, event.floor); despawn_floor(&mut commands, &query, event.floor);
setup_maze(&mut commands, &mut meshes, &mut materials, &config); spawn_floor(&mut commands, &mut meshes, &mut materials, &config);
}
}
fn despawn_floor(commands: &mut Commands, query: &Query<(Entity, &Floor)>, floor_num: u8) {
for (entity, floor) in query.iter() {
if floor.0 == floor_num {
commands.entity(entity).despawn_recursive();
}
} }
} }

View File

@ -1,13 +1,6 @@
use super::spawn::spawn_floor;
use crate::maze::MazeConfig;
use bevy::prelude::*; use bevy::prelude::*;
use hexlab::{GeneratorType, MazeBuilder};
use crate::maze::{
assets::MazeAssets,
components::{Floor, Maze},
MazeConfig,
};
use super::spawn::spawn_single_hex_tile;
pub(crate) fn setup( pub(crate) fn setup(
mut commands: Commands, mut commands: Commands,
@ -15,34 +8,5 @@ pub(crate) fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
config: Res<MazeConfig>, config: Res<MazeConfig>,
) { ) {
setup_maze(&mut commands, &mut meshes, &mut materials, &config); spawn_floor(&mut commands, &mut meshes, &mut materials, &config);
}
pub(super) fn setup_maze(
commands: &mut Commands,
meshes: &mut ResMut<Assets<Mesh>>,
materials: &mut ResMut<Assets<StandardMaterial>>,
config: &MazeConfig,
) {
let maze = MazeBuilder::new()
.with_radius(config.radius)
.with_seed(config.seed)
.with_generator(GeneratorType::RecursiveBacktracking)
.build()
.expect("Something went wrong while creating maze");
let assets = MazeAssets::new(meshes, materials, config);
commands
.spawn((
Name::new("Floor"),
Maze(maze.clone()),
Floor(1),
Transform::from_translation(Vec3::ZERO),
Visibility::Visible,
))
.with_children(|parent| {
for tile in maze.values() {
spawn_single_hex_tile(parent, &assets, tile, config)
}
});
} }

View File

@ -1,14 +1,41 @@
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6}; use crate::maze::{
assets::MazeAssets,
components::{Floor, Maze, Tile, Wall},
MazeConfig,
};
use bevy::prelude::*; use bevy::prelude::*;
use hexlab::prelude::*; use hexlab::prelude::*;
use hexx::HexOrientation; use hexx::HexOrientation;
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6};
use crate::maze::{ pub(super) fn spawn_floor(
assets::MazeAssets, commands: &mut Commands,
components::{Tile, Wall}, meshes: &mut ResMut<Assets<Mesh>>,
MazeConfig, materials: &mut ResMut<Assets<StandardMaterial>>,
}; config: &MazeConfig,
) {
let maze = MazeBuilder::new()
.with_radius(config.radius)
.with_seed(config.seed)
.with_generator(GeneratorType::RecursiveBacktracking)
.build()
.expect("Something went wrong while creating maze");
let assets = MazeAssets::new(meshes, materials, config);
commands
.spawn((
Name::new("Floor"),
Maze(maze.clone()),
Floor(1),
Transform::from_translation(Vec3::ZERO),
Visibility::Visible,
))
.with_children(|parent| {
for tile in maze.values() {
spawn_single_hex_tile(parent, &assets, tile, config)
}
});
}
pub(super) fn spawn_single_hex_tile( pub(super) fn spawn_single_hex_tile(
parent: &mut ChildBuilder, parent: &mut ChildBuilder,

View File

@ -16,7 +16,7 @@ pub struct MovementSpeed(pub f32);
impl Default for MovementSpeed { impl Default for MovementSpeed {
fn default() -> Self { fn default() -> Self {
Self(50.) Self(100.)
} }
} }

4
src/player/events.rs Normal file
View File

@ -0,0 +1,4 @@
use bevy::prelude::*;
#[derive(Debug, Event)]
pub(crate) struct RespawnPlayer;

View File

@ -1,14 +1,18 @@
mod assets; mod assets;
pub mod components; pub mod components;
pub mod events;
mod systems; mod systems;
use bevy::{ecs::system::RunSystemOnce, prelude::*}; use bevy::{ecs::system::RunSystemOnce, prelude::*};
use components::Player; use components::Player;
use events::RespawnPlayer;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.register_type::<Player>().add_plugins(systems::plugin); app.register_type::<Player>()
.add_event::<RespawnPlayer>()
.add_plugins(systems::plugin);
} }
pub fn spawn_player_command(world: &mut World) { pub fn spawn_player_command(world: &mut World) {
let _ = world.run_system_once(systems::spawn::spawn_player); let _ = world.run_system_once(systems::setup::setup);
} }

View File

@ -0,0 +1,9 @@
use bevy::prelude::*;
use crate::player::components::Player;
pub(crate) fn despawn_players(commands: &mut Commands, query: &Query<Entity, With<Player>>) {
for entity in query.iter() {
commands.entity(entity).despawn_recursive();
}
}

View File

@ -1,11 +1,22 @@
pub mod despawn;
mod input; mod input;
mod movement; mod movement;
pub mod respawn;
pub mod setup;
pub mod spawn; pub mod spawn;
use bevy::prelude::*; use bevy::prelude::*;
use input::player_input; use input::player_input;
use movement::player_movement; use movement::player_movement;
use respawn::respawn_player;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.add_systems(Update, (player_input, player_movement.after(player_input))); app.add_systems(
Update,
(
player_input,
player_movement.after(player_input),
respawn_player,
),
);
} }

View File

@ -0,0 +1,21 @@
use crate::{
maze::MazeConfig,
player::{components::Player, events::RespawnPlayer},
};
use bevy::prelude::*;
use super::{despawn::despawn_players, spawn::spawn_player};
pub(crate) fn respawn_player(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
config: Res<MazeConfig>,
query: Query<Entity, With<Player>>,
mut event_reader: EventReader<RespawnPlayer>,
) {
for _ in event_reader.read() {
despawn_players(&mut commands, &query);
spawn_player(&mut commands, &mut meshes, &mut materials, &config);
}
}

View File

@ -0,0 +1,14 @@
use bevy::prelude::*;
use crate::maze::MazeConfig;
use super::spawn::spawn_player;
pub(crate) fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
config: Res<MazeConfig>,
) {
spawn_player(&mut commands, &mut meshes, &mut materials, &config);
}

View File

@ -6,26 +6,26 @@ use crate::{
}, },
}; };
use bevy::prelude::*; use bevy::prelude::*;
use hexx::Hex;
pub fn spawn_player( pub(super) fn spawn_player(
mut commands: Commands, commands: &mut Commands,
mut meshes: ResMut<Assets<Mesh>>, meshes: &mut ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, materials: &mut ResMut<Assets<StandardMaterial>>,
maze_config: Res<MazeConfig>, config: &MazeConfig,
) { ) {
let player_height = maze_config.height * 0.5; let player_radius = config.hex_size * 0.5;
let player_radius = maze_config.hex_size * 0.5; let player_height = player_radius * 3.5;
let start_hex = Hex::new(1, 1); let y_offset = config.height / 2. + player_height / 1.3;
let start_pos = maze_config.layout.hex_to_world_pos(start_hex);
let start_pos = config.layout.hex_to_world_pos(config.start_pos);
commands.spawn(( commands.spawn((
Name::new("Player"), Name::new("Player"),
Player, Player,
CurrentPosition(start_hex), CurrentPosition(config.start_pos),
Mesh3d(meshes.add(generate_pill_mesh(player_radius, player_height / 2.))), Mesh3d(meshes.add(generate_pill_mesh(player_radius, player_height / 2.))),
MeshMaterial3d(materials.add(blue_material())), MeshMaterial3d(materials.add(blue_material())),
Transform::from_xyz(start_pos.x, player_height * 2., start_pos.y), Transform::from_xyz(start_pos.x, y_offset, start_pos.y),
)); ));
} }