|
|
|
|
@ -75,7 +75,7 @@ fn main() {
|
|
|
|
|
sync_resource_to_ui::<Deaths>.run_if(resource_changed::<Deaths>),
|
|
|
|
|
sync_resource_to_ui::<RewindFrames>.run_if(resource_changed::<RewindFrames>),
|
|
|
|
|
),
|
|
|
|
|
scoring.run_if(on_event::<CollisionEnded>)
|
|
|
|
|
scoring.run_if(on_event::<CollisionEnded>),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_observer(flap)
|
|
|
|
|
@ -137,7 +137,16 @@ fn init_bird(
|
|
|
|
|
|
|
|
|
|
let tape = Tape::default();
|
|
|
|
|
|
|
|
|
|
commands.spawn((name, mesh, material, physics, t, Bird, tape, CollisionEventsEnabled));
|
|
|
|
|
commands.spawn((
|
|
|
|
|
name,
|
|
|
|
|
mesh,
|
|
|
|
|
material,
|
|
|
|
|
physics,
|
|
|
|
|
t,
|
|
|
|
|
Bird,
|
|
|
|
|
tape,
|
|
|
|
|
CollisionEventsEnabled,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Component, Clone)]
|
|
|
|
|
@ -187,14 +196,15 @@ fn init_obstacles(
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
let hitbox = {
|
|
|
|
|
let physics = (RigidBody::Static, Collider::rectangle(1.0, 10.0), Sensor, CollisionEventsEnabled);
|
|
|
|
|
let physics = (
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
Collider::rectangle(1.0, 10.0),
|
|
|
|
|
Sensor,
|
|
|
|
|
CollisionEventsEnabled,
|
|
|
|
|
);
|
|
|
|
|
let name = Name::new("hitbox");
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
name.clone(),
|
|
|
|
|
physics.clone(),
|
|
|
|
|
Hitbox,
|
|
|
|
|
)
|
|
|
|
|
(name.clone(), physics.clone(), Hitbox)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// TODO: Instead of spawning infinite floor/pipes, we should instead spawn enough for 1-3 a few
|
|
|
|
|
@ -203,16 +213,20 @@ fn init_obstacles(
|
|
|
|
|
// overhead fairly static.
|
|
|
|
|
(1..10).for_each(|i| {
|
|
|
|
|
// TODO: Jitter up/down/close/far of pipes for challenge
|
|
|
|
|
let above = Transform::from_xyz(300.0 * i as f32, 300.0, -1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
let above =
|
|
|
|
|
Transform::from_xyz(300.0 * i as f32, 300.0, -1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
commands.spawn((pipe.clone(), above));
|
|
|
|
|
|
|
|
|
|
let below = Transform::from_xyz(300.0 * i as f32, -200.0, -1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
let below =
|
|
|
|
|
Transform::from_xyz(300.0 * i as f32, -200.0, -1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
commands.spawn((pipe.clone(), below));
|
|
|
|
|
|
|
|
|
|
let hitbox_pos = Transform::from_xyz(300.0 * i as f32, 0.0, 10.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
let hitbox_pos =
|
|
|
|
|
Transform::from_xyz(300.0 * i as f32, 0.0, 10.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
commands.spawn((hitbox_pos, hitbox.clone()));
|
|
|
|
|
|
|
|
|
|
let floor = Transform::from_xyz(300.0 * i as f32, -300.0, 1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
let floor =
|
|
|
|
|
Transform::from_xyz(300.0 * i as f32, -300.0, 1.0).with_scale(Vec3::splat(100.0));
|
|
|
|
|
commands.spawn((ground.clone(), floor));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -227,12 +241,34 @@ fn init_ui(mut commands: Commands) {
|
|
|
|
|
},
|
|
|
|
|
PlayerState::Stasis,
|
|
|
|
|
children![
|
|
|
|
|
(Text::new("You Died"), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(SyncResource::<Score>::default(), Text::default(), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(SyncResource::<Flaps>::default(), Text::default(), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(SyncResource::<RewindFrames>::default(), Text::default(), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(SyncResource::<Deaths>::default(), Text::default(), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(Text::new("Press R to Rewind"), TextLayout::new_with_justify(JustifyText::Center)),
|
|
|
|
|
(
|
|
|
|
|
Text::new("You Died"),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
SyncResource::<Score>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
SyncResource::<Flaps>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
SyncResource::<RewindFrames>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
SyncResource::<Deaths>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
Text::new("Press R to Rewind"),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
@ -255,19 +291,17 @@ fn init_ui(mut commands: Commands) {
|
|
|
|
|
))
|
|
|
|
|
.observe(start_game);
|
|
|
|
|
|
|
|
|
|
commands.spawn(
|
|
|
|
|
Node {
|
|
|
|
|
commands
|
|
|
|
|
.spawn(Node {
|
|
|
|
|
align_self: AlignSelf::End,
|
|
|
|
|
justify_self: JustifySelf::Center,
|
|
|
|
|
flex_direction: FlexDirection::Row,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
).with_children(|parent| {
|
|
|
|
|
})
|
|
|
|
|
.with_children(|parent| {
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Node { ..default() },
|
|
|
|
|
Button,
|
|
|
|
|
children![Text::new("Rewind!"),],
|
|
|
|
|
))
|
|
|
|
|
@ -275,13 +309,7 @@ fn init_ui(mut commands: Commands) {
|
|
|
|
|
.observe(end_rewind);
|
|
|
|
|
|
|
|
|
|
parent
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Button,
|
|
|
|
|
children![Text::new("Flap!"),],
|
|
|
|
|
))
|
|
|
|
|
.spawn((Node { ..default() }, Button, children![Text::new("Flap!"),]))
|
|
|
|
|
.observe(flap_button);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -294,7 +322,7 @@ fn init_ui(mut commands: Commands) {
|
|
|
|
|
BackgroundColor(WHITE.into()),
|
|
|
|
|
SyncResource::<Score>::default(),
|
|
|
|
|
Text::default(),
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center)
|
|
|
|
|
TextLayout::new_with_justify(JustifyText::Center),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -306,7 +334,11 @@ fn end_rewind(_trigger: Trigger<Pointer<Released>>, mut next: ResMut<NextState<P
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn flap_button(trigger: Trigger<Pointer<Pressed>>, mut commands: Commands, bird: Single<Entity, With<Bird>>) {
|
|
|
|
|
fn flap_button(
|
|
|
|
|
trigger: Trigger<Pointer<Pressed>>,
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
bird: Single<Entity, With<Bird>>,
|
|
|
|
|
) {
|
|
|
|
|
let e = *bird;
|
|
|
|
|
info!("Flapping {:?}", e);
|
|
|
|
|
commands.trigger_targets(Flap, e);
|
|
|
|
|
@ -328,7 +360,8 @@ fn un_pause_game(mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
struct Flap;
|
|
|
|
|
|
|
|
|
|
// Observer for flapping
|
|
|
|
|
fn flap(trigger: Trigger<Flap>,
|
|
|
|
|
fn flap(
|
|
|
|
|
trigger: Trigger<Flap>,
|
|
|
|
|
mut bird: Query<&mut ExternalImpulse, With<Bird>>,
|
|
|
|
|
mut flaps: ResMut<Flaps>,
|
|
|
|
|
) {
|
|
|
|
|
@ -387,7 +420,9 @@ fn record(
|
|
|
|
|
"Only record in the alive state"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
birds.iter_mut().for_each(|(transform, linear_velocity, angular_velocity, mut tape)| {
|
|
|
|
|
birds
|
|
|
|
|
.iter_mut()
|
|
|
|
|
.for_each(|(transform, linear_velocity, angular_velocity, mut tape)| {
|
|
|
|
|
tape.translations.push(transform.translation);
|
|
|
|
|
tape.rotations.push(transform.rotation);
|
|
|
|
|
tape.linear_velocities.push(*linear_velocity);
|
|
|
|
|
@ -397,7 +432,15 @@ fn record(
|
|
|
|
|
|
|
|
|
|
fn rewind(
|
|
|
|
|
#[cfg(debug_assertions)] state: Res<State<PlayerState>>,
|
|
|
|
|
mut birds: Query<(&mut Transform, &mut AngularVelocity, &mut LinearVelocity, &mut Tape), With<Bird>>,
|
|
|
|
|
mut birds: Query<
|
|
|
|
|
(
|
|
|
|
|
&mut Transform,
|
|
|
|
|
&mut AngularVelocity,
|
|
|
|
|
&mut LinearVelocity,
|
|
|
|
|
&mut Tape,
|
|
|
|
|
),
|
|
|
|
|
With<Bird>,
|
|
|
|
|
>,
|
|
|
|
|
mut frames: ResMut<RewindFrames>,
|
|
|
|
|
) {
|
|
|
|
|
debug_assert!(
|
|
|
|
|
@ -405,13 +448,14 @@ fn rewind(
|
|
|
|
|
"Only rewind in the rewinding state"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
birds.iter_mut().for_each(|(mut transform, mut angular_velocity, mut linear_velocity, mut tape)| {
|
|
|
|
|
birds.iter_mut().for_each(
|
|
|
|
|
|(mut transform, mut angular_velocity, mut linear_velocity, mut tape)| {
|
|
|
|
|
match (tape.translations.pop(), tape.rotations.pop()) {
|
|
|
|
|
(Some(t), Some(r)) => {
|
|
|
|
|
transform.translation = t;
|
|
|
|
|
transform.rotation = r;
|
|
|
|
|
frames.0 += 1;
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
(None, None) => (),
|
|
|
|
|
_ => panic!("Translations and rotations are out of sync!"),
|
|
|
|
|
}
|
|
|
|
|
@ -422,7 +466,8 @@ fn rewind(
|
|
|
|
|
if let Some(lv) = tape.linear_velocities.pop() {
|
|
|
|
|
*linear_velocity = lv;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PERF: May run more than necessary, should be event-driven on aabb intersection
|
|
|
|
|
@ -523,9 +568,10 @@ fn scoring(
|
|
|
|
|
hitboxes: Query<Entity, With<Hitbox>>,
|
|
|
|
|
mut score: ResMut<Score>,
|
|
|
|
|
) {
|
|
|
|
|
events.read().filter(|CollisionEnded(a, b)| {
|
|
|
|
|
bird.contains(*a) && hitboxes.contains(*b)
|
|
|
|
|
}).for_each(|_| {
|
|
|
|
|
events
|
|
|
|
|
.read()
|
|
|
|
|
.filter(|CollisionEnded(a, b)| bird.contains(*a) && hitboxes.contains(*b))
|
|
|
|
|
.for_each(|_| {
|
|
|
|
|
info!("Hit event while {:?}", state.get());
|
|
|
|
|
match state.get() {
|
|
|
|
|
PlayerState::Alive => score.0 = score.0.saturating_add(1),
|
|
|
|
|
@ -533,5 +579,4 @@ fn scoring(
|
|
|
|
|
PlayerState::Pause | PlayerState::Stasis => (),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|