diff --git a/assets/code/hexlab/generation.rs b/assets/code/hexlab/generation.rs new file mode 100644 index 0000000..c9c78eb --- /dev/null +++ b/assets/code/hexlab/generation.rs @@ -0,0 +1,35 @@ +pub fn generate_backtracking( + maze: &mut HexMaze, + start_pos: Option, + seed: Option, +) { + if maze.is_empty() { + return; + } + let start = start_pos.unwrap_or(Hex::ZERO); + let mut visited = HashSet::new(); + let mut rng: Box = seed.map_or_else( + || Box::new(thread_rng()) as Box, + |seed| Box::new(ChaCha8Rng::seed_from_u64(seed)) as Box, + ); + recursive_backtrack(maze, start, &mut visited, &mut rng); +} + +fn recursive_backtrack( + maze: &mut HexMaze, + current: Hex, + visited: &mut HashSet, + rng: &mut R, +) { + visited.insert(current); + let mut directions = EdgeDirection::ALL_DIRECTIONS; + directions.shuffle(rng); + for direction in directions { + let neighbor = current + direction; + if maze.get_tile(&neighbor).is_some() && !visited.contains(&neighbor) { + maze.remove_tile_wall(¤t, direction); + maze.remove_tile_wall(&neighbor, direction.const_neg()); + recursive_backtrack(maze, neighbor, visited, rng); + } + } +} diff --git a/assets/code/hexlab/structs.rs b/assets/code/hexlab/structs.rs new file mode 100644 index 0000000..af579f1 --- /dev/null +++ b/assets/code/hexlab/structs.rs @@ -0,0 +1,32 @@ +#[derive(Default)] +pub struct MazeBuilder { + radius: Option, + seed: Option, + generator_type: GeneratorType, + start_position: Option, +} + +#[derive(Debug, Clone, Copy, Default)] +pub enum GeneratorType { + #[default] + RecursiveBacktracking, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct HexMaze(HashMap); + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "bevy", derive(Reflect, Component))] +#[cfg_attr(feature = "bevy", reflect(Component))] +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct HexTile { + pub(crate) pos: Hex, + pub(crate) walls: Walls, +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "bevy", derive(Reflect, Component))] +#[cfg_attr(feature = "bevy", reflect(Component))] +pub struct Walls(u8); diff --git a/assets/code/maze-ascension/components.rs b/assets/code/maze-ascension/components.rs new file mode 100644 index 0000000..26b5918 --- /dev/null +++ b/assets/code/maze-ascension/components.rs @@ -0,0 +1,32 @@ +#[derive(Component, Default)] +pub struct Music; + +#[derive(Component, Default)] +pub struct SoundEffect; + +#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Default, Reflect)] +#[reflect(Component)] +pub struct Player; + +#[derive(Component, Reflect)] +#[reflect(Component)] +pub struct MovementController { + pub intent: Vec2, + pub max_speed: f32, +} + +#[derive(Component, Reflect)] +#[reflect(Component)] +pub struct PlayerAnimation { + timer: Timer, + frame: usize, + state: PlayerAnimationState, +} + +#[derive(Component, Debug, Reflect)] +#[reflect(Component)] +pub struct InteractionPalette { + pub none: Color, + pub hovered: Color, + pub pressed: Color, +} diff --git a/assets/code/maze-ascension/maze_generation.rs b/assets/code/maze-ascension/maze_generation.rs new file mode 100644 index 0000000..51ffa68 --- /dev/null +++ b/assets/code/maze-ascension/maze_generation.rs @@ -0,0 +1,141 @@ +pub(super) fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + config: Res, + layout: Res, +) { + 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); + commands + .spawn(( + Name::new("Floor"), + SpatialBundle { + transform: Transform::from_translation(Vec3::ZERO), + ..default() + }, + )) + .with_children(|parent| { + for tile in maze.values() { + spawn_single_hex_tile( + parent, + &assets, + tile, + &layout.0, + config.height, + ) + } + }); +} + +fn spawn_single_hex_tile( + parent: &mut ChildBuilder, + assets: &MazeAssets, + tile: &HexTile, + layout: &HexLayout, + hex_height: f32, +) { + dbg!(tile); + 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())), + PbrBundle { + mesh: assets.hex_mesh.clone(), + material: assets.hex_material.clone(), + transform: Transform::from_translation(world_pos) + .with_rotation(rotation), + ..default() + }, + )) + .with_children(|parent| { + spawn_walls(parent, assets, hex_height / 2., &tile.walls()) + }); +} + +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 { + 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); + let pos = Vec3::new(x_offset, y_offset, z_offset); + + let x_rotation = Quat::from_rotation_x(wall_angle + FRAC_PI_2); + let final_rotation = z_rotation * x_rotation; + + spawn_single_wall(parent, assets, final_rotation, pos); + } +} + +fn spawn_single_wall( + parent: &mut ChildBuilder, + asstets: &MazeAssets, + rotation: Quat, + offset: Vec3, +) { + parent.spawn(( + Name::new("Wall"), + PbrBundle { + mesh: asstets.wall_mesh.clone(), + material: asstets.wall_material.clone(), + transform: Transform::from_translation(offset) + .with_rotation(rotation), + ..default() + }, + )); +} + +fn create_base_assets( + meshes: &mut ResMut>, + materials: &mut ResMut>, + config: &Res, +) -> MazeAssets { + MazeAssets { + hex_mesh: meshes.add(generate_hex_mesh(HEX_SIZE, config.height)), + wall_mesh: meshes.add(generate_square_mesh(HEX_SIZE)), + hex_material: materials.add(white_material()), + wall_material: materials.add(Color::BLACK), + } +} + +fn generate_hex_mesh(radius: f32, depth: f32) -> Mesh { + let hexagon = RegularPolygon { + sides: 6, + circumcircle: Circle::new(radius), + }; + let prism_shape = Extrusion::new(hexagon, depth); + let rotation = Quat::from_rotation_x(FRAC_PI_2); + + Mesh::from(prism_shape).rotated_by(rotation) +} + +fn generate_square_mesh(depth: f32) -> Mesh { + let square = Rectangle::new(WALL_SIZE, WALL_SIZE); + let rectangular_prism = Extrusion::new(square, depth); + let rotation = Quat::from_rotation_x(FRAC_PI_2); + + Mesh::from(rectangular_prism).rotated_by(rotation) +} diff --git a/assets/code/rustfmt.toml b/assets/code/rustfmt.toml new file mode 100644 index 0000000..df99c69 --- /dev/null +++ b/assets/code/rustfmt.toml @@ -0,0 +1 @@ +max_width = 80 diff --git a/code.typ b/code.typ new file mode 100644 index 0000000..fdd4c77 --- /dev/null +++ b/code.typ @@ -0,0 +1,18 @@ +#import "utils.typ": * + +"hexlab" bibliotēkas datu struktūras un svarīgās funkcijas. + + +#context { + set par.line(numbering: "1") + codeblock("./assets/code/hexlab/structs.rs", "rust") + codeblock("./assets/code/hexlab/generation.rs", "rust") +} + +Spēles datu struktūras un svarīgās funkcijas. + +#context { + set par.line(numbering: "1") + codeblock("./assets/code/maze-ascension/components.rs", "rust") + codeblock("./assets/code/maze-ascension/maze_generation.rs", "rust") +} diff --git a/layout.typ b/layout.typ index fae574b..f55a714 100644 --- a/layout.typ +++ b/layout.typ @@ -31,10 +31,13 @@ paper: "a4", ) set text( - //font: "New Computer Modern", - font: "CMU", size: 12pt, hyphenate: auto, lang: "lv", region: "lv", + font: "Times New Roman", + size: 12pt, + hyphenate: auto, + lang: "lv", + region: "lv", ) - show raw: set text(font: "New Computer Modern Mono") + show raw: set text(font: "JetBrainsMono NF") show math.equation: set text(weight: 400) diff --git a/main.typ b/main.typ index d517e87..61ba5bf 100644 --- a/main.typ +++ b/main.typ @@ -34,12 +34,12 @@ / Spēlētājs: lietotāja ieraksts vienas virtuālās istabas kontekstā. /* Pēdējos gados spēļu izstrādes joma ir piedzīvojusi strauju popularitātes -* pieaugumu, ko veicināja neatkarīgo spēļu skaita pieaugums un jaudīgu spēļu -* dzinēju pieejamība. Starp šiem dzinējiem Bevy izceļas kā mūsdienīgs atvērtā -* koda risinājums, kas izmanto Rust programmēšanas valodu, lai nodrošinātu -* drošību un veiktspēju. Šajā diplomdarbā tiek pētīts Bevy spēļu dzinēja -* potenciāls, izstrādājot minimālistisku labirinta izpētes spēli "Maze -* Ascension". */ + * pieaugumu, ko veicināja neatkarīgo spēļu skaita pieaugums un jaudīgu spēļu + * dzinēju pieejamība. Starp šiem dzinējiem Bevy izceļas kā mūsdienīgs atvērtā + * koda risinājums, kas izmanto Rust programmēšanas valodu, lai nodrošinātu + * drošību un veiktspēju. Šajā diplomdarbā tiek pētīts Bevy spēļu dzinēja + * potenciāls, izstrādājot minimālistisku labirinta izpētes spēli "Maze + * Ascension". */ = Ievads == Nolūks @@ -253,45 +253,71 @@ pienākumi, un tas ietver funkcijas, kas veicina kopējo spēles sistēmu. tablex( columns: (auto, 1fr, auto), /* --- header --- */ - [*Modulis*], [*Funkcija*], [*Identifikators*], + [*Modulis*], + [*Funkcija*], + [*Identifikators*], /* -------------- */ rowspanx(3)[Ievades apstrādes modulis], - [Ievades notikumu apstrāde], [], - [Ievades stāvokļa atjaunināšana], [], - [Ievades validācija], [], + [Ievades notikumu apstrāde], + [], + [Ievades stāvokļa atjaunināšana], + [], + [Ievades validācija], + [], rowspanx(4)[Spēles stāvokļa pārvaldības modulis], - [Spēļu stāvokļa pārvaldība], [], - [Spēles cilpas pārvaldība], [], - [Stāvokļu pāreju apstrāde], [], - [Spēles notikumu apstrāde], [], + [Spēļu stāvokļa pārvaldība], + [], + [Spēles cilpas pārvaldība], + [], + [Stāvokļu pāreju apstrāde], + [], + [Spēles notikumu apstrāde], + [], rowspanx(4)[Spēlētāja modulis], - [Kustības vadība], [], - [Sadursmju apstrāde], [], - [Papildsēju pārvaldība], [], - [Spēlētāju stāvokļa atjaunināšana], [], + [Kustības vadība], + [], + [Sadursmju apstrāde], + [], + [Papildsēju pārvaldība], + [], + [Spēlētāju stāvokļa atjaunināšana], + [], rowspanx(1)[Labirinta ģenerēšanas modulis], - [Labirinta būvētājs], [#link()[LGMF01]], + [Labirinta būvētājs], + [#link()[LGMF01]], rowspanx(5)[Līmeņu pārvaldības modulis], - [Līmeņu ielāde], [], - [Progresa izsekošana], [], - [Pāreju apstrāde], [], - [Stāvokļa saglabāšana], [], - [Stāvokļa ielāde], [], + [Līmeņu ielāde], + [], + [Progresa izsekošana], + [], + [Pāreju apstrāde], + [], + [Stāvokļa saglabāšana], + [], + [Stāvokļa ielāde], + [], rowspanx(4)[Renderēšanas modulis], - [Labirinta renderēšana], [], - [Spēlētāja renderēšana], [], - [Lietotājsaskarnes renderēšana], [], - [Vizuālo efektu renderēšana], [], + [Labirinta renderēšana], + [], + [Spēlētāja renderēšana], + [], + [Lietotājsaskarnes renderēšana], + [], + [Vizuālo efektu renderēšana], + [], rowspanx(3)[Audio modulis], - [Skaņas efektu atskaņošana], [], - [Mūzikas pārvaldība], [], - [Audio stāvokļu apstrāde], [], + [Skaņas efektu atskaņošana], + [], + [Mūzikas pārvaldība], + [], + [Audio stāvokļu apstrāde], + [], ), ) @@ -420,8 +446,8 @@ ir noteiktas, lai nodrošinātu plašu pieejamību, vienlaikus saglabājot veikt == Daļējs funkciju projektējums #todo("pievienot funkciju projektējumu +diagrammas") /* Apraksta svarīgākās, sarežģītākās funkcijas vai sistēmas darbības aspektus; -* obligāti jālieto vismaz 4 dažādi diagrammu veidi, izņemot DPD un lietošanas -* piemēru (use case) diagrammas */ + * obligāti jālieto vismaz 4 dažādi diagrammu veidi, izņemot DPD un lietošanas + * piemēru (use case) diagrammas */ == Saskarņu projektējums #todo("pievienot saskarnes (UI/UX)") /* 5-7 lietotāja saskarnes un to apraksts */ @@ -522,6 +548,7 @@ Versiju specifikācija notiek pēc semantiskās versiju atlases@sem_ver (MAJOR.M ) #heading("Pielikumi", numbering: none) +// #include "code.typ" // #include "doc.typ" diff --git a/utils.typ b/utils.typ index 8fca132..d663752 100644 --- a/utils.typ +++ b/utils.typ @@ -191,3 +191,11 @@ [#author #title Pieejams: #link(link_str) aplūkots #date.display("[day].[month].[year]")] } } + +#let codeblock(filename, lang) = { + raw( + read(filename), + block: true, + lang: lang, + ) +}