|
|
|
|
@ -7,7 +7,7 @@ fn main() {
|
|
|
|
|
name: "flappy bird (with rewind)".into(),
|
|
|
|
|
})
|
|
|
|
|
.init_state::<PlayerState>()
|
|
|
|
|
.add_systems(Startup, (init_bird, init_ui))
|
|
|
|
|
.add_systems(Startup, (init_bird, init_ground, init_ui, tweak_camera.after(setup_camera)))
|
|
|
|
|
.add_systems(OnEnter(PlayerState::Alive), alive_bird)
|
|
|
|
|
.add_systems(OnExit(PlayerState::Alive), kill_bird)
|
|
|
|
|
.add_systems(
|
|
|
|
|
@ -42,6 +42,15 @@ fn main() {
|
|
|
|
|
.run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tweak_camera(
|
|
|
|
|
mut camera: Query<(&mut Camera, &mut AmbientLight), With<Camera>>,
|
|
|
|
|
) {
|
|
|
|
|
camera.iter_mut().for_each(|(mut c, mut al)| {
|
|
|
|
|
c.clear_color = ClearColorConfig::Custom(WHITE.into());
|
|
|
|
|
al.brightness = 100.0;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
struct Bird;
|
|
|
|
|
|
|
|
|
|
@ -68,7 +77,7 @@ fn init_bird(
|
|
|
|
|
) {
|
|
|
|
|
let material = MeshMaterial3d(materials.add(StandardMaterial {
|
|
|
|
|
base_color_texture: Some(server.load("flappy/bevy.png")),
|
|
|
|
|
base_color: WHITE.with_alpha(0.9).into(),
|
|
|
|
|
base_color: WHITE.into(),
|
|
|
|
|
alpha_mode: AlphaMode::Blend,
|
|
|
|
|
..default()
|
|
|
|
|
}));
|
|
|
|
|
@ -79,13 +88,40 @@ fn init_bird(
|
|
|
|
|
|
|
|
|
|
let t = Transform::from_xyz(0.0, 0.0, -10.0).with_rotation(Quat::from_rotation_x(PI / 2.0));
|
|
|
|
|
|
|
|
|
|
let mass = (RigidBody::Dynamic, Collider::capsule(1.0, 1.0), Mass(1.0));
|
|
|
|
|
|
|
|
|
|
let force = ExternalForce::default().with_persistence(false);
|
|
|
|
|
let physics = (
|
|
|
|
|
RigidBody::Dynamic,
|
|
|
|
|
Collider::capsule(1.0, 1.0), Mass(1.0),
|
|
|
|
|
ExternalForce::default().with_persistence(false),
|
|
|
|
|
LockedAxes::ROTATION_LOCKED.lock_translation_z(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let tape = Tape::default();
|
|
|
|
|
|
|
|
|
|
commands.spawn((name, mesh, material, mass, t, Bird, force, tape));
|
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Bird, tape));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Component)]
|
|
|
|
|
struct Ground;
|
|
|
|
|
|
|
|
|
|
fn init_ground(
|
|
|
|
|
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 mesh = Mesh3d(meshes.add(Cuboid::new(5.0, 1.0, 1.0)));
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Ground));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init_ui(
|
|
|
|
|
@ -125,7 +161,7 @@ fn flap(
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
bird.iter_mut().for_each(|(t, mut f)| {
|
|
|
|
|
f.apply_force(t.rotation * Vec3::NEG_Z * 1000.0);
|
|
|
|
|
f.apply_force(t.rotation * Vec3::NEG_Z * 500.0);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -135,7 +171,7 @@ fn toggle_rewind(keycode: Res<ButtonInput<KeyCode>>, mut next: ResMut<NextState<
|
|
|
|
|
"Only toggle rewind when R is pressed"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if keycode.pressed(KeyCode::KeyR) {
|
|
|
|
|
if keycode.just_pressed(KeyCode::KeyR) {
|
|
|
|
|
debug!("Toggling rewind ON");
|
|
|
|
|
next.set(PlayerState::Rewind)
|
|
|
|
|
} else {
|
|
|
|
|
@ -181,48 +217,33 @@ fn rewind(
|
|
|
|
|
// PERF: Runs more than it needs, should only execute when bird enters/exit frame
|
|
|
|
|
fn detect_dead(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
bird: Single<Entity, With<Bird>>,
|
|
|
|
|
visible: Query<&VisibleEntities, With<Camera>>,
|
|
|
|
|
bird: Single<&ColliderAabb, With<Bird>>,
|
|
|
|
|
ground: Single<&ColliderAabb, With<Ground>>,
|
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(
|
|
|
|
|
matches!(state.get(), PlayerState::Alive),
|
|
|
|
|
"Only check if dead while alive"
|
|
|
|
|
);
|
|
|
|
|
visible.iter().for_each(|ve| {
|
|
|
|
|
if ve.entities.iter().len() > 0 {
|
|
|
|
|
let bird_visible = ve
|
|
|
|
|
.entities
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|(_type_id, list)| list.contains(&(*bird)));
|
|
|
|
|
|
|
|
|
|
if bird_visible {
|
|
|
|
|
debug!("Bird is visible, making alive");
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
} else {
|
|
|
|
|
debug!("Bird is not visible, making dead");
|
|
|
|
|
next.set(PlayerState::Dead);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
if bird.intersects(*ground) {
|
|
|
|
|
next.set(PlayerState::Dead);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn alive_bird(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
bird: Single<Entity, With<Bird>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
mut bird: Single<&mut RigidBody, With<Bird>>,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(!matches!(state.get(), PlayerState::Dead));
|
|
|
|
|
debug!("Aliving bird");
|
|
|
|
|
commands.entity(*bird).insert(RigidBody::Dynamic);
|
|
|
|
|
**bird = RigidBody::Dynamic;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn kill_bird(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
bird: Single<Entity, With<Bird>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
mut bird: Single<&mut RigidBody, With<Bird>>,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(matches!(state.get(), PlayerState::Dead));
|
|
|
|
|
debug_assert!(!matches!(state.get(), PlayerState::Alive));
|
|
|
|
|
debug!("Killing bird");
|
|
|
|
|
commands.entity(*bird).remove::<RigidBody>();
|
|
|
|
|
**bird = RigidBody::Static;
|
|
|
|
|
}
|
|
|
|
|
|