diff --git a/.gitattributes b/.gitattributes index 74b6958..e40cfc3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.png filter=lfs diff=lfs merge=lfs -text *.ttf filter=lfs diff=lfs merge=lfs -text +*.xcf filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 76de216..35a5df6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -2,99 +2,44 @@ name: CI on: push: - branches: [main] + branches: [ main ] pull_request: - branches: [main] + branches: [ main ] env: CARGO_TERM_COLOR: always +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: - # Run cargo test test: - name: Test Suite + name: CI runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 + - uses: actions/checkout@v7 with: lfs: 'true' - - name: Cache - uses: actions/cache@v2 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.toml') }} - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - name: Install Dependencies - run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev - - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test - # Run cargo clippy -- -D warnings - clippy_check: - name: Clippy - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - with: - lfs: 'true' - - name: Cache - uses: actions/cache@v2 + - uses: dtolnay/rust-toolchain@stable with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.toml') }} - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - components: clippy - override: true + components: clippy, rustfmt + targets: wasm32-unknown-unknown + - uses: Swatinem/rust-cache@v2 + - name: Install Dependencies - run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev - - name: Run clippy - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: -- -D warnings + run: sudo apt-get update && sudo apt-get install -y g++ pkg-config libx11-dev libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev - # Run cargo fmt --all -- --check - format: - name: Format - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - with: - lfs: 'true' - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - components: rustfmt - override: true - - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check \ No newline at end of file + - name: Fmt + run: cargo fmt --all --check + - name: Clippy + run: cargo clippy --all-targets -- -D warnings + - name: Test + run: cargo test + - name: Wasm Test + run: cargo check --target wasm32-unknown-unknown diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 82c7b06..907f798 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -4,166 +4,46 @@ on: push: tags: - '*' + workflow_dispatch: env: - # update with the name of the main binary binary: nothing-moves +permissions: + contents: write jobs: - # Build for wasm release-wasm: runs-on: ubuntu-latest steps: - - uses: little-core-labs/get-git-tag@v3.0.1 - id: get_version - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 with: lfs: 'true' - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable target: wasm32-unknown-unknown - override: true - - name: install trunk - run: | - cargo install trunk - - name: Build - run: | - trunk build --release - - name: Prepare package - run: | - cp -r assets dist/ - - name: Package as a zip - uses: vimtor/action-zip@v1 - with: - files: dist - dest: ${{ env.binary }}.zip - - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.binary }}.zip - asset_name: ${{ env.binary }}-wasm-${{ steps.get_version.outputs.tag }}.zip - tag: ${{ github.ref }} - overwrite: true + - uses: Swatinem/rust-cache@v2 + - uses: jetli/trunk-action@v0.5.1 - # Build for Linux - release-linux: - runs-on: ubuntu-latest - - steps: - - uses: little-core-labs/get-git-tag@v3.0.1 - id: get_version - - uses: actions/checkout@v2 - with: - lfs: 'true' - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - - name: install dependencies - run: | - sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev - name: Build - run: | - cargo build --release --target x86_64-unknown-linux-gnu - - name: Prepare package - run: | - mkdir linux - cp target/x86_64-unknown-linux-gnu/release/${{ env.binary }} linux/ - cp -r assets linux/ + run: trunk build --release - name: Package as a zip - uses: vimtor/action-zip@v1 - with: - files: linux - dest: ${{ env.binary }}.zip - - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.binary }}.zip - asset_name: ${{ env.binary }}-linux-${{ steps.get_version.outputs.tag }}.zip - tag: ${{ github.ref }} - overwrite: true - - # Build for Windows - release-windows: - runs-on: windows-latest - - steps: - - uses: little-core-labs/get-git-tag@v3.0.1 - id: get_version - - uses: actions/checkout@v2 - with: - lfs: 'true' - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-pc-windows-msvc - override: true + run: zip -r ${{ env.binary }}.zip dist - - name: Build - run: | - cargo build --release --target x86_64-pc-windows-msvc - - name: Prepare package - run: | - mkdir windows - cp target/x86_64-pc-windows-msvc/release/${{ env.binary }}.exe windows/ - cp -r assets windows/ - - name: Package as a zip - uses: vimtor/action-zip@v1 + - name: Upload binaries to build + uses: actions/upload-artifact@v4 with: - files: windows - dest: ${{ env.binary }}.zip + name: ${{ env.binary }}-wasm + path: dist - name: Upload binaries to release + if: startsWith(github.ref, 'refs/tags/') uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} file: ${{ env.binary }}.zip - asset_name: ${{ env.binary }}-windows-${{ steps.get_version.outputs.tag }}.zip + asset_name: ${{ env.binary }}-wasm-${{ github.ref_name }}.zip tag: ${{ github.ref }} overwrite: true - - # Build for macOS - release-macos: - runs-on: macOS-latest - - steps: - - uses: little-core-labs/get-git-tag@v3.0.1 - id: get_version - - uses: actions/checkout@v2 - with: - lfs: 'true' - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-apple-darwin - override: true - - name: Environment Setup - run: | - export CFLAGS="-fno-stack-check" - export MACOSX_DEPLOYMENT_TARGET="10.9" - - name: Build - run: | - cargo build --release --target x86_64-apple-darwin - - name: Prepare Package - run: | - mkdir -p ${{ env.binary }}.app/Contents/MacOS - cp target/x86_64-apple-darwin/release/${{ env.binary }} ${{ env.binary }}.app/Contents/MacOS/ - cp -r assets ${{ env.binary }}.app/Contents/MacOS/ - hdiutil create -fs HFS+ -volname "${{ env.binary }}" -srcfolder ${{ env.binary }}.app ${{ env.binary }}.dmg - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.binary }}.dmg - asset_name: ${{ env.binary }}-macos-${{ steps.get_version.outputs.tag }}.dmg - tag: ${{ github.ref }} - overwrite: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 40ce561..2b1cd31 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ Cargo.lock /target /dist + +# RustRover +.idea diff --git a/Cargo.toml b/Cargo.toml index af59c92..6305fc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,9 @@ name = "nothing-moves" version = "0.1.0" [dependencies] -bevy = "0.7" -rand = "0.8" +bevy = "*" +rand = "*" +bevy-inspector-egui = "*" # Enable only a small amount of optimization in debug mode [profile.dev] @@ -15,3 +16,19 @@ opt-level = 1 # Enable high optimizations for dependencies (incl. Bevy), but not for our code: [profile.dev.package."*"] opt-level = 3 + +[profile.wasm-release] +# Use release profile as default values +inherits = "release" + +# Optimize with size in mind, also try "s", sometimes it is better. +# This doesn't increase compilation times compared to -O3, great improvements +opt-level = "z" + +# Do a second optimization pass removing duplicate or unused code from dependencies. +# Slows compile times, marginal improvements +lto = "fat" + +# When building crates, optimize larger chunks at a time +# Slows compile times, marginal improvements +codegen-units = 1 diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 0000000..36598c5 --- /dev/null +++ b/Trunk.toml @@ -0,0 +1,5 @@ +[build] +public_url = "./" + +[serve] +port = 8080 diff --git a/asset_bases/arrow.xcf b/asset_bases/arrow.xcf new file mode 100644 index 0000000..501009a --- /dev/null +++ b/asset_bases/arrow.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bb59464ba36eeb7a604ebbd31f63af002d61654fe2da7f460bf687fad3d3d0b +size 2701 diff --git a/asset_bases/circle_arrow.xcf b/asset_bases/circle_arrow.xcf new file mode 100644 index 0000000..4246842 --- /dev/null +++ b/asset_bases/circle_arrow.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e85ea5ccc8e3920bf41f5497695d3ef3604b5dab4fc9b60f2869b616e394d5d +size 7058 diff --git a/asset_bases/circle_dash_arrow.xcf b/asset_bases/circle_dash_arrow.xcf new file mode 100644 index 0000000..edcef5f --- /dev/null +++ b/asset_bases/circle_dash_arrow.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba116e1d44d9dc1d29189718732081acbb781b8dd775f4c203ed295b12294122 +size 6318 diff --git a/asset_bases/dash_arrow.xcf b/asset_bases/dash_arrow.xcf new file mode 100644 index 0000000..b18e3c0 --- /dev/null +++ b/asset_bases/dash_arrow.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d44edb8e4603aac095049587fbb222c025dcde24fa8a46663cd2ff19b8c13c8b +size 2468 diff --git a/assets/scenes/ui/maze_controls.scn.ron b/assets/scenes/ui/maze_controls.scn.ron new file mode 100644 index 0000000..c296e5d --- /dev/null +++ b/assets/scenes/ui/maze_controls.scn.ron @@ -0,0 +1,12 @@ +( + entities: { + 0: ( + components: { + "bevy_ui::ui_node::Style": (), + "bevy_ui::ui_node::Node": (), + "bevy_ui::ui_node::BackgroundColor": (WHITE), + "bevy_transform::components::transform::Transform": () + } + ) + } +) diff --git a/assets/textures/arrow.png b/assets/textures/arrow.png new file mode 100644 index 0000000..af2633a --- /dev/null +++ b/assets/textures/arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73b4eae4d9e6cb1693b9f501b5cd466159307558353ab8eaaf497e3ff360a9c7 +size 2337 diff --git a/assets/textures/circle_arrow.png b/assets/textures/circle_arrow.png new file mode 100644 index 0000000..1389ae4 --- /dev/null +++ b/assets/textures/circle_arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fac63123788ac28e25720441da14e31a15006d34f2d315451a4fe24f9c4040e +size 4717 diff --git a/assets/textures/circle_arrow_flip.png b/assets/textures/circle_arrow_flip.png new file mode 100644 index 0000000..d7ceb64 --- /dev/null +++ b/assets/textures/circle_arrow_flip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92ca24a455fe7df148fe9e22b592b57f7f0ba53379cfb1bfd8fa62ef4a951faf +size 12935 diff --git a/assets/textures/circle_dash_arrow.png b/assets/textures/circle_dash_arrow.png new file mode 100644 index 0000000..3c6a8a0 --- /dev/null +++ b/assets/textures/circle_dash_arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d826945e1d400351e4e80499599a3e327f817e4e75b2ebe2af8255f18953c8cd +size 4081 diff --git a/assets/textures/circle_dash_arrow_flip.png b/assets/textures/circle_dash_arrow_flip.png new file mode 100644 index 0000000..f60f972 --- /dev/null +++ b/assets/textures/circle_dash_arrow_flip.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0848f8e5c7bc5321d74d3f3b3e433ebebb82233960d4a702f5d41cd50394ee26 +size 11965 diff --git a/assets/textures/dash_arrow.png b/assets/textures/dash_arrow.png new file mode 100644 index 0000000..d6069f3 --- /dev/null +++ b/assets/textures/dash_arrow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99e9e19bbd4ad4ae1ffa4c44b392cf1808428e74b84032abefd6a40b17a1cff6 +size 2987 diff --git a/index.html b/index.html index e69de29..a45bc29 100644 --- a/index.html +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + Nothing Moves + + + +
+
+ + Javascript and support for canvas is required + +
+ + \ No newline at end of file diff --git a/src/assets.rs b/src/assets.rs new file mode 100644 index 0000000..23996ef --- /dev/null +++ b/src/assets.rs @@ -0,0 +1,39 @@ +use bevy::prelude::*; + +#[derive(Resource)] +pub struct CommonAssets { + common_font: Handle, +} + +pub trait CommonSpawnable { + fn spawn_under(self, assets: &CommonAssets, c: &mut ChildSpawnerCommands, bundle: impl Bundle); +} + +impl CommonAssets { + pub fn load_resource(mut c: Commands, assets: Res) { + c.insert_resource(Self::load(assets)) + } + + fn load(assets: Res) -> Self { + Self { + common_font: assets.load("fonts\\UnicaOne-Regular.ttf"), + } + } + + pub fn spawn_common( + &self, + c: &mut ChildSpawnerCommands, + common: impl CommonSpawnable, + bundle: impl Bundle, + ) { + common.spawn_under(self, c, bundle); + } + + pub fn common_text_style(&self) -> TextFont { + TextFont { + font: FontSource::Handle(self.common_font.clone()), + font_size: FontSize::Px(50.0), + ..default() + } + } +} diff --git a/src/level/bundle.rs b/src/level/bundle.rs new file mode 100644 index 0000000..f9f63e8 --- /dev/null +++ b/src/level/bundle.rs @@ -0,0 +1,14 @@ +use super::*; +use bevy::{app::PluginGroupBuilder, prelude::*}; + +pub struct LevelPluginBundle; + +impl PluginGroup for LevelPluginBundle { + fn build(self) -> PluginGroupBuilder { + PluginGroupBuilder::start::() + .add(maze_ui_renderer::MazeUiRendererPlugin) + .add(maze_renderer::MazeRendererPlugin) + .add(input::MazeInputBundle) + .add(loader::MazeLoaderPlugin) + } +} diff --git a/src/level/input.rs b/src/level/input.rs index 885c375..e89b13c 100644 --- a/src/level/input.rs +++ b/src/level/input.rs @@ -3,52 +3,85 @@ use bevy::prelude::*; use super::maze_level::*; use super::maze_level::{Axis, Direction}; -pub fn level_navigation( - level: Option>, - keys: Res>, - mut position_event: EventWriter, - mut axis_event: EventWriter, +pub struct MazeInputBundle; + +impl Plugin for MazeInputBundle { + fn build(&self, app: &mut App) { + app.add_message::() + .add_message::() + .add_systems(OnEnter(crate::AppState::InMaze), initial_events_on_load) + .add_systems( + Update, + level_navigation.run_if(in_state(crate::AppState::InMaze)), + ); + } +} + +#[derive(Clone, Debug, Message)] +pub struct AxisChanged { + pub axis: [u8; 2], + pub previous_axis: [u8; 2], +} + +#[derive(Clone, Debug, Message)] +pub struct PositionChanged { + pub _position: [u8; 2], + pub _previous_position: [u8; 2], +} + +fn initial_events_on_load( + maze: Res, + mut position_changed: MessageWriter, + mut axis_changed: MessageWriter, ) { - if let Some(mut level) = level { - if keys.just_pressed(KeyCode::Q) { - level.shift_axis(Axis::X, Direction::Negative); - axis_event.send(AxisChanged { axis: level.axis() }); - } - if keys.just_pressed(KeyCode::E) { - level.shift_axis(Axis::X, Direction::Positive); - axis_event.send(AxisChanged { axis: level.axis() }); - } - if keys.just_pressed(KeyCode::Z) { - level.shift_axis(Axis::Y, Direction::Negative); - axis_event.send(AxisChanged { axis: level.axis() }); - } - if keys.just_pressed(KeyCode::X) { - level.shift_axis(Axis::Y, Direction::Positive); - axis_event.send(AxisChanged { axis: level.axis() }); - } - if keys.just_pressed(KeyCode::W) { - level.move_pos(Axis::X, Direction::Positive); - position_event.send(PositionChanged { - position: level.pos(), - }); - } - if keys.just_pressed(KeyCode::S) { - level.move_pos(Axis::X, Direction::Negative); - position_event.send(PositionChanged { - position: level.pos(), - }); - } - if keys.just_pressed(KeyCode::D) { - level.move_pos(Axis::Y, Direction::Positive); - position_event.send(PositionChanged { - position: level.pos(), - }); + position_changed.write(PositionChanged { + _position: maze.pos(), + _previous_position: maze.pos(), + }); + axis_changed.write(AxisChanged { + axis: maze.axis(), + previous_axis: maze.axis(), + }); +} + +fn level_navigation( + mut level: ResMut, + keys: Res>, + mut position_event: MessageWriter, + mut axis_event: MessageWriter, +) { + let mut shift_axis = |key: KeyCode, axis: Axis, dir: Direction| { + if keys.just_pressed(key) { + let previous_axis = level.axis(); + level.shift_axis(axis, dir); + let axis = level.axis(); + if previous_axis != axis { + axis_event.write(AxisChanged { + axis, + previous_axis, + }); + } } - if keys.just_pressed(KeyCode::A) { - level.move_pos(Axis::Y, Direction::Negative); - position_event.send(PositionChanged { - position: level.pos(), - }); + }; + shift_axis(KeyCode::KeyQ, Axis::X, Direction::Negative); + shift_axis(KeyCode::KeyE, Axis::X, Direction::Positive); + shift_axis(KeyCode::KeyZ, Axis::Y, Direction::Negative); + shift_axis(KeyCode::KeyX, Axis::Y, Direction::Positive); + let mut shift_position = |key: KeyCode, axis: Axis, dir: Direction| { + if keys.just_pressed(key) { + let previous_position = level.pos(); + level.move_pos(axis, dir); + let position = level.pos(); + if previous_position != position { + position_event.write(PositionChanged { + _position: position, + _previous_position: previous_position, + }); + } } - } + }; + shift_position(KeyCode::KeyW, Axis::X, Direction::Positive); + shift_position(KeyCode::KeyS, Axis::X, Direction::Negative); + shift_position(KeyCode::KeyD, Axis::Y, Direction::Positive); + shift_position(KeyCode::KeyA, Axis::Y, Direction::Negative); } diff --git a/src/level/loader.rs b/src/level/loader.rs index afb7d26..ce9c7d9 100644 --- a/src/level/loader.rs +++ b/src/level/loader.rs @@ -2,12 +2,18 @@ use crate::AppState; use bevy::prelude::*; use rand::prelude::*; -use super::{ - maze_level::{AxisChanged, PositionChanged}, - MazeLevel, -}; +use super::maze_level::MazeLevel; -#[derive(Clone, Debug)] +pub struct MazeLoaderPlugin; + +impl Plugin for MazeLoaderPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Update, level_load_system) + .add_message::(); + } +} + +#[derive(Clone, Debug, Message)] pub struct LoadLevel { pub rng_source: RngSource, pub dimensions: DimensionLength, @@ -38,12 +44,29 @@ impl Default for LoadLevel { } } -pub fn level_load_system( +impl LoadLevel { + pub fn new(d: &[u8]) -> Self { + let dimensions = match d.len() { + 2 => DimensionLength::Two([d[0], d[1]]), + 3 => DimensionLength::Three([d[0], d[1], d[2]]), + 4 => DimensionLength::Four([d[0], d[1], d[2], d[3]]), + 5 => DimensionLength::Five([d[0], d[1], d[2], d[3], d[4]]), + 6 => DimensionLength::Six([d[0], d[1], d[2], d[3], d[4], d[5]]), + _ => panic!("Unexpected dimension length, it must be 2-6."), + }; + Self { + dimensions, + ..default() + } + } +} + +fn level_load_system( mut c: Commands, - mut events: EventReader, - mut app_state: ResMut>, + mut events: MessageReader, + mut app_state: ResMut>, ) { - for level_loader in events.iter() { + for level_loader in events.read() { let mut rng = match level_loader.rng_source { RngSource::Seeded(seed) => StdRng::seed_from_u64(seed), }; @@ -54,71 +77,6 @@ pub fn level_load_system( DimensionLength::Five(lengths) => MazeLevel::new(&lengths, &mut rng), DimensionLength::Six(lengths) => MazeLevel::new(&lengths, &mut rng), }); - app_state.push(AppState::InMaze).unwrap(); - } -} - -pub fn initial_events_on_load( - maze: Res, - mut position_changed: EventWriter, - mut axis_changed: EventWriter, -) { - position_changed.send(PositionChanged { - position: maze.pos(), - }); - axis_changed.send(AxisChanged { axis: maze.axis() }); -} - -pub fn load_maze_assets( - mut c: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -) { - c.insert_resource(MazeAssets { - joint: meshes.add(Mesh::from(shape::Box::new(0.2, 1.0, 0.2))), - wall: meshes.add(Mesh::from(shape::Box::new(0.1, 0.6, 1.0))), - material: materials.add(Color::rgb(0.8, 0.7, 0.6).into()), - }); -} - -pub fn spawn_player( - mut c: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -) { - c.spawn_bundle(PbrBundle { - mesh: meshes.add(Mesh::from(shape::Capsule { - radius: 0.3, - ..default() - })), - material: materials.add(Color::rgb(0.5, 0.5, 0.8).into()), - ..Default::default() - }); -} - -#[derive(Component)] -pub struct MazeAssets { - joint: Handle, - wall: Handle, - material: Handle, -} - -impl MazeAssets { - pub fn wall(&self, transform: Transform) -> PbrBundle { - PbrBundle { - mesh: self.wall.clone(), - material: self.material.clone(), - transform, - ..Default::default() - } - } - - pub fn joint(&self, transform: Transform) -> PbrBundle { - PbrBundle { - mesh: self.joint.clone(), - material: self.material.clone(), - transform, - ..Default::default() - } + app_state.set(AppState::InMaze); } } diff --git a/src/level/maze_level.rs b/src/level/maze_level.rs index f2875b6..3aa515c 100644 --- a/src/level/maze_level.rs +++ b/src/level/maze_level.rs @@ -1,5 +1,7 @@ use std::ops::{Deref, DerefMut}; +use bevy::prelude::Resource; + use crate::maze; struct MazeImpl { @@ -8,16 +10,6 @@ struct MazeImpl { axis: [u8; 2], } -#[derive(Clone, Debug)] -pub struct AxisChanged { - pub axis: [u8; 2], -} - -#[derive(Clone, Debug)] -pub struct PositionChanged { - pub position: [u8; 2], -} - #[derive(PartialEq, Eq, Clone, Copy)] pub enum Axis { X, @@ -53,11 +45,21 @@ pub enum Direction { } impl Direction { + /* fn shift_wrapped(&self, value: u8, limit: u8) -> u8 { (match self { Direction::Positive => value.checked_add(1).unwrap_or(0), - Direction::Negative => value.checked_sub(1).unwrap_or(limit - 2), - } % (limit - 1)) + Direction::Negative => value.checked_sub(1).unwrap_or(limit - 1), + } % limit) + } + */ + + fn shift_clamped(&self, value: u8, limit: u8) -> u8 { + match self { + Direction::Positive => value.checked_add(1).unwrap_or(limit - 1), + Direction::Negative => value.saturating_sub(1), + } + .clamp(0, limit - 1) } } @@ -96,7 +98,7 @@ impl MazeView for MazeImpl { target_axis }; - let new_off_axis = dir.shift_wrapped(linear_current, DIMS as u8); + let new_off_axis = dir.shift_clamped(linear_current, DIMS as u8 - 1); let dest = if new_off_axis >= off_target_axis { new_off_axis + 1 } else { @@ -129,6 +131,13 @@ impl MazeView for MazeImpl { ] } + fn pos_in(&self, axis: [u8; 2]) -> [u8; 2] { + [ + self.position[axis[0] as usize], + self.position[axis[1] as usize], + ] + } + fn move_pos(&mut self, axis: Axis, dir: Direction) { let dim = *axis.get(&self.axis) as usize; if let Some(true) = self.can_move(dim as u8, dir) { @@ -146,11 +155,8 @@ impl MazeView for MazeImpl { let dim = dim as usize; let mut pos = self.position; if dir == Direction::Negative { - if let Some(new_pos) = pos[dim].checked_sub(1) { - pos[dim] = new_pos; - } else { - return None; - } + let new_pos = pos[dim].checked_sub(1)?; + pos[dim] = new_pos; } self.maze.can_move(&pos, dim) } @@ -175,6 +181,7 @@ pub trait MazeView: Sync + Send { fn dims(&self) -> &[u8]; fn pos_limit(&self) -> [u8; 2]; fn pos(&self) -> [u8; 2]; + fn pos_in(&self, axis: [u8; 2]) -> [u8; 2]; fn move_pos(&mut self, axis: Axis, dir: Direction); fn can_move(&self, dim: u8, dir: Direction) -> Option; @@ -182,6 +189,7 @@ pub trait MazeView: Sync + Send { fn wall_in_current(&self, position: [u8; 2], axis: Axis) -> bool; } +#[derive(Resource)] pub struct MazeLevel { inner: Box, } @@ -189,7 +197,7 @@ pub struct MazeLevel { impl Default for MazeLevel { fn default() -> Self { Self { - inner: Box::new(MazeImpl::<2>::default()), + inner: Box::>::default(), } } } diff --git a/src/level/maze_renderer.rs b/src/level/maze_renderer.rs index f6fc42d..2305d14 100644 --- a/src/level/maze_renderer.rs +++ b/src/level/maze_renderer.rs @@ -1,105 +1,231 @@ +use super::maze_level::*; +use bevy::prelude::*; +use std::cmp::Ordering; use std::f32::consts::PI; +use std::time::Duration; -use super::{loader::MazeAssets, maze_level::*}; -use bevy::prelude::*; +pub struct MazeRendererPlugin; + +impl Plugin for MazeRendererPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, load_maze_assets) + .add_systems(OnEnter(crate::AppState::InMaze), spawn_player) + .add_systems( + Update, + ( + maze_level_renderer, + rotate_for_n_update, + remove_after_time::, + remove_after_time::, + update_maze_offset.after(maze_level_renderer), + start_despawn_of_render, + ) + .run_if(in_state(crate::AppState::InMaze)), + ); + } +} -pub fn spawn_maze_root(mut c: Commands) { - c.spawn_bundle(MazeRendererBundle { - renderer: MazeRenderer { last_axis: [0, 0] }, - transform: Default::default(), - global_transform: Default::default(), +fn load_maze_assets( + mut c: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + c.insert_resource(MazeAssets { + joint: meshes.add(Cuboid::new(0.2, 1.0, 0.2)), + wall: meshes.add(Cuboid::new(0.1, 0.6, 1.0)), + material: materials.add(StandardMaterial { + base_color: Color::srgb(0.8, 0.7, 0.6), + ..default() + }), }); } -#[derive(Bundle)] -pub struct MazeRendererBundle { - pub renderer: MazeRenderer, +#[derive(Resource)] +struct MazeAssets { + joint: Handle, + wall: Handle, + material: Handle, +} + +impl MazeAssets { + pub fn wall(&self) -> (Mesh3d, MeshMaterial3d) { + ( + Mesh3d(self.wall.clone()), + MeshMaterial3d(self.material.clone()), + ) + } + + pub fn joint(&self) -> (Mesh3d, MeshMaterial3d) { + ( + Mesh3d(self.joint.clone()), + MeshMaterial3d(self.material.clone()), + ) + } +} + +fn spawn_player( + mut c: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + c.spawn(( + Mesh3d(meshes.add(Mesh::from(Capsule3d { + radius: 0.3, + ..default() + }))), + MeshMaterial3d(materials.add(StandardMaterial::from(Color::srgb(0.5, 0.5, 0.8)))), + Transform::default(), + )); +} + +#[derive(Bundle, Default)] +pub struct MazePositionTrackerBundle { + pub position_tracker: MazePositionTracker, pub transform: Transform, pub global_transform: GlobalTransform, + pub computed_visibility: InheritedVisibility, + pub visibility: Visibility, } -#[derive(Component)] -pub struct MazeRenderer { - last_axis: [u8; 2], +#[derive(Component, Default)] +pub struct MazePositionTracker { + visible_axis: [u8; 2], +} + +fn maze_level_offset(maze: &MazeLevel, axis: [u8; 2]) -> Vec3 { + let p = maze.pos_in(axis); + Vec3::new(-(p[0] as f32), 0.0, -(p[1] as f32)) } -pub fn update_maze_offset( +fn update_maze_offset( level: Res, - mut maze_query: Query<(&MazeRenderer, &mut Transform)>, - mut position_changed: EventReader, - mut axis_changed: EventReader, + time: Res