diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 5139072..46e675a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,7 +24,7 @@ { "type": "cargo", "command": "run", - "args": ["--bin", "minimal"], + "args": ["--bin", "worldspace"], "problemMatcher": [ "$rustc" ], diff --git a/crates/bevy_lunex/src/logic/cursor.rs b/crates/bevy_lunex/src/logic/cursor.rs index b636883..f378368 100644 --- a/crates/bevy_lunex/src/logic/cursor.rs +++ b/crates/bevy_lunex/src/logic/cursor.rs @@ -105,15 +105,12 @@ pub struct CursorBundle { pub cursor: Cursor2d, /// The virtual pointer that the cursor controls pub pointer: PointerBundle, - /// Required for Cursor to exist - pub spatial: SpatialBundle, } impl Default for CursorBundle { fn default() -> Self { Self { cursor: default(), pointer: PointerBundle::new(PointerId::Custom(pointer::Uuid::new_v4())), - spatial: default(), } } } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index eff706b..dc62a35 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,33 +1,33 @@ # Summary -[Introduction](1_overview.md) +[Introduction](overview.md) --- # Getting started -- [Installation](2_installation.md) -- [Quick start](3_quick_start.md) -- [Debug](4_debug.md) +- [Installation](installation.md) +- [Quick start](quick_start.md) +- [Debug](debug.md) --- # Advanced -- [Linking](advanced/0_linking.md) -- [Layouts](advanced/1_layouts.md) -- [Units](advanced/2_units.md) -- [Cursor](advanced/3_cursor.md) -- [Text](advanced/4_text.md) -- [Abstraction](advanced/5_abstraction.md) - - [Components](advanced/abstraction/1_components.md) - - [Routes](advanced/abstraction/2_routes.md) -- [Interactivity](advanced/6_interactivity.md) -- [Animation](advanced/7_animation.md) -- [2D & 3D](advanced/8_2d_and_3d.md) -- [Worldspace UI](advanced/9_worldspace_ui.md) -- [Custom rendering](advanced/10_rendering.md) +- [Linking](advanced/linking.md) +- [Layouts](advanced/layouts.md) +- [Units](advanced/units.md) +- [Cursor](advanced/cursor.md) +- [Text](advanced/text.md) +- [Abstraction](advanced/abstraction.md) + - [Components](advanced/abstraction/components.md) + - [Routes](advanced/abstraction/routes.md) +- [Interactivity](advanced/interactivity.md) +- [Animation](advanced/animation.md) +- [2D & 3D](advanced/2d_and_3d.md) +- [Worldspace UI](advanced/worldspace_ui.md) +- [Custom rendering](advanced/rendering.md) ----------- -[Contributors](5_contributors.md) -[Help](6_help.md) +[Contributors](contributors.md) +[Help](help.md) diff --git a/docs/src/advanced/8_2d_and_3d.md b/docs/src/advanced/2d_and_3d.md similarity index 100% rename from docs/src/advanced/8_2d_and_3d.md rename to docs/src/advanced/2d_and_3d.md diff --git a/docs/src/advanced/3_cursor.md b/docs/src/advanced/3_cursor.md deleted file mode 100644 index c3943f7..0000000 --- a/docs/src/advanced/3_cursor.md +++ /dev/null @@ -1,108 +0,0 @@ -# Cursor - -Lunex provides a custom abstraction for a cursor related features within your Bevy application. - -This is achieved by moving all logic to a an entity that is spawned as a child of a `Camera2d`. - -It is required that you spawn this entity, otherwise picking won't work. - -### Required components - -You need to spawn these components for - -```rust -// This is the main component -Cursor2d::new(), -// This is required so that the sprite doesn't block our picking raycaster -Pickable::IGNORE, -// This is required so that the sprite doesn't block our picking raycaster -PointerBundle::new(PointerId::Custom(pointer::Uuid::new_v4())), -``` - -### Styling components - -If you want to attach custom image to your cursor, you have to attach texture atlas to the entity. -You will need to have all the icons in a image strip like this. - -![Cursor](../images/cursor.png) - - -```rust -// Specify the texture atlas properties -TextureAtlas { - // ... -}, -// Specify the image to load -SpriteBundle { - // ... -}, -``` - -### Gamepad support - -To bind a cursor to a gamepad, you have to add this component: - -```rust -GamepadCursor::new(0), -``` - -If you want the cursor to accept both Mouse and Gamepad inputs, you have to create an additional -system that listens to recent input events and based on them "removes" or "adds" this component. - -Currently, there is only 1 mode supported and that is `Free` which means you just use your stick to move -the cursor around. There is no "jumping" yet. - -However, it is planned to add `Snap` mode, which makes the cursor "jump" and snap to the next node in input direction. - -## Example - -Here's an example of how to set up a custom cursor with gamepad control: - -```rust -# fn setup(mut commands: Commands, assets: Res, mut atlas_layout: ResMut>){ -commands.spawn(( - MainUi, - Camera2dBundle { transform: Transform::from_xyz(0.0, 0.0, 1000.0), ..default() } -)).with_children(|camera| { - - // Spawn 2D camera - commands.spawn(camera()).with_children(|camera| { - - // Spawn cursor - camera.spawn (( - - // Here we can map different native cursor icons to texture atlas indexes and sprite offsets - Cursor2d::new() - .set_index(CursorIcon::Default, 0, (14.0, 14.0)) - .set_index(CursorIcon::Pointer, 1, (10.0, 12.0)) - .set_index(CursorIcon::Grab, 2, (40.0, 40.0)), - - // Here we specify that the cursor should be controlled by gamepad 0 - GamepadCursor::new(0), - - // This is required for picking to work - PointerBundle::new(PointerId::Custom(pointer::Uuid::new_v4())), - - // Add texture atlas to the cursor - TextureAtlas { // Size 80x80, 3 columns, 1 row - layout: atlas_layout.add(TextureAtlasLayout::from_grid(UVec2::splat(80), 3, 1, None, None)), - index: 0, - }, - SpriteBundle { - texture: assets.load("cursor.png"), - transform: Transform { scale: Vec3::new(0.45, 0.45, 1.0), ..default() }, - sprite: Sprite { - color: Color::YELLOW.with_alpha(2.0), - anchor: Anchor::TopLeft, - ..default() - }, - ..default() - }, - - // Make the raycaster ignore this entity, we don't want our cursor to block clicking - Pickable::IGNORE, - )); - }); -}); -# } -``` diff --git a/docs/src/advanced/5_abstraction.md b/docs/src/advanced/abstraction.md similarity index 100% rename from docs/src/advanced/5_abstraction.md rename to docs/src/advanced/abstraction.md diff --git a/docs/src/advanced/abstraction/1_components.md b/docs/src/advanced/abstraction/components.md similarity index 100% rename from docs/src/advanced/abstraction/1_components.md rename to docs/src/advanced/abstraction/components.md diff --git a/docs/src/advanced/abstraction/2_routes.md b/docs/src/advanced/abstraction/routes.md similarity index 100% rename from docs/src/advanced/abstraction/2_routes.md rename to docs/src/advanced/abstraction/routes.md diff --git a/docs/src/advanced/7_animation.md b/docs/src/advanced/animation.md similarity index 100% rename from docs/src/advanced/7_animation.md rename to docs/src/advanced/animation.md diff --git a/docs/src/advanced/cursor.md b/docs/src/advanced/cursor.md new file mode 100644 index 0000000..66b538e --- /dev/null +++ b/docs/src/advanced/cursor.md @@ -0,0 +1,83 @@ +# Cursor + +Lunex provides a custom abstraction for a cursor related features within your Bevy application. +This is achieved by moving all logic to a an entity that is spawned into the world. +It is required that you spawn this entity, otherwise picking won't work. + +### Requirements + +You need to spawn this bundle for native cursor to work + +```rust +commands.spawn( CursorBundle::default() ) +``` + +### Styled cursors + +You can also attach custom image to your cursor entity. First, you will also need to have all the cursor icons in a image strip like this: + +![Cursor](../images/cursor.png) + +Then you can use `StyledCursorBundle` instead of `CursorBundle`. + +```rust +commands.spawn(StyledCursorBundle { + // Put texture atlas and sprite bundle here + ..default() +}) +``` + +Make sure you spawn `StyledCursorBundle` as a child of 2D `Camera`, otherwise the sprite would not follow the view. + +### Gamepad support + +To bind a cursor to a gamepad, you have to add this component: + +```rust +GamepadCursor::new(0), +``` + +If you want the cursor to accept both Mouse and Gamepad inputs, you have to create an additional +system that listens to recent input events and based on them "removes" or "adds" this component. + +Currently, there is only 1 mode supported and that is `Free` which means you just use your stick to move +the cursor around. There is no "jumping" yet. + +However, it is planned to add `Snap` mode, which makes the cursor "jump" and snap to the next node in input direction. + +## Example + +Here's an example of how to set up a custom cursor with gamepad control: + +```rust +// Spawn cursor +camera.spawn (( + StyledCursorBundle { + // Here we can map different native cursor icons to texture atlas indexes and sprite offsets + cursor: Cursor2d::new() + .set_index(CursorIcon::Default, 0, (14.0, 14.0)) + .set_index(CursorIcon::Pointer, 1, (10.0, 12.0)) + .set_index(CursorIcon::Grab, 2, (40.0, 40.0)), + // Add texture atlas to the cursor + atlas: TextureAtlas { + layout: atlas_layout.add(TextureAtlasLayout::from_grid(UVec2::splat(80), 3, 1, None, None)), + index: 0, + }, + // Add sprite strip to the cursor + sprite: SpriteBundle { + texture: assets.load("cursor.png"), + transform: Transform { scale: Vec3::new(0.45, 0.45, 1.0), ..default() }, + sprite: Sprite { + color: Color::YELLOW.with_alpha(2.0), + anchor: Anchor::TopLeft, + ..default() + }, + ..default() + }, + ..default() + }, + + // Here we specify that the cursor should be controlled by gamepad 0 + GamepadCursor::new(0), +)); +``` diff --git a/docs/src/advanced/6_interactivity.md b/docs/src/advanced/interactivity.md similarity index 100% rename from docs/src/advanced/6_interactivity.md rename to docs/src/advanced/interactivity.md diff --git a/docs/src/advanced/1_layouts.md b/docs/src/advanced/layouts.md similarity index 100% rename from docs/src/advanced/1_layouts.md rename to docs/src/advanced/layouts.md diff --git a/docs/src/advanced/0_linking.md b/docs/src/advanced/linking.md similarity index 100% rename from docs/src/advanced/0_linking.md rename to docs/src/advanced/linking.md diff --git a/docs/src/advanced/10_rendering.md b/docs/src/advanced/rendering.md similarity index 100% rename from docs/src/advanced/10_rendering.md rename to docs/src/advanced/rendering.md diff --git a/docs/src/advanced/4_text.md b/docs/src/advanced/text.md similarity index 100% rename from docs/src/advanced/4_text.md rename to docs/src/advanced/text.md diff --git a/docs/src/advanced/2_units.md b/docs/src/advanced/units.md similarity index 100% rename from docs/src/advanced/2_units.md rename to docs/src/advanced/units.md diff --git a/docs/src/advanced/9_worldspace_ui.md b/docs/src/advanced/worldspace_ui.md similarity index 100% rename from docs/src/advanced/9_worldspace_ui.md rename to docs/src/advanced/worldspace_ui.md diff --git a/docs/src/5_contributors.md b/docs/src/contributors.md similarity index 100% rename from docs/src/5_contributors.md rename to docs/src/contributors.md diff --git a/docs/src/4_debug.md b/docs/src/debug.md similarity index 100% rename from docs/src/4_debug.md rename to docs/src/debug.md diff --git a/docs/src/6_help.md b/docs/src/help.md similarity index 84% rename from docs/src/6_help.md rename to docs/src/help.md index 67a04a6..e99aab3 100644 --- a/docs/src/6_help.md +++ b/docs/src/help.md @@ -5,3 +5,4 @@ For issues related to the library, please create a ticket on [`GitHub`](https:// You can also reach out to me on the [`Bevy Discord`](https://discord.gg/bevy), where you can use the [`#Ecosystem-crates: Bevy Lunex`](https://discord.com/channels/691052431525675048/1034543904478998539) thread. For specific questions, feel free to send me a direct message on Discord: `@idedary`. +_Just make sure you are in the Bevy discord or otherwise I will ignore you_. diff --git a/docs/src/2_installation.md b/docs/src/installation.md similarity index 100% rename from docs/src/2_installation.md rename to docs/src/installation.md diff --git a/docs/src/1_overview.md b/docs/src/overview.md similarity index 100% rename from docs/src/1_overview.md rename to docs/src/overview.md diff --git a/docs/src/3_quick_start.md b/docs/src/quick_start.md similarity index 100% rename from docs/src/3_quick_start.md rename to docs/src/quick_start.md diff --git a/examples/bevypunk/goto.md b/examples/bevypunk.md similarity index 100% rename from examples/bevypunk/goto.md rename to examples/bevypunk.md diff --git a/examples/calculator/goto.md b/examples/calculator.md similarity index 100% rename from examples/calculator/goto.md rename to examples/calculator.md diff --git a/examples/mesh2d/Cargo.toml b/examples/mesh2d/Cargo.toml index da05b83..cff6b5d 100644 --- a/examples/mesh2d/Cargo.toml +++ b/examples/mesh2d/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] - bevy = { version = "0.14.0-rc.2", default_features = false, features = [ + bevy = { version = "^0.14.0", default_features = false, features = [ "bevy_asset", "bevy_gilrs", "bevy_winit", @@ -17,12 +17,6 @@ "bevy_text", "multi_threaded", "png", - "hdr", "x11", - "bevy_gizmos", - "tonemapping_luts", - "default_font", - - "dynamic_linking", ] } bevy_lunex = { workspace = true, features=["debug"] } diff --git a/examples/minimal/Cargo.toml b/examples/minimal/Cargo.toml index 956f9d3..f7f20b8 100644 --- a/examples/minimal/Cargo.toml +++ b/examples/minimal/Cargo.toml @@ -6,7 +6,7 @@ publish = false [dependencies] - bevy = { version = "0.14.0-rc.2", default_features = false, features = [ + bevy = { version = "^0.14.0", default_features = false, features = [ "bevy_asset", "bevy_gilrs", "bevy_winit", @@ -17,12 +17,6 @@ "bevy_text", "multi_threaded", "png", - "hdr", "x11", - "bevy_gizmos", - "tonemapping_luts", - "default_font", - - "dynamic_linking", ] } bevy_lunex = { workspace = true, features=["debug"] } diff --git a/examples/worldspace/Cargo.toml b/examples/worldspace/Cargo.toml new file mode 100644 index 0000000..bd1ba36 --- /dev/null +++ b/examples/worldspace/Cargo.toml @@ -0,0 +1,24 @@ +[package] + name = "worldspace" + authors.workspace = true + version.workspace = true + edition.workspace = true + publish = false + +[dependencies] + bevy = { version = "^0.14.0", default_features = false, features = [ + "bevy_asset", + "bevy_winit", + "bevy_core_pipeline", + "bevy_pbr", + "bevy_render", + "bevy_sprite", + "bevy_text", + "multi_threaded", + "png", + "hdr", + "vorbis", + "x11", + "tonemapping_luts", + ] } + bevy_lunex = { workspace = true, features=["debug"] } diff --git a/examples/worldspace/assets/panel.png b/examples/worldspace/assets/panel.png new file mode 100644 index 0000000..18579d3 Binary files /dev/null and b/examples/worldspace/assets/panel.png differ diff --git a/examples/worldspace/src/boilerplate.rs b/examples/worldspace/src/boilerplate.rs new file mode 100644 index 0000000..e95757c --- /dev/null +++ b/examples/worldspace/src/boilerplate.rs @@ -0,0 +1,80 @@ +use bevy::{app::PluginGroupBuilder, input::mouse::{MouseMotion, MouseWheel}, prelude::*, render::settings::WgpuSettings}; + + +// #========================================# +// #=== BOILERPLATE REQUIRED FOR BEVYCOM ===# + +#[derive(Component)] +pub struct PlayerCam { + pub orbit: Vec3, + pub distance: f32, + pub sensitivity: Vec2, +} +pub fn rotate_playercam(mut mouse_motion_events: EventReader, mouse_input: Res>, mut query: Query<(&PlayerCam, &mut Transform)>) { + let mut delta = Vec2::ZERO; + if mouse_input.pressed(MouseButton::Left) { + delta = mouse_motion_events.read().map(|e| e.delta).sum(); + } + if mouse_input.just_pressed(MouseButton::Left) { + delta = Vec2::ZERO; + } + for (camera, mut transform) in &mut query { + + // ROTATION + let (mut rx, mut ry, rz) = transform.rotation.to_euler(EulerRot::YXZ); + rx += (-delta.x * camera.sensitivity.x).to_radians(); + ry += (-delta.y * camera.sensitivity.x).to_radians(); + ry = ry.clamp(-90_f32.to_radians(), 90_f32.to_radians()); + transform.rotation = Quat::from_euler(EulerRot::YXZ, rx, ry, rz); + + + // ORBIT TRANSFORM + let tx = camera.distance * rx.sin(); + let ty = camera.distance * rx.cos(); + let tz = camera.distance * ry.sin(); + + let diff = camera.distance * ry.cos(); + let plane_ratio_decrease = (camera.distance - diff)/camera.distance; + + transform.translation = camera.orbit; + transform.translation.x += tx * (1.0 - plane_ratio_decrease); + transform.translation.z += ty * (1.0 - plane_ratio_decrease); + transform.translation.y += -tz; + } +} +pub fn zoom_playercam(mut mouse_wheel_events: EventReader, mut query: Query<&mut PlayerCam>) { + let delta: f32 = mouse_wheel_events.read().map(|e| e.y).sum(); + for mut camera in &mut query { + camera.distance += -delta*25.0; + } +} + + +// #======================================# +// #=== JUST SPAWN PRESETS FOR CLARITY ===# + +/// Function to return default plugins with correct settings +pub fn default_plugins() -> PluginGroupBuilder { + DefaultPlugins.set ( + WindowPlugin { + primary_window: Some(Window { + title: "Bevycom".into(), + mode: bevy::window::WindowMode::Windowed, + present_mode: bevy::window::PresentMode::AutoNoVsync, + resolution: bevy::window::WindowResolution::new(1280.0, 720.0), + ..default() + }), + ..default() + } + ).set ( + bevy::render::RenderPlugin { + render_creation: bevy::render::settings::RenderCreation::Automatic( + WgpuSettings { + power_preference: bevy::render::settings::PowerPreference::HighPerformance, + ..default() + } + ), + ..default() + } + ) +} diff --git a/examples/worldspace/src/main.rs b/examples/worldspace/src/main.rs new file mode 100644 index 0000000..47e41a3 --- /dev/null +++ b/examples/worldspace/src/main.rs @@ -0,0 +1,72 @@ +use bevy::prelude::*; +use bevy_lunex::prelude::*; + +mod boilerplate; +use boilerplate::*; + + +fn main() { + App::new() + .add_plugins((default_plugins(), UiPlugin)) + .add_systems(Startup, setup) + .add_systems(Update, (rotate_playercam, zoom_playercam)) + .run(); +} + +fn setup( + mut commands: Commands, + mut material: ResMut>, + asset_server: Res, +) { + // Spawn cursor + commands.spawn(CursorBundle::default()); + + // Spawn camera + commands.spawn(( + Camera3dBundle::default(), + PlayerCam { + orbit: Vec3::new(0.0, 0.0, 0.0), + distance: 800.0, + sensitivity: Vec2::splat(0.1), + } + )); + + // Spawn it 3 times + for x in [-1, 0, 1] { + + // Spawn the floating Ui panel + commands.spawn(( + UiTreeBundle:: { + transform: Transform::from_xyz(-400.0, 300.0, 0.0 + (300.0 * x as f32)), + tree: UiTree::new("PanelWidget"), + ..default() + }, + )).with_children(|ui| { + ui.spawn(( + // Link this widget + UiLink::::path("Root"), + + // The layout that is used when in base state + UiLayout::window_full().size((818.0, 965.0)).pack::(), + + // Give the mesh an image + UiMaterial3dBundle::from_transparent_image(&mut material, asset_server.load("panel.png")), + + // Make the panel pickable + PickableBundle::default(), + + // This is required to control our hover animation + UiAnimator::::new().forward_speed(6.0).backward_speed(5.0), + + // This is required for Layout animation + UiLayoutController::default(), + + // The layout that is used when in hover state + UiLayout::window_full().x(100.0).size((818.0, 965.0)).pack::(), + + // This will change cursor icon on mouse hover + OnHoverSetCursor::new(CursorIcon::Pointer), + )); + }); + } +}