diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09dd0f5..556d06f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,25 @@ jobs: - name: Run clippy run: | - cargo clippy -- -D warnings + cargo clippy --all-features -- -D warnings + checks: + name: Checks + strategy: + matrix: + features: ["--no-default-features", "--features avian2d", "--features avian3d", "--features debug-with-gizmos"] + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + + - name: Run clippy + run: | + cargo check ${{ matrix.features }} + tests: name: Tests runs-on: ubuntu-latest diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6a687b2..f6ec892 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ permissions: id-token: write jobs: - deploy: + build: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} @@ -23,45 +23,30 @@ jobs: with: target: wasm32-unknown-unknown - - name: Install wasm-bindgen + - name: install tools run: | - cargo install cargo-quickinstall - cargo quickinstall wasm-bindgen-cli + cargo install wasm-bindgen-cli + sudo apt-get install -y binaryen - name: Build run: | - cargo build --target wasm32-unknown-unknown --release --example moving --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/moving.wasm - - cargo build --target wasm32-unknown-unknown --release --example lines --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/lines.wasm - - cargo build --target wasm32-unknown-unknown --release --example many --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/many.wasm - - cargo build --target wasm32-unknown-unknown --release --example gltf --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/gltf.wasm - - cargo build --target wasm32-unknown-unknown --release --example random_obstacles --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/random_obstacles.wasm - - cargo build --target wasm32-unknown-unknown --release --example auto_navmesh_aabb --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/auto_navmesh_aabb.wasm - - cargo build --target wasm32-unknown-unknown --release --example auto_navmesh_primitive --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/auto_navmesh_primitive.wasm - - cargo build --target wasm32-unknown-unknown --release --example primitive_3d --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/primitive_3d.wasm - - cargo build --target wasm32-unknown-unknown --release --example demo --features bevy/webgl2 - wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/demo.wasm + for example in "auto_navmesh_aabb" "auto_navmesh_primitive" "primitive_3d" "demo" "auto_navmesh_avian2d" "auto_navmesh_avian3d" "many" "lines" "moving" "gltf" "random_obstacles" + do + echo "Building $example" + cargo build --target wasm32-unknown-unknown --release --example $example --features "bevy/webgl2,avian2d,avian3d" + wasm-bindgen --no-typescript --out-dir wasm --target web target/wasm32-unknown-unknown/release/examples/$example.wasm + wasm-opt -Oz wasm/${example}_bg.wasm --output wasm/${example}-opt.wasm + rm wasm/${example}_bg.wasm + mv wasm/${example}-opt.wasm wasm/${example}_bg.wasm + cp wasm/example.html wasm/${example}.html + sed -i'' "s/name-of-example/${example}/g" wasm/${example}.html + done - name: Copy Assets run: cp -r assets wasm/ - name: Copy Screenshots - run: cp screenshots/* wasm/ + run: cp -r screenshots wasm/ - name: Setup Pages uses: actions/configure-pages@v5 @@ -71,6 +56,13 @@ jobs: with: path: "wasm" + deploy: + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 diff --git a/Cargo.toml b/Cargo.toml index 962af30..2943498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,18 @@ categories = ["game-development"] itertools = "0.13" tracing = { version = "0.1", optional = true } +[dependencies.avian2d] +version = "0.1" +features = ["2d", "f32", "parry-f32"] +default-features = false +optional = true + +[dependencies.avian3d] +version = "0.1" +features = ["3d", "f32", "parry-f32"] +default-features = false +optional = true + [dependencies.polyanya] version = "0.7.1" @@ -52,5 +64,10 @@ default = ["debug-with-gizmos"] linuxci = ["bevy/x11"] debug-with-gizmos = ["bevy/bevy_gizmos"] -[profile.dev.package."*"] -opt-level = 3 +[[example]] +name = "auto_navmesh_avian2d" +required-features = ["avian2d"] + +[[example]] +name = "auto_navmesh_avian3d" +required-features = ["avian3d"] diff --git a/examples/auto_navmesh_aabb.rs b/examples/auto_navmesh_aabb.rs index 7578fff..6ea38e3 100644 --- a/examples/auto_navmesh_aabb.rs +++ b/examples/auto_navmesh_aabb.rs @@ -30,6 +30,7 @@ fn main() { DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "Navmesh with Polyanya".to_string(), + fit_canvas_to_parent: true, ..default() }), ..default() diff --git a/examples/auto_navmesh_avian2d.rs b/examples/auto_navmesh_avian2d.rs new file mode 100644 index 0000000..6ee88ce --- /dev/null +++ b/examples/auto_navmesh_avian2d.rs @@ -0,0 +1,225 @@ +use avian2d::{math::*, prelude::*}; +use bevy::{color::palettes, math::vec2, prelude::*, sprite::MaterialMesh2dBundle}; +use rand::Rng; +use vleue_navigator::prelude::*; + +#[derive(Component)] +enum Obstacle { + Peg, + Wall, +} + +fn main() { + App::new() + .add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + title: "Navmesh with Polyanya".to_string(), + fit_canvas_to_parent: true, + ..default() + }), + ..default() + }), + PhysicsPlugins::default().with_length_unit(20.0), + VleueNavigatorPlugin, + NavmeshUpdaterPlugin::::default(), + )) + .insert_resource(ClearColor(Color::srgb(0.05, 0.05, 0.1))) + .insert_resource(Gravity(Vector::NEG_Y * 9.81 * 100.0)) + .add_systems(Startup, setup) + .add_systems( + PreUpdate, + (puck_back_to_start, move_puck, display_puck_path), + ) + .insert_resource(NavMeshesDebug(palettes::tailwind::RED_800.into())) + .run(); +} + +fn setup( + mut commands: Commands, + mut materials: ResMut>, + mut meshes: ResMut>, +) { + commands.spawn(Camera2dBundle::default()); + + let square_sprite = Sprite { + color: Color::srgb(0.7, 0.7, 0.8), + custom_size: Some(Vec2::splat(50.0)), + ..default() + }; + + // Left wall + commands.spawn(( + SpriteBundle { + sprite: square_sprite.clone(), + transform: Transform::from_xyz(-50.0 * 9.5, 0.0, 0.0) + .with_scale(Vec3::new(1.0, 15.0, 1.0)), + ..default() + }, + RigidBody::Static, + Collider::rectangle(50.0, 50.0), + Obstacle::Wall, + )); + // Right wall + commands.spawn(( + SpriteBundle { + sprite: square_sprite, + transform: Transform::from_xyz(50.0 * 9.5, 0.0, 0.0) + .with_scale(Vec3::new(1.0, 15.0, 1.0)), + ..default() + }, + RigidBody::Static, + Collider::rectangle(50.0, 50.0), + Obstacle::Wall, + )); + + let marble_radius = 15.0; + let step = 10; + let marble_mesh = meshes.add(Circle::new(marble_radius)); + let marble_material = materials.add(Color::srgb(0.2, 0.7, 0.9)); + + for x in (-50..50).step_by(step).skip(1) { + for (yi, y) in (-50..50).step_by(step).skip(1).enumerate() { + commands.spawn(( + MaterialMesh2dBundle { + mesh: marble_mesh.clone().into(), + material: marble_material.clone(), + transform: Transform::from_xyz( + (x as f32 + + if yi % 2 == 0 { + -(step as f32 / 4.0) + } else { + step as f32 / 4.0 + } + + rand::thread_rng().gen_range(-1.0..1.0)) + * 9.5, + (y as f32 + rand::thread_rng().gen_range(-1.0..1.0)) * 6.0, + 0.0, + ), + ..default() + }, + RigidBody::Static, + Collider::circle(marble_radius as Scalar), + Obstacle::Peg, + )); + } + } + + let mesh = meshes.add(Circle::new(5.0)); + let material = materials.add(Color::srgb(0.7, 0.9, 0.2)); + for x in (-50..50).step_by(5).skip(1) { + let start = Vec3::new(x as f32 * 9.5, 300.0, 0.0); + commands.spawn(( + MaterialMesh2dBundle { + mesh: mesh.clone().into(), + material: material.clone(), + transform: Transform::from_translation(start), + ..default() + }, + RigidBody::Dynamic, + LinearVelocity( + Vec2::new( + rand::thread_rng().gen_range(-1.0..1.0), + rand::thread_rng().gen_range(-1.0..1.0), + ) + .normalize() + * 200.0, + ), + Collider::circle(5.0 as Scalar), + Puck(start), + )); + } + + commands.spawn(NavMeshBundle { + settings: NavMeshSettings { + // Define the outer borders of the navmesh. + fixed: Triangulation::from_outer_edges(&vec![ + vec2(-500.0, -500.0), + vec2(500.0, -500.0), + vec2(500.0, 500.0), + vec2(-500.0, 500.0), + ]), + ..default() + }, + update_mode: NavMeshUpdateMode::Direct, + ..default() + }); +} + +#[derive(Component)] +struct Puck(Vec3); + +fn puck_back_to_start( + mut commands: Commands, + query: Query<(Entity, Ref, &Puck), Without>, + navmeshes: Res>, + navmesh: Query<&Handle>, +) { + let Some(navmesh) = navmeshes.get(navmesh.single()) else { + return; + }; + + for (entity, transform, puck) in query.iter() { + if transform.translation.y < -300.0 { + let Some(path) = navmesh.transformed_path(transform.translation, puck.0) else { + continue; + }; + + if let Some((first, remaining)) = path.path.split_first() { + let mut remaining = remaining.to_vec(); + remaining.reverse(); + + commands.entity(entity).insert(( + RigidBody::Static, + Path { + current: first.clone(), + next: remaining, + }, + )); + } + } + } +} + +#[derive(Component)] +pub struct Path { + current: Vec3, + next: Vec, +} + +pub fn move_puck( + mut commands: Commands, + mut navigator: Query<(&mut Transform, &mut Path, Entity, &mut LinearVelocity)>, + time: Res