use avian3d::math::Vector; // TEMP use avian2d::prelude::*; use bevy::{color::palettes::css::*, prelude::*}; fn main() { App::new() .add_plugins(DefaultPlugins) .add_plugins(PhysicsPlugins::default()) .insert_resource(ClearColor(WHITE.into())) .add_systems(Startup, setup) .add_systems(Update, move_player) .add_systems(Update, follow_player) .add_systems(Update, draw_gizmos) .add_systems( Update, event_detection.run_if(on_event::.or(on_event::)), ) .add_observer(set_tree_position) .run(); } #[derive(Component)] struct Player; #[derive(Component)] struct RenderGizmo; #[derive(Component)] struct PlayArea; #[derive(Component, Debug)] struct TreePos(isize); fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { commands.spawn(Camera2d); // Spawn "trees" (which are also sensors) [ ("TREE -3", -3), ("TREE -2", -2), ("TREE -2", -1), ("TREE 1", 1), ("TREE 2", 2), ("TREE 3", 3), ] .iter() .for_each(|(label, pos)| { commands.spawn(( RigidBody::Static, Collider::rectangle(100.0, 250.0), Mesh2d(meshes.add(Rectangle::new(100.0, 250.0))), MeshMaterial2d(materials.add(Color::from(GREEN))), TreePos(*pos), children![Text2d::new(*label),], )); }); commands.spawn(( Player, RigidBody::Kinematic, ExternalImpulse::default().with_persistence(true), Collider::rectangle(50.0, 50.0), Mesh2d(meshes.add(Rectangle::new(50.0, 50.0))), MeshMaterial2d(materials.add(Color::from(RED))), children![ ( Text2d::new("PLAYER"), Transform::from_scale(Vec3::new(0.5, 0.5, 0.5)), ), ( Text2d::new("PLAY AREA"), Transform::from_xyz(0.0, 212.0, 1.0), TextColor(BLACK.into()) ), ( RenderGizmo, PlayArea, Visibility::Visible, Transform::from_scale(Vec3::new(400.0, 400.0, 0.0)), ), ], )); } fn set_tree_position( trigger: Trigger, tree_pos: Query<&TreePos>, mut commands: Commands, ) { let TreePos(pos) = tree_pos.get(trigger.target()).unwrap(); let x = if (*pos) > 0 { (200.0 * (*pos) as f32) - 100.0 } else { (200.0 * (*pos) as f32) + 100.0 }; let t = Transform::from_xyz(x, 0.0, 0.0); commands.entity(trigger.target()).insert(t); } fn move_player( keyboard_input: Res>, mut player: Single<&mut ExternalImpulse, With>, ) { const SPEED: f32 = 5.0; if keyboard_input.pressed(KeyCode::ArrowLeft) { player.set_impulse(Vec2::NEG_X * SPEED); } else if keyboard_input.pressed(KeyCode::ArrowRight) { player.set_impulse(Vec2::X * SPEED); } } fn follow_player( player: Query<&Transform, (With, Changed)>, mut camera: Single<&mut Transform, (With, Without)>, ) { player.iter().for_each(|pt| { camera.translation = pt.translation; }); } fn draw_gizmos(mut gizmos: Gizmos, play_area: Single<&GlobalTransform, With>) { gizmos.rect_2d( play_area.translation().truncate(), play_area.scale().truncate(), MAGENTA, ) } fn event_detection( mut start_events: EventReader, mut end_events: EventReader, player: Single<&Player>, trees: Query<&TreePos>, keyboard_input: Res>, ) { debug_assert!(!(start_events.is_empty() && end_events.is_empty())); let s = start_events.read().map(|CollisionStarted(a, b)| (a, b)); let e = end_events.read().map(|CollisionEnded(a, b)| (a, b)); let mut events = s.chain(e); let current_tree = events.find_map(|(a, b)| { info!("{a:?}, {b:?}"); trees.get(*a).or(trees.get(*b)).ok() }); info!("{:?}", current_tree); if keyboard_input.pressed(KeyCode::ArrowLeft) { // moving left } else if keyboard_input.pressed(KeyCode::ArrowRight) { // moving right } }