|
|
|
|
@ -1,16 +1,28 @@
|
|
|
|
|
// Bevy basically forces "complex types" with Querys
|
|
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
|
|
|
|
|
use bevy::input::common_conditions::input_just_released;
|
|
|
|
|
use games::physics3d::*;
|
|
|
|
|
use games::*;
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
App::new()
|
|
|
|
|
.add_plugins(BaseGamePlugin {
|
|
|
|
|
name: "flappy bird (with rewind)".into(),
|
|
|
|
|
})
|
|
|
|
|
.add_plugins((
|
|
|
|
|
BaseGamePlugin {
|
|
|
|
|
name: "flappy bird (with rewind)".into(),
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Physics3dPlugin,
|
|
|
|
|
))
|
|
|
|
|
.init_state::<PlayerState>()
|
|
|
|
|
.add_systems(Startup, (init_bird, init_obstacles, init_ui, tweak_camera.after(setup_camera)))
|
|
|
|
|
.add_systems(
|
|
|
|
|
Startup,
|
|
|
|
|
(
|
|
|
|
|
init_bird,
|
|
|
|
|
init_obstacles,
|
|
|
|
|
init_ui,
|
|
|
|
|
tweak_camera.after(create_camera_3d),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.add_systems(OnEnter(PlayerState::Alive), alive_bird)
|
|
|
|
|
.add_systems(OnEnter(PlayerState::Pause), pause_bird)
|
|
|
|
|
.add_systems(OnEnter(PlayerState::Stasis), pause_bird)
|
|
|
|
|
@ -24,7 +36,8 @@ fn main() {
|
|
|
|
|
debug_state_changes::<PlayerState>,
|
|
|
|
|
// Toggle (UI) elements when the player dies/alives
|
|
|
|
|
toggle_state_visibility::<PlayerState>,
|
|
|
|
|
).run_if(state_changed::<PlayerState>),
|
|
|
|
|
)
|
|
|
|
|
.run_if(state_changed::<PlayerState>),
|
|
|
|
|
// Detect if the bird is "dead" by checking if it is visible
|
|
|
|
|
// from the point of view of the camera
|
|
|
|
|
detect_dead.run_if(in_state(PlayerState::Alive)),
|
|
|
|
|
@ -47,16 +60,15 @@ fn main() {
|
|
|
|
|
// Pause when the player presses Escape
|
|
|
|
|
pause_game.run_if(input_just_pressed(KeyCode::Escape)),
|
|
|
|
|
// Transition out of the pause screen if the player presses space
|
|
|
|
|
un_pause_game.run_if(input_just_pressed(KeyCode::Space))
|
|
|
|
|
un_pause_game
|
|
|
|
|
.run_if(input_just_pressed(KeyCode::Space))
|
|
|
|
|
.run_if(in_state(PlayerState::Pause)),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.run();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tweak_camera(
|
|
|
|
|
mut camera: Query<(&mut Camera, &mut AmbientLight, &mut Transform), With<Camera>>,
|
|
|
|
|
) {
|
|
|
|
|
fn tweak_camera(mut camera: Query<(&mut Camera, &mut AmbientLight, &mut Transform), With<Camera>>) {
|
|
|
|
|
camera.iter_mut().for_each(|(mut c, mut al, mut t)| {
|
|
|
|
|
c.clear_color = ClearColorConfig::Custom(WHITE.into());
|
|
|
|
|
al.brightness = 100.0;
|
|
|
|
|
@ -74,7 +86,7 @@ enum PlayerState {
|
|
|
|
|
Rewind,
|
|
|
|
|
Stasis,
|
|
|
|
|
#[default]
|
|
|
|
|
Pause
|
|
|
|
|
Pause,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A tape tracking the bird's state every frame
|
|
|
|
|
@ -105,7 +117,8 @@ fn init_bird(
|
|
|
|
|
|
|
|
|
|
let physics = (
|
|
|
|
|
RigidBody::Static,
|
|
|
|
|
Collider::capsule(1.0, 1.0), Mass(1.0),
|
|
|
|
|
Collider::capsule(1.0, 1.0),
|
|
|
|
|
Mass(1.0),
|
|
|
|
|
ExternalForce::new(Vec3::X * 1.0).with_persistence(true),
|
|
|
|
|
ExternalImpulse::default().with_persistence(false),
|
|
|
|
|
LockedAxes::ROTATION_LOCKED.lock_translation_z(),
|
|
|
|
|
@ -151,7 +164,13 @@ fn init_obstacles(
|
|
|
|
|
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)
|
|
|
|
|
(
|
|
|
|
|
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
|
|
|
|
|
@ -168,12 +187,9 @@ fn init_obstacles(
|
|
|
|
|
commands.spawn((pipe.clone(), below));
|
|
|
|
|
commands.spawn((ground.clone(), floor));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn init_ui(
|
|
|
|
|
mut commands: Commands,
|
|
|
|
|
) {
|
|
|
|
|
fn init_ui(mut commands: Commands) {
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::Center,
|
|
|
|
|
@ -182,30 +198,27 @@ fn init_ui(
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
PlayerState::Stasis,
|
|
|
|
|
children![
|
|
|
|
|
Text::new("You Died"),
|
|
|
|
|
Text::new("Press R to Rewind"),
|
|
|
|
|
],
|
|
|
|
|
children![Text::new("You Died"), Text::new("Press R to Rewind"),],
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
fn start_game(_trigger: Trigger<Pointer<Click>>, mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::Center,
|
|
|
|
|
justify_self: JustifySelf::Center,
|
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Button,
|
|
|
|
|
// TODO: Add Pause (basically Stasis) state
|
|
|
|
|
PlayerState::Pause,
|
|
|
|
|
children![
|
|
|
|
|
Text::new("Go!"),
|
|
|
|
|
],
|
|
|
|
|
)).observe(start_game);
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::Center,
|
|
|
|
|
justify_self: JustifySelf::Center,
|
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Button,
|
|
|
|
|
// TODO: Add Pause (basically Stasis) state
|
|
|
|
|
PlayerState::Pause,
|
|
|
|
|
children![Text::new("Go!"),],
|
|
|
|
|
))
|
|
|
|
|
.observe(start_game);
|
|
|
|
|
|
|
|
|
|
fn start_rewind(trigger: Trigger<Pointer<Pressed>>, mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Rewind);
|
|
|
|
|
@ -215,30 +228,27 @@ fn init_ui(
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
commands.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::End,
|
|
|
|
|
justify_self: JustifySelf::Center,
|
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Button,
|
|
|
|
|
children![
|
|
|
|
|
Text::new("Rewind!"),
|
|
|
|
|
],
|
|
|
|
|
)).observe(start_rewind).observe(end_rewind);
|
|
|
|
|
commands
|
|
|
|
|
.spawn((
|
|
|
|
|
Node {
|
|
|
|
|
align_self: AlignSelf::End,
|
|
|
|
|
justify_self: JustifySelf::Center,
|
|
|
|
|
flex_direction: FlexDirection::Column,
|
|
|
|
|
..default()
|
|
|
|
|
},
|
|
|
|
|
Button,
|
|
|
|
|
children![Text::new("Rewind!"),],
|
|
|
|
|
))
|
|
|
|
|
.observe(start_rewind)
|
|
|
|
|
.observe(end_rewind);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Pause the game when the player presses "Escape"
|
|
|
|
|
fn pause_game(
|
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
|
) {
|
|
|
|
|
fn pause_game(mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Pause);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn un_pause_game(
|
|
|
|
|
mut next: ResMut<NextState<PlayerState>>,
|
|
|
|
|
) {
|
|
|
|
|
fn un_pause_game(mut next: ResMut<NextState<PlayerState>>) {
|
|
|
|
|
next.set(PlayerState::Alive);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -326,9 +336,7 @@ fn detect_dead(
|
|
|
|
|
"Only check if dead while alive"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if obstacles.iter().any(|obstacle| {
|
|
|
|
|
bird.intersects(obstacle)
|
|
|
|
|
}) {
|
|
|
|
|
if obstacles.iter().any(|obstacle| bird.intersects(obstacle)) {
|
|
|
|
|
next.set(PlayerState::Stasis);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@ -353,7 +361,7 @@ fn pause_bird(
|
|
|
|
|
|
|
|
|
|
fn camera_follow_bird(
|
|
|
|
|
bird: Single<&Transform, (With<Bird>, Changed<Transform>)>,
|
|
|
|
|
mut camera: Single<&mut Transform, (With<Camera>, Without<Bird>)>
|
|
|
|
|
mut camera: Single<&mut Transform, (With<Camera>, Without<Bird>)>,
|
|
|
|
|
) {
|
|
|
|
|
camera.translation.x = bird.translation.x;
|
|
|
|
|
}
|
|
|
|
|
|