very basic pipes and floor

main
Elijah Voigt 3 months ago
parent 9d5caadbf4
commit 0a7f2f8d94

@ -1,3 +1,6 @@
// Bevy basically forces "complex types" with Querys
#![allow(clippy::type_complexity)]
use bevy::input::common_conditions::input_just_released;
use games::*;
@ -7,7 +10,7 @@ fn main() {
name: "flappy bird (with rewind)".into(),
})
.init_state::<PlayerState>()
.add_systems(Startup, (init_bird, init_ground, init_ui, tweak_camera.after(setup_camera)))
.add_systems(Startup, (init_bird, init_obstacles, init_ui, tweak_camera.after(setup_camera)))
.add_systems(OnEnter(PlayerState::Alive), alive_bird)
.add_systems(OnExit(PlayerState::Alive), kill_bird)
.add_systems(
@ -45,11 +48,13 @@ fn main() {
}
fn tweak_camera(
mut camera: Query<(&mut Camera, &mut AmbientLight), With<Camera>>,
mut camera: Query<(&mut Camera, &mut AmbientLight, &mut Transform), With<Camera>>,
) {
camera.iter_mut().for_each(|(mut c, mut al)| {
camera.iter_mut().for_each(|(mut c, mut al, mut t)| {
c.clear_color = ClearColorConfig::Custom(WHITE.into());
al.brightness = 100.0;
// move the camera "back" so everything else is at 0 on the Z axis
t.translation.z = 10.0;
});
}
@ -88,7 +93,7 @@ fn init_bird(
let name = Name::new("bird");
let t = Transform::from_xyz(0.0, 0.0, -10.0).with_rotation(Quat::from_rotation_x(PI / 2.0));
let t = Transform::from_xyz(0.0, 0.0, 0.0).with_rotation(Quat::from_rotation_x(PI / 2.0));
let physics = (
RigidBody::Dynamic,
@ -103,28 +108,59 @@ fn init_bird(
commands.spawn((name, mesh, material, physics, t, Bird, tape));
}
#[derive(Component)]
#[derive(Component, Clone)]
struct Ground;
fn init_ground(
#[derive(Component, Clone)]
struct Pipe;
fn init_obstacles(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let material = MeshMaterial3d(materials.add(StandardMaterial {
base_color: GREEN.into(),
..default()
}));
let ground = {
let material = MeshMaterial3d(materials.add(StandardMaterial {
base_color: LIGHT_GREEN.into(),
..default()
}));
let mesh = Mesh3d(meshes.add(Cuboid::new(10.0, 1.0, 1.0)));
let mesh = Mesh3d(meshes.add(Cuboid::new(10.0, 1.0, 1.0)));
let name = Name::new("ground");
let name = Name::new("ground");
let t = Transform::from_xyz(0.0, -4.0, -10.0);
let physics = (RigidBody::Static, Collider::cuboid(1.0, 1.0, 1.0));
let physics = (RigidBody::Static, Collider::cuboid(1.0, 1.0, 1.0));
(name, mesh, material, physics, Ground)
};
let pipe = {
let material = MeshMaterial3d(materials.add(StandardMaterial {
base_color: GREEN.into(),
..default()
}));
let mesh = Mesh3d(meshes.add(Cuboid::new(1.0, 3.0, 1.0)));
let physics = (RigidBody::Static, Collider::cuboid(1.0, 3.0, 1.0));
let name = Name::new("pipe");
(name.clone(), mesh.clone(), material.clone(), physics.clone(), Pipe)
};
// TODO: Instead of spawning infinite floor/pipes, we should instead spawn enough for 1-3 a few
// screens and then "move" them around.
// This is considerably more complexity so can be implemented later, but would keep memory
// overhead fairly static.
(1..50).for_each(|i| {
// TODO: Jitter up/down/close/far of pipes for challenge
let above = Transform::from_xyz(5.0 * i as f32, 4.0, 0.0);
let below = Transform::from_xyz(5.0 * i as f32, -4.0, 0.0);
let floor = Transform::from_xyz(1.0 * i as f32, -4.0, 0.0);
commands.spawn((pipe.clone(), above));
commands.spawn((pipe.clone(), below));
commands.spawn((ground.clone(), floor));
});
commands.spawn((name, mesh, material, physics, t, Ground));
}
fn init_ui(
@ -217,18 +253,21 @@ fn rewind(
});
}
// PERF: Runs more than it needs, should only execute when bird enters/exit frame
// PERF: May run more than necessary, should be event-driven on aabb intersection
fn detect_dead(
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
bird: Single<&ColliderAabb, With<Bird>>,
ground: Single<&ColliderAabb, With<Ground>>,
obstacles: Query<&ColliderAabb, Or<(With<Ground>, With<Pipe>)>>,
mut next: ResMut<NextState<PlayerState>>,
) {
debug_assert!(
matches!(state.get(), PlayerState::Alive),
"Only check if dead while alive"
);
if bird.intersects(*ground) {
if obstacles.iter().any(|obstacle| {
bird.intersects(obstacle)
}) {
next.set(PlayerState::Dead);
}
}

Loading…
Cancel
Save