feat(grid): generate maze

This commit is contained in:
Kristofers Solo 2024-09-22 15:28:07 +03:00
parent 0ee94c826a
commit f16fd51090
5 changed files with 154 additions and 47 deletions

14
src/hex.rs Normal file
View File

@ -0,0 +1,14 @@
use bevy::{color::palettes::css::BLACK, prelude::*};
use bevy_prototype_lyon::{
draw::{Fill, Stroke},
entity::ShapeBundle,
path::PathBuilder,
plugin::ShapePlugin,
};
use rand::{thread_rng, Rng};
pub(super) fn plugin(app: &mut App) {
app.add_plugins(ShapePlugin);
app.add_systems(Startup, setup_system);
}

View File

@ -4,7 +4,7 @@ use hexx::EdgeDirection;
pub(super) fn plugin(_app: &mut App) {} pub(super) fn plugin(_app: &mut App) {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect)]
pub enum HexDirection { pub enum Direction {
Top, Top,
TopRight, TopRight,
BottomRight, BottomRight,
@ -13,12 +13,8 @@ pub enum HexDirection {
TopLeft, TopLeft,
} }
impl HexDirection { impl Direction {
pub fn to_hexx_direction(self) -> EdgeDirection { pub const ALL: [Direction; 6] = [
self.into()
}
pub const ALL: [HexDirection; 6] = [
Self::Top, Self::Top,
Self::TopRight, Self::TopRight,
Self::BottomRight, Self::BottomRight,
@ -38,16 +34,3 @@ impl HexDirection {
} }
} }
} }
impl From<HexDirection> for EdgeDirection {
fn from(value: HexDirection) -> Self {
match value {
HexDirection::Top => Self::FLAT_NORTH,
HexDirection::TopRight => Self::FLAT_NORTH_EAST,
HexDirection::BottomRight => Self::FLAT_SOUTH_EAST,
HexDirection::Bottom => Self::FLAT_SOUTH,
HexDirection::BottomLeft => Self::FLAT_SOUTH_WEST,
HexDirection::TopLeft => Self::FLAT_NORTH_WEST,
}
}
}

View File

