Break some components out into 2d/3d variants for import

main
Elijah Voigt 3 months ago
parent 414ed60a7c
commit 8d891e74d0

74
Cargo.lock generated

@ -322,6 +322,27 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "avian2d"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7133547d9cc068d527d91fdcb8c8017c89b26ce05dbd30daef9f1ca64824495d"
dependencies = [
"arrayvec",
"avian_derive",
"bevy",
"bevy_heavy",
"bevy_math",
"bevy_transform_interpolation",
"bitflags 2.9.1",
"derive_more",
"itertools 0.13.0",
"nalgebra",
"parry2d",
"parry2d-f64",
"thread_local",
]
[[package]] [[package]]
name = "avian3d" name = "avian3d"
version = "0.3.1" version = "0.3.1"
@ -2248,6 +2269,7 @@ dependencies = [
name = "games" name = "games"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"avian2d",
"avian3d", "avian3d",
"bevy", "bevy",
"chrono", "chrono",
@ -3526,6 +3548,58 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "parry2d"
version = "0.17.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87edd53b1639e011e4765eecfceb0fa2c486da696dcdcfbc9a38bfc3574fb7e0"
dependencies = [
"approx",
"arrayvec",
"bitflags 2.9.1",
"downcast-rs 1.2.1",
"either",
"ena",
"log",
"nalgebra",
"num-derive",
"num-traits",
"ordered-float",
"rayon",
"rustc-hash 2.1.1",
"simba",
"slab",
"smallvec",
"spade",
"thiserror 1.0.69",
]
[[package]]
name = "parry2d-f64"
version = "0.17.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42dadff562001ff51eed809d7c75ac6f185d8cffc575d7b45a8bdc6ea6f1bf30"
dependencies = [
"approx",
"arrayvec",
"bitflags 2.9.1",
"downcast-rs 1.2.1",
"either",
"ena",
"log",
"nalgebra",
"num-derive",
"num-traits",
"ordered-float",
"rayon",
"rustc-hash 2.1.1",
"simba",
"slab",
"smallvec",
"spade",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "parry3d" name = "parry3d"
version = "0.17.6" version = "0.17.6"

@ -13,6 +13,9 @@ features = ["derive"]
[dependencies.avian3d] [dependencies.avian3d]
version = "0.3.1" version = "0.3.1"
[dependencies.avian2d]
version = "0.3.1"
[dependencies.bevy] [dependencies.bevy]
version = "0.16.1" version = "0.16.1"
features = ["wayland", "dynamic_linking"] features = ["wayland", "dynamic_linking"]

@ -18,7 +18,7 @@ fn main() {
( (
setup_physics_scene, setup_physics_scene,
setup_ui, setup_ui,
position_camera.after(setup_camera), position_camera.after(create_camera_3d),
), ),
) )
.add_systems( .add_systems(

@ -3,12 +3,19 @@ use super::*;
/// A good starting place for creating a game building on top of the base Bevy app /// A good starting place for creating a game building on top of the base Bevy app
pub struct BaseGamePlugin { pub struct BaseGamePlugin {
pub name: String, pub name: String,
pub game_type: GameType,
}
pub enum GameType {
Three,
Two,
} }
impl Default for BaseGamePlugin { impl Default for BaseGamePlugin {
fn default() -> Self { fn default() -> Self {
BaseGamePlugin { BaseGamePlugin {
name: "mygame".into(), name: "mygame".into(),
game_type: GameType::Three,
} }
} }
} }
@ -25,10 +32,13 @@ impl Plugin for BaseGamePlugin {
})) }))
.add_plugins(DebuggingPlugin) .add_plugins(DebuggingPlugin)
.add_plugins(MeshPickingPlugin) .add_plugins(MeshPickingPlugin)
.add_plugins(PhysicsPlugins::default())
.add_plugins(LoadingPlugin) .add_plugins(LoadingPlugin)
.add_plugins(BaseUiPlugin) .add_plugins(BaseUiPlugin);
.add_systems(Startup, setup_camera);
match self.game_type {
GameType::Two => app.add_systems(Startup, create_camera_2d),
GameType::Three => app.add_systems(Startup, create_camera_3d),
};
} }
} }
@ -47,7 +57,16 @@ pub fn toggle_state_visibility<S: States + Component>(
}); });
} }
// TODO: Rename to "create camera" pub fn create_camera_3d(mut commands: Commands) {
pub fn setup_camera(mut commands: Commands) { // 3d camera
commands.spawn((Camera3d { ..default() }, Camera { ..default() }, AmbientLight::default())); commands.spawn((
Camera3d { ..default() },
Camera { ..default() },
AmbientLight::default(),
));
}
pub fn create_camera_2d(mut commands: Commands) {
// 2d camera
commands.spawn((Camera2d, Camera { ..default() }));
} }

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

@ -2,6 +2,9 @@ use games::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(BaseGamePlugin { name: "hum".into() }) .add_plugins(BaseGamePlugin {
name: "hum".into(),
..default()
})
.run(); .run();
} }

@ -4,6 +4,7 @@ fn main() {
App::new() App::new()
.add_plugins(BaseGamePlugin { .add_plugins(BaseGamePlugin {
name: "tetris-rpg".into(), name: "tetris-rpg".into(),
..default()
}) })
.run(); .run();
} }

