mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
fix(maze): tile wall orientation
This commit is contained in:
parent
41a6059912
commit
34f85be4ef
445
Cargo.lock
generated
445
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
220
src/maze/grid.rs
220
src/maze/grid.rs
@ -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(¤t_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(¤t_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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
})
|
||||
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user