@ -1,4 +1,8 @@
use bevy::{color::palettes::css::BLACK, prelude::*}; use bevy::{
color::palettes::css::{BLACK, GREEN, RED},
prelude::*,
utils::hashbrown::HashMap,
};
use bevy_prototype_lyon::{ use bevy_prototype_lyon::{
draw::{Fill, Stroke}, draw::{Fill, Stroke},
@ -6,7 +10,18 @@ use bevy_prototype_lyon::{
path::PathBuilder, path::PathBuilder,
plugin::ShapePlugin, plugin::ShapePlugin,
}; };
use rand::{thread_rng, Rng}; use rand::{prelude::SliceRandom, rngs::ThreadRng, thread_rng};
use super::tile::{AxialCoord, Tile};
const DIRECTIONS: [AxialCoord; 6] = [
AxialCoord { q: 1, r: 0 }, // Right
AxialCoord { q: 1, r: -1 }, // Top-right
AxialCoord { q: 0, r: -1 }, // Top-left
AxialCoord { q: -1, r: 0 }, // Left
AxialCoord { q: -1, r: 1 }, // Bottom-left
AxialCoord { q: 0, r: 1 }, // Bottom-right
];
pub struct HexGrid; pub struct HexGrid;
@ -18,45 +33,45 @@ impl Plugin for HexGrid {
} }
pub(super) fn setup_system(mut commands: Commands) { pub(super) fn setup_system(mut commands: Commands) {
let radius = 5; let radius = 7;
let hex_positions = generate_hex_grix(radius); let mut grid = generate_hex_grix(radius);
let hex_size = 30.; let start_coord = AxialCoord::new(-radius, 0);
let hex_height = hex_size * 2.; let end_coord = AxialCoord::new(radius, 0);
let hex_width = (3.0_f32).sqrt() * hex_size;
for (q, r) in hex_positions {
let x = hex_width * (q as f32 + r as f32 / 2.);
let y = hex_height * (r as f32 * 3. / 4.);
let mut rng = thread_rng(); let mut rng = thread_rng();
let walls: [bool; 6] = [ generate_maze(&mut grid, start_coord, &mut rng);
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
];
add_hex_tile(&mut commands, Vec2::new(x, y), hex_size, walls); render_maze(&mut commands, &mut grid, radius, start_coord, end_coord);
}
} }
fn generate_hex_grix(radius: i32) -> Vec<(i32, i32)> { fn generate_hex_grix(radius: i32) -> HashMap<AxialCoord, Tile> {
let mut positions = Vec::new(); let mut grid = HashMap::new();
for q in -radius..=radius { for q in -radius..=radius {
let r1 = (-radius).max(-q - radius); let r1 = (-radius).max(-q - radius);
let r2 = radius.min(-q + radius); let r2 = radius.min(-q + radius);
for r in r1..=r2 { for r in r1..=r2 {
positions.push((q, r)); let coord = AxialCoord::new(q, r);
let tile = Tile {
position: coord,
walls: [true; 6],
visited: false,
};
grid.insert(coord, tile);
} }
} }
positions grid
} }
fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool; 6]) { fn add_hex_tile(
commands: &mut Commands,
position: Vec2,
size: f32,
walls: [bool; 6],
fill_color: Color,
) {
let hex_points = (0..6) let hex_points = (0..6)
.map(|i| { .map(|i| {
let angle_deg = 60. * i as f32 - 30.; let angle_deg = 60. * i as f32 - 30.;
@ -73,6 +88,7 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool
path_builder.close(); path_builder.close();
let hexagon = path_builder.build(); let hexagon = path_builder.build();
// Create the hexagon fill
commands.spawn(( commands.spawn((
ShapeBundle { ShapeBundle {
path: hexagon, path: hexagon,
@ -82,9 +98,10 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool
}, },
..default() ..default()
}, },
Fill::color(Color::srgb(0.8, 0.8, 0.8)), Fill::color(fill_color),
)); ));
// Draw walls
for i in 0..6 { for i in 0..6 {
if walls[i] { if walls[i] {
let start = hex_points[i]; let start = hex_points[i];
@ -108,3 +125,70 @@ fn add_hex_tile(commands: &mut Commands, position: Vec2, size: f32, walls: [bool
} }
} }
} }
fn generate_maze(
grid: &mut HashMap<AxialCoord, Tile>,
current_coord: AxialCoord,
rng: &mut ThreadRng,
) {
{
let current_tile = grid.get_mut(&current_coord).unwrap();
current_tile.visit();
}
let mut directions = DIRECTIONS.clone();
directions.shuffle(rng);
for (i, direction) in directions.iter().enumerate() {
let neightbor_coord = AxialCoord {
q: current_coord.q + direction.q,
r: current_coord.r + direction.r,
};
if let Some(neightbor_tile) = grid.get(&neightbor_coord) {
if !neightbor_tile.visited {
// Remove the wall between current_tile and neighbor_tile
{
let current_tile = grid.get_mut(&current_coord).unwrap();
current_tile.walls[i] = false;
}
{
let neightbor_tile = grid.get_mut(&neightbor_coord).unwrap();
neightbor_tile.walls[opposite_wall(i)] = false;
}
// Recurse with the neighbor tile
generate_maze(grid, neightbor_coord, rng);
}
}
}
}
fn render_maze(
commands: &mut Commands,
grid: &mut HashMap<AxialCoord, Tile>,
radius: i32,
start_coord: AxialCoord,
end_coord: AxialCoord,
) {
let hex_size = 30.;
let hex_height = hex_size * 2.;
let hex_width = (3.0_f32).sqrt() * hex_size;
for tile in grid.values() {
let (q, r) = (tile.position.q, tile.position.r);
let x = hex_width * (q as f32 + r as f32 / 2.);
let y = hex_height * (r as f32 * 3. / 4.);
let mut fill_color = Color::srgb(0.8, 0.8, 0.8);
if tile.position == start_coord {
fill_color = GREEN.into();
} else if tile.position == end_coord {
fill_color = RED.into();
}
add_hex_tile(commands, Vec2::new(x, y), hex_size, tile.walls, fill_color);
}
}
fn opposite_wall(index: usize) -> usize {
(index + 3) % 6
}

View File

@ -4,7 +4,9 @@ use bevy::{
}; };
use bevy_prototype_lyon::plugin::ShapePlugin; use bevy_prototype_lyon::plugin::ShapePlugin;
use grid::setup_system; use grid::setup_system;
pub mod direction;
pub mod grid; pub mod grid;
pub mod tile;
pub struct HexGrid; pub struct HexGrid;

24
src/hexgrid/tile.rs Normal file
View File

@ -0,0 +1,24 @@
#[derive(Debug, Clone)]
pub struct Tile {
pub position: AxialCoord,
pub walls: [bool; 6],
pub visited: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AxialCoord {
pub q: i32,
pub r: i32,
}
impl Tile {
pub fn visit(&mut self) {
self.visited = true
}
}
impl AxialCoord {
pub fn new(q: i32, r: i32) -> Self {
Self { q, r }
}
}