From 04071886e07ea18bfe2be668d7ca2ce2702989f4 Mon Sep 17 00:00:00 2001 From: GitGhillie Date: Fri, 16 May 2025 14:16:21 +0200 Subject: [PATCH 01/22] wip --- Cargo.toml | 11 +++ examples/3d/healthbar.rs | 142 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 examples/3d/healthbar.rs diff --git a/Cargo.toml b/Cargo.toml index a3d3a2ab63e51..9bcff9f908206 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -969,6 +969,17 @@ description = "A scene showcasing pbr atmospheric scattering" category = "3D Rendering" wasm = true +[[example]] +name = "healthbar" +path = "examples/3d/healthbar.rs" +doc-scrape-examples = true + +[package.metadata.example.healthbar] +name = "Healthbar" +description = "A scene showcasing world-to-viewport UI" +category = "3D Rendering" +wasm = true + [[example]] name = "fog" path = "examples/3d/fog.rs" diff --git a/examples/3d/healthbar.rs b/examples/3d/healthbar.rs new file mode 100644 index 0000000000000..d71ce2147fed4 --- /dev/null +++ b/examples/3d/healthbar.rs @@ -0,0 +1,142 @@ +//! A simple 3D scene with light shining over a cube sitting on a plane. (todo) + +use bevy::input::common_conditions::input_just_pressed; +use bevy::prelude::*; + +/// Marker for the health bar root UI node +#[derive(Component)] +struct HealthBarMarker; + +/// Health bar should be moved above this entity (todo: use relations) +#[derive(Component)] +struct HealthBarTarget; + +// Define a struct to keep some information about our entity. +// Here it's an arbitrary movement speed, the spawn location, and a maximum distance from it. +#[derive(Component)] +struct Movable { + spawn: Vec3, + max_distance: f32, + speed: f32, +} + +// Implement a utility function for easier Movable struct creation. +impl Movable { + fn new(spawn: Vec3) -> Self { + Movable { + spawn, + max_distance: 5.0, + speed: 2.0, + } + } +} + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugins(RemotePlugin::default()) + .add_plugins(RemoteHttpPlugin::default()) + .add_systems(Startup, (setup, setup_ui)) + .add_systems(Update, update_ui.run_if(input_just_pressed(KeyCode::Space))) + .add_systems(Update, move_cube) + .run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // circular base + commands.spawn(( + Mesh3d(meshes.add(Circle::new(4.0))), + MeshMaterial3d(materials.add(Color::WHITE)), + Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), + )); + // cube + let entity_spawn = Vec3::new(0.0, 0.5, 0.0); + commands.spawn(( + Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), + MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), + Transform::from_translation(entity_spawn), + HealthBarTarget, + Movable::new(entity_spawn) + )); + // light + commands.spawn(( + PointLight { + shadows_enabled: true, + ..default() + }, + Transform::from_xyz(4.0, 8.0, 4.0), + )); + // camera + commands.spawn(( + Camera3d::default(), + Transform::from_xyz(-4.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y), + )); +} + +/// todo comment +fn setup_ui(mut commands: Commands, asset_server: Res) { + let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf"); + + commands.spawn(( + Node { + position_type: PositionType::Absolute, + left: Val::Vw(30.0), + top: Val::Vw(50.0), + width: Val::Px(150.0), + height: Val::Px(65.0), + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + ..default() + }, + BackgroundColor(Color::srgba(0.1, 0.5, 0.1, 0.3)), + HealthBarMarker, + children![( + Text::new("42"), + TextFont { + font: font_handle.clone(), + font_size: 33.0, + ..default() + }, + TextColor(Color::srgb(1.0, 1.0, 1.0)), + )], + )); +} + +fn update_ui( + mut health_bar_query: Query<&mut Node, With>, + target_query: Single<&GlobalTransform, With>, + camera_query: Single<(&Camera, &GlobalTransform)>, +) { + let camera = camera_query.0; + let cam_transform = camera_query.1; + + let world_position = target_query.translation(); + + for mut health_bar_node in health_bar_query.iter_mut() { + let viewport_position = camera.world_to_viewport(cam_transform, world_position).unwrap(); + health_bar_node.left = Val::Px(viewport_position.x); + health_bar_node.top = Val::Px(viewport_position.y); + + println!("{}", camera.world_to_viewport(cam_transform, Vec3::ZERO).unwrap()); + println!("{:?}", health_bar_node.left); + println!("{:?}", health_bar_node.top); + } +} + +// This system will move all Movable entities with a Transform +fn move_cube(mut cubes: Query<(&mut Transform, &mut Movable)>, timer: Res