fix(maze): tile wall orientation

This commit is contained in:
Kristofers Solo 2024-11-11 17:21:36 +02:00
parent 41a6059912
commit 34f85be4ef
9 changed files with 236 additions and 564 deletions

445
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[package]
name = "maze-ascension"
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
version = "0.0.5"
version = "0.1.0"
edition = "2021"
[dependencies]
@ -18,7 +18,7 @@ tracing = { version = "0.1", features = [
"release_max_level_warn",
] }
hexx = { version = "0.18", features = ["bevy_reflect", "grid"] }
bevy_prototype_lyon = "0.12"
hexlab = { version = "0.1", features = ["bevy"] }
bevy-inspector-egui = { version = "0.27", optional = true }
@ -31,7 +31,7 @@ dev = [
# Improve compile times for dev builds by linking Bevy as a dynamic library.
"bevy/dynamic_linking",
"bevy/bevy_dev_tools",
"bevy-inspector-egui",
"dep:bevy-inspector-egui",
]
dev_native = [
"dev",

View File

@ -91,7 +91,7 @@ fn spawn_camera(mut commands: Commands) {
commands.spawn((
Name::new("Camera"),
Camera3dBundle {
transform: Transform::from_xyz(0., 300., 300.).looking_at(Vec3::ZERO, Vec3::Y),
transform: Transform::from_xyz(200., 200., 0.).looking_at(Vec3::ZERO, Vec3::Y),
..default()
},
// Render all UI to this camera.

View File

@ -1,220 +0,0 @@
use bevy::{
color::palettes::css::{BLACK, GREEN, RED},
pbr::wireframe::{WireframeConfig, WireframePlugin},
prelude::*,
utils::hashbrown::HashMap,
};
use bevy_prototype_lyon::{
draw::{Fill, Stroke},
entity::ShapeBundle,
path::PathBuilder,
plugin::ShapePlugin,
};
use hexx::{EdgeDirection, Hex};
use rand::{prelude::SliceRandom, rngs::ThreadRng, thread_rng};
use super::{
resource::{Layout, MazeConfig, HEX_SIZE},
tile::{Tile, TileBundle, Walls},
};
pub(super) fn plugin(app: &mut App) {
app.add_plugins((ShapePlugin, WireframePlugin));
app.init_resource::<MazeConfig>();
app.init_resource::<Layout>();
app.insert_resource(WireframeConfig {
global: false,
..default()
});
}
pub(super) fn _spawn_hex_grid(mut commands: Commands, config: Res<MazeConfig>) {
let radius = config.radius as i32;
for q in -radius..=radius {
let r1 = (-radius).max(-q - radius);
let r2 = radius.min(-q + radius);
for r in r1..=r2 {
let tile = Tile::new(q, r);
commands.spawn((
Name::new(format!("Tile {}", &tile.to_string())),
TileBundle {
hex: tile,
..default()
},
));
}
}
}
pub(super) fn _generate_maze(
mut commands: Commands,
query: Query<(Entity, &Tile, &Walls)>,
config: Res<MazeConfig>,
) {
let mut tiles = query
.into_iter()
.map(|(entity, tile, walls)| (tile.hex, (entity, tile.clone(), walls.clone())))
.collect();
let mut rng = thread_rng();
_recursive_maze(&mut tiles, config.start_pos, &mut rng);
for (entity, tile, walls) in tiles.values() {
commands
.entity(*entity)
.insert(tile.clone())
.insert(walls.clone());
}
}
fn _recursive_maze(
tiles: &mut HashMap<Hex, (Entity, Tile, Walls)>,
current_hex: Hex,
rng: &mut ThreadRng,
) {
{
let (_, tile, _) = tiles.get_mut(&current_hex).unwrap();
tile._visit();
}
let mut directions = EdgeDirection::ALL_DIRECTIONS;
directions.shuffle(rng);
for direction in directions.into_iter() {
let neighbor_hex = current_hex + direction;
if let Some((_, neighbor_tile, _)) = tiles.get(&neighbor_hex) {
if !neighbor_tile.visited {
_remove_wall_between(tiles, current_hex, neighbor_hex, direction);
_recursive_maze(tiles, neighbor_hex, rng);
}
}
}
}
fn _remove_wall_between(
tiles: &mut HashMap<Hex, (Entity, Tile, Walls)>,
current_hex: Hex,
neighbor_hex: Hex,
direction: EdgeDirection,
) {
{
let (_, _, walls) = tiles.get_mut(&current_hex).unwrap();
walls.0[direction.index() as usize] = false;
}
{
let (_, _, walls) = tiles.get_mut(&neighbor_hex).unwrap();
walls.0[direction.const_neg().index() as usize] = false;
}
}
fn _add_hex_tile(
commands: &mut Commands,
position: Vec3,
size: f32,
tile: &Tile,
walls: &Walls,
fill_color: Color,
layout: &Layout,
) {
let hex_points = tile
.hex
.all_vertices()
.into_iter()
.map(|v| {
let mut layout = layout.clone();
layout.origin = position.xy();
layout.hex_size = Vec2::splat(size);
layout.hex_to_world_pos(v.origin + v.direction)
})
.collect::<Vec<Vec2>>();
let mut path_builder = PathBuilder::new();
path_builder.move_to(hex_points[0]);
for point in &hex_points[1..] {
path_builder.line_to(*point);
}
path_builder.close();
let hexagon = path_builder.build();
// Create the hexagon fill
commands
.spawn((
ShapeBundle {
path: hexagon,
spatial: SpatialBundle {
transform: Transform::from_xyz(position.x, position.y, 0.),
..default()
},
..default()
},
Fill::color(fill_color),
))
.with_children(|p| {
p.spawn(Text2dBundle {
text: Text {
sections: vec![TextSection {
value: tile.to_string(),
style: TextStyle {
font_size: 16.,
color: Color::BLACK,
..default()
},
}],
..default()
},
transform: Transform::from_xyz(position.x * 2., position.y * 2., 1.),
..default()
});
});
// Draw walls
for direction in EdgeDirection::iter() {
let idx = direction.index() as usize;
if walls[idx] {
let start = hex_points[idx];
let end = hex_points[(idx + 1) % 6];
let mut line_builder = PathBuilder::new();
line_builder.move_to(start);
line_builder.line_to(end);
let line = line_builder.build();
commands.spawn((
ShapeBundle {
path: line,
spatial: SpatialBundle {
transform: Transform::from_xyz(position.x, position.y, 1.),
..default()
},
..default()
},
Stroke::new(BLACK, 2.),
));
}
}
}
pub(super) fn _render_maze(
mut commands: Commands,
query: Query<(&Tile, &mut Walls)>,
layout: Res<Layout>,
config: Res<MazeConfig>,
) {
for (tile, walls) in query.iter() {
let world_pos = layout.hex_to_world_pos(tile.hex).extend(0.);
let fill_color = match tile.hex {
pos if pos == config.start_pos => GREEN.into(),
pos if pos == config.end_pos => RED.into(),
_ => Color::srgb(0.8, 0.8, 0.8),
};
_add_hex_tile(
&mut commands,
world_pos,
HEX_SIZE,
tile,
walls,
fill_color,
&layout,
);
}
}

View File

@ -1,10 +1,8 @@
use bevy::{ecs::world::Command, prelude::*};
use plugin::MazePlugin;
pub mod grid;
pub mod plugin;
pub mod prism;
pub mod resource;
pub mod tile;
pub fn spawn_grid(world: &mut World) {
MazePlugin.apply(world);

View File

@ -3,7 +3,7 @@ use bevy::{
prelude::*,
};
use super::{grid, prism};
use super::prism;
#[derive(Default)]
pub(crate) struct MazePlugin;
@ -11,7 +11,7 @@ pub(crate) struct MazePlugin;
impl Plugin for MazePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(prism::plugin);
app.add_plugins(grid::plugin);
// app.add_plugins(grid::plugin);
// app.insert_resource(AmbientLight {
// brightness: f32::MAX,
// color: Color::WHITE,

View File

@ -1,13 +1,14 @@
use super::resource::{Layout, MazeConfig, HEX_SIZE};
use bevy::prelude::*;
use core::f32;
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3};
use hexlab::prelude::*;
use hexx::{HexLayout, HexOrientation};
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6};
use super::{
resource::{Layout, MazeConfig, HEX_SIZE},
tile::Tile,
};
pub(super) fn plugin(_app: &mut App) {}
pub(super) fn plugin(app: &mut App) {
app.init_resource::<MazeConfig>();
app.init_resource::<Layout>();
}
const WALL_SIZE: f32 = 1.0;
pub(super) fn setup(
@ -17,10 +18,14 @@ pub(super) fn setup(
config: Res<MazeConfig>,
layout: Res<Layout>,
) {
let radius = config.radius as i32;
let maze = MazeBuilder::new()
.with_radius(config.radius)
// .with_seed(0)
.with_generator(GeneratorType::RecursiveBacktracking)
.build()
.expect("Something went wrong while creating maze");
let assets = create_base_assets(&mut meshes, &mut materials, &config);
// spawn_single_hex_tile(&mut commands, &assets, &config);
commands
.spawn((
Name::new("Floor"),
@ -30,44 +35,47 @@ pub(super) fn setup(
},
))
.with_children(|parent| {
for q in -radius..=radius {
let r1 = (-radius).max(-q - radius);
let r2 = radius.min(-q + radius);
for r in r1..=r2 {
let tile = Tile::new(q, r);
spawn_single_hex_tile(parent, &tile, &layout, &assets, &config);
}
for tile in maze.values() {
spawn_single_hex_tile(parent, &assets, tile, &layout.0, config.height)
}
});
}
fn spawn_single_hex_tile(
parent: &mut ChildBuilder,
tile: &Tile,
layout: &Res<Layout>,
assets: &MazeAssets,
config: &Res<MazeConfig>,
tile: &HexTile,
layout: &HexLayout,
hex_height: f32,
) {
let pos = tile.to_vec3(layout);
let world_pos = tile.to_vec3(layout);
let rotation = match layout.orientation {
HexOrientation::Pointy => Quat::from_rotation_y(0.0),
HexOrientation::Flat => Quat::from_rotation_y(FRAC_PI_6), // 30 degrees rotation
};
parent
.spawn((
Name::new(format!("Hex {}", &tile.to_string())),
Name::new(format!("Hex {}", tile.to_string())),
PbrBundle {
mesh: assets.hex_mesh.clone(),
material: assets.hex_material.clone(),
transform: Transform::from_translation(pos),
transform: Transform::from_translation(world_pos).with_rotation(rotation),
..default()
},
))
.with_children(|parent| spawn_walls(parent, assets, config));
.with_children(|parent| spawn_walls(parent, assets, hex_height / 2., &tile.walls()));
}
fn spawn_walls(parent: &mut ChildBuilder, asstets: &MazeAssets, config: &Res<MazeConfig>) {
let y_offset = config.height / 2.;
fn spawn_walls(parent: &mut ChildBuilder, assets: &MazeAssets, y_offset: f32, walls: &Walls) {
let z_rotation = Quat::from_rotation_z(-FRAC_PI_2);
for i in 0..6 {
let wall_angle = FRAC_PI_3 * i as f32;
if !walls.contains(i) {
continue;
}
let wall_angle = -FRAC_PI_3 * i as f32;
let x_offset = (HEX_SIZE - WALL_SIZE) * f32::cos(wall_angle);
let z_offset = (HEX_SIZE - WALL_SIZE) * f32::sin(wall_angle);
@ -76,7 +84,7 @@ fn spawn_walls(parent: &mut ChildBuilder, asstets: &MazeAssets, config: &Res<Maz
let x_rotation = Quat::from_rotation_x(wall_angle + FRAC_PI_2);
let final_rotation = z_rotation * x_rotation;
spawn_single_wall(parent, asstets, final_rotation, pos);
spawn_single_wall(parent, assets, final_rotation, pos);
}
}

View File

@ -16,7 +16,7 @@ pub struct MazeConfig {
impl Default for MazeConfig {
fn default() -> Self {
let mut rng = thread_rng();
let radius = 11;
let radius = 7;
let start_pos = Hex::new(
rng.gen_range(-radius..radius),
rng.gen_range(-radius..radius),
@ -43,7 +43,7 @@ pub struct Layout(pub HexLayout);
impl FromWorld for Layout {
fn from_world(_world: &mut World) -> Self {
Self(HexLayout {
orientation: HexOrientation::Pointy,
orientation: HexOrientation::Flat,
hex_size: Vec2::splat(HEX_SIZE),
..default()
})

View File

@ -1,55 +0,0 @@
use std::fmt::Display;
use bevy::prelude::*;
use hexx::{Hex, HexLayout};
#[derive(Debug, Reflect, Component, Default, PartialEq, Eq, Hash, Clone)]
#[reflect(Component)]
pub struct Tile {
pub hex: Hex,
pub visited: bool,
}
#[derive(Debug, Reflect, Component, Deref, DerefMut, Clone)]
#[reflect(Component)]
pub struct Walls(pub [bool; 6]);
#[derive(Debug, Reflect, Bundle, Default)]
pub struct TileBundle {
pub hex: Tile,
pub walls: Walls,
}
impl Tile {
pub fn new(q: i32, r: i32) -> Self {
Self {
hex: Hex::new(q, r),
visited: false,
}
}
pub fn _visit(&mut self) {
self.visited = true;
}
pub fn to_vec2(&self, layout: &HexLayout) -> Vec2 {
layout.hex_to_world_pos(self.hex)
}
pub fn to_vec3(&self, layout: &HexLayout) -> Vec3 {
let pos = self.to_vec2(layout);
Vec3::new(pos.x, 0., pos.y)
}
}
impl Display for Tile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.hex.x, self.hex.y)
}
}
impl Default for Walls {
fn default() -> Self {
Self([true; 6])
}
}