|
|
|
@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
// Bevy basically forces "complex types" with Querys
|
|
|
|
|
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
|
|
|
|
|
|
|
use bevy::input::common_conditions::input_just_released;
|
|
|
|
use bevy::input::common_conditions::input_just_released;
|
|
|
|
use games::*;
|
|
|
|
use games::*;
|
|
|
|
|
|
|
|
|
|
|
|
@ -7,7 +10,7 @@ fn main() {
|
|
|
|
name: "flappy bird (with rewind)".into(),
|
|
|
|
name: "flappy bird (with rewind)".into(),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.init_state::<PlayerState>()
|
|
|
|
.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(OnEnter(PlayerState::Alive), alive_bird)
|
|
|
|
.add_systems(OnExit(PlayerState::Alive), kill_bird)
|
|
|
|
.add_systems(OnExit(PlayerState::Alive), kill_bird)
|
|
|
|
.add_systems(
|
|
|
|
.add_systems(
|
|
|
|
@ -45,11 +48,13 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn tweak_camera(
|
|
|
|
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());
|
|
|
|
c.clear_color = ClearColorConfig::Custom(WHITE.into());
|
|
|
|
al.brightness = 100.0;
|
|
|
|
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 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 = (
|
|
|
|
let physics = (
|
|
|
|
RigidBody::Dynamic,
|
|
|
|
RigidBody::Dynamic,
|
|
|
|
@ -103,16 +108,20 @@ fn init_bird(
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Bird, tape));
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Bird, tape));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
#[derive(Component, Clone)]
|
|
|
|
struct Ground;
|
|
|
|
struct Ground;
|
|
|
|
|
|
|
|
|
|
|
|
fn init_ground(
|
|
|
|
#[derive(Component, Clone)]
|
|
|
|
|
|
|
|
struct Pipe;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn init_obstacles(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut commands: Commands,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
|
|
|
|
let ground = {
|
|
|
|
let material = MeshMaterial3d(materials.add(StandardMaterial {
|
|
|
|
let material = MeshMaterial3d(materials.add(StandardMaterial {
|
|
|
|
base_color: GREEN.into(),
|
|
|
|
base_color: LIGHT_GREEN.into(),
|
|
|
|
..default()
|
|
|
|
..default()
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
@ -120,11 +129,38 @@ fn init_ground(
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Ground));
|
|
|
|
(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));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn init_ui(
|
|
|
|
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(
|
|
|
|
fn detect_dead(
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
bird: Single<&ColliderAabb, With<Bird>>,
|
|
|
|
bird: Single<&ColliderAabb, With<Bird>>,
|
|
|
|
ground: Single<&ColliderAabb, With<Ground>>,
|
|
|
|
obstacles: Query<&ColliderAabb, Or<(With<Ground>, With<Pipe>)>>,
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
debug_assert!(
|
|
|
|
debug_assert!(
|
|
|
|
matches!(state.get(), PlayerState::Alive),
|
|
|
|
matches!(state.get(), PlayerState::Alive),
|
|
|
|
"Only check if dead while alive"
|
|
|
|
"Only check if dead while alive"
|
|
|
|
);
|
|
|
|
);
|
|
|
|
if bird.intersects(*ground) {
|
|
|
|
|
|
|
|
|
|
|
|
if obstacles.iter().any(|obstacle| {
|
|
|
|
|
|
|
|
bird.intersects(obstacle)
|
|
|
|
|
|
|
|
}) {
|
|
|
|
next.set(PlayerState::Dead);
|
|
|
|
next.set(PlayerState::Dead);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|