@ -16,6 +16,7 @@ fn main() {
App::new() App::new()
.add_plugins(BaseGamePlugin { .add_plugins(BaseGamePlugin {
name: "trees".into(), name: "trees".into(),
..default()
}) })
.add_plugins(MonologueAssetsPlugin) .add_plugins(MonologueAssetsPlugin)
.add_plugins(TreesDebugPlugin) .add_plugins(TreesDebugPlugin)
@ -30,7 +31,7 @@ fn main() {
init_trees, init_trees,
init_ui, init_ui,
load_monologues, load_monologues,
position_camera.after(setup_camera), position_camera.after(create_camera_3d),
), ),
) )
// When we're done loading, plant forest // When we're done loading, plant forest

@ -12,7 +12,6 @@ impl Plugin for DebuggingPlugin {
.init_resource::<Fps>() .init_resource::<Fps>()
.init_resource::<EntityCount>() .init_resource::<EntityCount>()
.init_resource::<Notice>() .init_resource::<Notice>()
.add_plugins(PhysicsDebugPlugin::default())
.add_systems(Startup, init_debug_ui) .add_systems(Startup, init_debug_ui)
.add_systems( .add_systems(
Update, Update,
@ -44,10 +43,6 @@ impl Plugin for DebuggingPlugin {
global: false, global: false,
default_color: MAGENTA.into(), default_color: MAGENTA.into(),
}) })
.add_systems(
Update,
toggle_physics_debug_render.run_if(state_changed::<DebuggingState>),
)
.add_systems(OnEnter(DebuggingState::On), enable_wireframe) .add_systems(OnEnter(DebuggingState::On), enable_wireframe)
.add_systems(OnExit(DebuggingState::On), disable_wireframe); .add_systems(OnExit(DebuggingState::On), disable_wireframe);
} }
@ -384,18 +379,6 @@ fn close_on_click(trigger: Trigger<Pointer<Click>>, mut query: Query<&mut Visibi
} }
} }
fn toggle_physics_debug_render(
state: Res<State<DebuggingState>>,
mut config_store: ResMut<GizmoConfigStore>,
) {
let (_, config) = config_store.config_mut::<PhysicsGizmos>();
*config = match state.get() {
// TODO: Not all, don't want to hide mesh
DebuggingState::On => PhysicsGizmos::all(),
DebuggingState::Off => PhysicsGizmos::none(),
};
}
pub fn debug_state_changes<S: States + std::fmt::Debug>(s: Res<State<S>>) { pub fn debug_state_changes<S: States + std::fmt::Debug>(s: Res<State<S>>) {
info!("State changed: {:?}", s); info!("State changed: {:?}", s);
} }

@ -4,6 +4,8 @@
mod base_game; mod base_game;
mod debug; mod debug;
mod loading; mod loading;
pub mod physics2d;
pub mod physics3d;
mod scheduling; mod scheduling;
mod ui; mod ui;
mod version; mod version;
@ -13,14 +15,13 @@ pub use std::f32::consts::PI;
pub use std::fmt::Display; pub use std::fmt::Display;
// Community libraries // Community libraries
pub use avian3d::prelude::*;
pub use bevy::{ pub use bevy::{
asset::{AssetLoader, LoadContext, LoadState, LoadedFolder, io::Reader}, asset::{AssetLoader, LoadContext, LoadState, LoadedFolder, io::Reader},
color::palettes::css::*, color::palettes::css::*,
gizmos::{aabb::AabbGizmoPlugin, light::LightGizmoPlugin}, gizmos::{aabb::AabbGizmoPlugin, light::LightGizmoPlugin},
input::{ input::{
ButtonState, ButtonState,
common_conditions::{input_just_pressed, input_pressed}, common_conditions::{input_just_pressed, input_just_released, input_pressed},
keyboard::KeyboardInput, keyboard::KeyboardInput,
mouse::MouseMotion, mouse::MouseMotion,
mouse::{MouseScrollUnit, MouseWheel}, mouse::{MouseScrollUnit, MouseWheel},

@ -0,0 +1,26 @@
use super::*;
pub use avian2d::prelude::*;
/// 2D Physics systems, resources, events, etc.
pub struct Physics2dPlugin;
impl Plugin for Physics2dPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(PhysicsDebugPlugin::default()).add_systems(
Update,
toggle_physics_debug_render.run_if(state_changed::<DebuggingState>),
);
}
}
fn toggle_physics_debug_render(
state: Res<State<DebuggingState>>,
mut config_store: ResMut<GizmoConfigStore>,
) {
let (_, config) = config_store.config_mut::<PhysicsGizmos>();
*config = match state.get() {
// TODO: Not all, don't want to hide mesh
DebuggingState::On => PhysicsGizmos::all(),
DebuggingState::Off => PhysicsGizmos::none(),
};
}

@ -0,0 +1,27 @@
use super::*;
pub use avian3d::prelude::*;
/// 3D Physics systems, resources, events, etc.
pub struct Physics3dPlugin;
impl Plugin for Physics3dPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((PhysicsDebugPlugin::default(), PhysicsPlugins::default()))
.add_systems(
Update,
toggle_physics_debug_render.run_if(state_changed::<DebuggingState>),
);
}
}
fn toggle_physics_debug_render(
state: Res<State<DebuggingState>>,
mut config_store: ResMut<GizmoConfigStore>,
) {
let (_, config) = config_store.config_mut::<PhysicsGizmos>();
*config = match state.get() {
// TODO: Not all, don't want to hide mesh
DebuggingState::On => PhysicsGizmos::all(),
DebuggingState::Off => PhysicsGizmos::none(),
};
}
Loading…
Cancel
Save