diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..0346bdb --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,13 @@ +//! The general app camera module, not to be confused with the `game::camera`ยน module. +//! +//! 1: I can't link to out of scope private items, so you'll have to navigate there yourself. +use bevy::prelude::*; + +/// Sets up the camera for the entire app, with a starting position for the game. (TODO: probably change that) +/// This camera is also used for menu rendering etc. +pub fn setup(mut c: Commands) { + c.spawn(( + Camera3d::default(), + Transform::from_xyz(-20., 15., 0.).looking_at(Vec3::ZERO, Vec3::Y), + )); +} diff --git a/src/cleanup.rs b/src/cleanup.rs new file mode 100644 index 0000000..6e70709 --- /dev/null +++ b/src/cleanup.rs @@ -0,0 +1,14 @@ +//! Defines generic despawn/cleanup function and cleanup marker components + +use bevy::prelude::*; + +/// Despawn/cleanup every entity with `T`. +/// See the [bevy cheatbook's cleanup example](https://bevy-cheatbook.github.io/patterns/generic-systems.html#example-cleanup). +pub fn despawn(mut c: Commands, q: Query>) { + for e in q { + c.entity(e).despawn(); + } +} + +#[derive(Component)] +pub struct Scene; diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..a8cc928 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,23 @@ +use bevy::prelude::*; + +use crate::{ + AppState, + cleanup::{self, despawn}, +}; + +mod camera; +mod scene; + +/// Gameplay system set. All functions in this control the gameplay (duh). +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +struct GameplaySet; + +pub fn plugin(app: &mut App) { + app.add_plugins(camera::plugin) + .add_systems(OnEnter(AppState::Ingame), scene::setup.in_set(GameplaySet)) + .add_systems( + OnExit(AppState::Ingame), + despawn::.in_set(GameplaySet), + ); + app.configure_sets(Update, GameplaySet.run_if(in_state(AppState::Ingame))); +} diff --git a/src/game/camera.rs b/src/game/camera.rs new file mode 100644 index 0000000..cb4c1fe --- /dev/null +++ b/src/game/camera.rs @@ -0,0 +1,65 @@ +//! Sets up the game's camera controls etc. + +use bevy::{ + input::mouse::{AccumulatedMouseMotion, MouseButtonInput}, + prelude::*, +}; + +use super::GameplaySet; + +pub fn plugin(app: &mut App) { + app.init_resource::().add_systems( + Update, + ( + detect_mouse_button_press, + panning_basic.after(detect_mouse_button_press), + ) + .in_set(GameplaySet), + ); +} + +/// Resource to store current mouse button state. +#[derive(Resource, Default)] +struct MouseButtonsPressed { + left: bool, +} + +/// Detect whether mouse buttons (currently only left) are pressed. +fn detect_mouse_button_press( + mut button_events: EventReader, + mut pressed: ResMut, +) { + for event in button_events.read() { + if event.button == MouseButton::Left { + pressed.left = dbg!(event.state.is_pressed()); + } + } +} + +/// is SUPPOSED to do proper panning +/// i hate 3d math +fn panning_basic( + accumulated_mouse_motion: Res, + mut cam_transform: Single<&mut Transform, With>, + pressed: Res, +) { + if pressed.left && accumulated_mouse_motion.delta != Vec2::ZERO { + let yaw = Quat::from_rotation_y(accumulated_mouse_motion.delta.x / 150.); + // cam_transform.rotate_around(Vec3::ZERO, yaw); + cam_transform.rotation *= yaw; + + let pitch = Quat::from_rotation_x(accumulated_mouse_motion.delta.y / 150.); + let new_rot = cam_transform.rotation * pitch; + // cam_transform.rotate_around(Vec3::ZERO, pitch); + // cam_transform.rotation *= pitch; + let up_vector = new_rot * Vec3::Y; + if up_vector.y > 0.0 { + cam_transform.rotation = new_rot; + } + } + // ??????? + // TODO: figure out/understand how they do it https://github.com/The-DevBlog/bevy_third_person_camera/blob/a7c6b458573fcb0730b65eda6507ca27fb58f571/src/mouse.rs#L29-L74 + // + let rot_matrix = Mat3::from_quat(cam_transform.rotation); + cam_transform.translation = rot_matrix.mul_vec3(Vec3::new(0.0, 0.0, 20.)); +} diff --git a/src/game/scene.rs b/src/game/scene.rs new file mode 100644 index 0000000..28c0bf4 --- /dev/null +++ b/src/game/scene.rs @@ -0,0 +1,25 @@ +use bevy::prelude::*; + +use crate::cleanup; + +const NORMALSPUR: f32 = 1.435; + +pub fn setup( + mut c: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // spawn in floor plane + c.spawn(( + Mesh3d(meshes.add(Plane3d::default().mesh().size(128., 128.))), + MeshMaterial3d(materials.add(Color::srgb(0.3, 0.8, 0.4))), + cleanup::Scene, + )); + + // track (temporary) + c.spawn(( + Mesh3d(meshes.add(Cuboid::new(NORMALSPUR, 0.25, 96.))), + MeshMaterial3d(materials.add(Color::BLACK)), + cleanup::Scene, + )); +} diff --git a/src/main.rs b/src/main.rs index f0712df..5638344 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,23 @@ use bevy::prelude::*; +mod camera; +mod cleanup; +mod game; + +#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] +#[allow(unused)] +enum AppState { + Menus, + #[default] + Ingame, + PostGame, +} + fn main() { App::new() .add_plugins(DefaultPlugins) - .add_systems(Startup, yeet) + .init_state::() + .add_plugins(game::plugin) + .add_systems(Startup, camera::setup) .run(); } - -fn yeet( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -) { - commands.spawn(( - Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), - MeshMaterial3d(materials.add(Color::srgb_u8(255, 255, 255))), - Transform::from_xyz(0.0, 0.5, 0.0), - )); - commands.spawn(( - Mesh3d(meshes.add(Plane3d::new(Vec3::Y, vec2(4., 4.)))), - MeshMaterial3d(materials.add(Color::srgb_u8(255, 255, 255))), - Transform::from_xyz(0., 0., 0.), - )); - commands.spawn(( - PointLight { - shadows_enabled: true, - ..default() - }, - Transform::from_xyz(4.0, 8.0, 4.0), - )); - commands.spawn(( - Camera3d::default(), - Transform::from_xyz(-4.5, 3.5, 4.5).looking_at(Vec3::ZERO, Vec3::Y), - )); -}