Menus, hurray!

This commit is contained in:
Daniel Flanagan 2024-07-29 16:29:39 -05:00
parent 9d5b554f7e
commit 8c26fa7eb8
7 changed files with 148 additions and 126 deletions

View file

@ -23,16 +23,7 @@ pub struct Fonts {
pub iosevkalytemin: Handle<Font>, pub iosevkalytemin: Handle<Font>,
} }
pub struct AssetPlugin; pub fn startup(
impl Plugin for AssetPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<AssetLoader>()
.add_systems(Startup, load_assets);
}
}
fn load_assets(
mut assets: ResMut<AssetLoader>, mut assets: ResMut<AssetLoader>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>, mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,

View file

@ -1,29 +1,24 @@
use bevy::prelude::*; use bevy::prelude::*;
use bevy::render::camera::Camera as BevyCamera; use bevy::render::camera::Camera;
pub struct CameraPlugin; pub fn startup(mut commands: Commands) {
#[derive(Component, Debug)]
pub struct CameraTarget;
impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, spawn)
.add_systems(PostUpdate, follow);
}
}
fn spawn(mut commands: Commands) {
let mut bundle = (Camera2dBundle::default(), IsDefaultUiCamera); let mut bundle = (Camera2dBundle::default(), IsDefaultUiCamera);
bundle.0.projection.scale = 0.5; bundle.0.projection.scale = 0.5;
commands.spawn(bundle); commands.spawn(bundle);
} }
fn follow( #[derive(Component, Debug)]
player: Query<&Transform, With<CameraTarget>>, pub struct Watched;
mut camera: Query<&mut Transform, (With<BevyCamera>, Without<CameraTarget>)>,
pub fn update(
watched: Query<&Transform, With<Watched>>,
mut camera: Query<&mut Transform, (With<Camera>, Without<Watched>)>,
) { ) {
if let Ok(player) = player.get_single() { if let Ok(mut camera) = camera.get_single_mut() {
camera.single_mut().translation = player.translation if let Ok(watched) = watched.get_single() {
camera.translation.x = watched.translation.x
} else {
camera.translation = Vec3::ZERO;
}
} }
} }

View file

@ -1,5 +1,5 @@
use bevy::audio::{AudioPlugin, SpatialScale}; use bevy::audio::{AudioPlugin, SpatialScale};
use bevy::prelude::*; use bevy::prelude::{default, *};
mod assets; mod assets;
mod camera; mod camera;
@ -8,50 +8,82 @@ mod movement;
mod player; mod player;
mod statue; mod statue;
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
pub enum Game {
#[default]
Running,
Paused,
}
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
pub enum View {
// #[default]
// LoadingScreen,
#[default]
MainMenu,
InGame,
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct MainMenuSet;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct InGameSet;
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
app.add_plugins( app.init_resource::<assets::AssetLoader>();
DefaultPlugins
.set(WindowPlugin { app.insert_state(View::default());
primary_window: Some(Window { app.insert_state(Game::default());
title: "Kodo Tag".into(),
mode: bevy::window::WindowMode::BorderlessFullscreen, app.add_plugins((DefaultPlugins
// resolution: (640. * 2., 360. * 2.).into(), .set(WindowPlugin {
..default() primary_window: Some(Window {
}), title: "Kodo Tag".into(),
mode: bevy::window::WindowMode::BorderlessFullscreen,
// resolution: (640. * 2., 360. * 2.).into(),
..default() ..default()
}) }),
.set(AudioPlugin { ..default()
default_spatial_scale: SpatialScale::new_2d(1.), })
global_volume: GlobalVolume::new(1.), .set(AudioPlugin {
..default() default_spatial_scale: SpatialScale::new_2d(1.),
}) global_volume: GlobalVolume::new(1.),
.set(ImagePlugin::default_nearest()), ..default()
) })
.add_plugins(( .set(ImagePlugin::default_nearest()),))
statue::Statue, .add_systems(OnEnter(View::MainMenu), main_menu::startup)
camera::CameraPlugin, .add_systems(OnExit(View::MainMenu), main_menu::exit)
assets::AssetPlugin, .add_systems(OnEnter(View::InGame), (player::startup, statue::startup))
player::Player, .add_systems(OnExit(View::InGame), (player::exit, statue::exit))
main_menu::MainMenu, .add_systems(Startup, (assets::startup, camera::startup))
movement::Movement, .add_systems(
)) Update,
.add_systems(Update, exit_on_escape) (
.insert_resource(ClearColor(Color::rgb(0.1, 0., 0.3))) (
.insert_resource(AmbientLight { player::sprite_select,
color: Color::rgb(1., 1., 1.), player::controls,
brightness: 1., movement::update_velocity_by_heading,
}); movement::resolve_velocity.after(movement::update_velocity_by_heading),
movement::ysort.after(movement::update_velocity_by_heading),
camera::update
.after(player::controls)
.after(movement::resolve_velocity),
)
.in_set(InGameSet)
.run_if(in_state(View::InGame)),
(main_menu::update)
.in_set(MainMenuSet)
.run_if(in_state(View::MainMenu)),
),
)
.insert_resource(ClearColor(Color::rgb(0.1, 0.1, 0.1)))
.insert_resource(AmbientLight {
color: Color::rgb(1., 1., 1.),
brightness: 1.,
});
app.run() app.run()
} }
fn exit_on_escape(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
) {
if keyboard_input.pressed(KeyCode::Escape) {
app_exit_events.send(bevy::app::AppExit);
}
}

View file

@ -5,23 +5,20 @@ use crate::assets::AssetLoader;
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct MainMenu; pub struct MainMenu;
impl Plugin for MainMenu { pub fn startup(mut commands: Commands, assets: Res<AssetLoader>) {
fn build(&self, app: &mut App) {
app.add_systems(PostStartup, setup_main_menu);
}
}
fn setup_main_menu(mut commands: Commands, assets: Res<AssetLoader>) {
commands commands
.spawn(NodeBundle { .spawn((
style: Style { MainMenu,
width: Val::Percent(100.0), NodeBundle {
height: Val::Percent(100.0), style: Style {
justify_content: JustifyContent::SpaceBetween, width: Val::Percent(100.0),
height: Val::Percent(100.0),
justify_content: JustifyContent::SpaceBetween,
..default()
},
..default() ..default()
}, },
..default() ))
})
.with_children(|parent| { .with_children(|parent| {
// text // text
parent.spawn(( parent.spawn((
@ -59,3 +56,21 @@ fn setup_main_menu(mut commands: Commands, assets: Res<AssetLoader>) {
}); });
}); });
} }
pub fn exit(mut commands: Commands, q: Query<Entity, With<MainMenu>>) {
for id in q.iter() {
commands.entity(id).despawn_recursive();
}
}
pub fn update(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut next_state: ResMut<NextState<crate::View>>,
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
) {
if keyboard_input.just_pressed(KeyCode::Escape) {
app_exit_events.send(bevy::app::AppExit);
} else if keyboard_input.pressed(KeyCode::Enter) {
next_state.set(crate::View::InGame)
}
}

View file

@ -1,5 +1,8 @@
use bevy::prelude::*; use bevy::prelude::*;
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
struct SystemSet;
#[derive(Component, Deref, DerefMut, Debug, Default)] #[derive(Component, Deref, DerefMut, Debug, Default)]
pub struct Velocity(pub Vec2); pub struct Velocity(pub Vec2);
@ -9,19 +12,19 @@ pub struct Heading(pub Vec2);
#[derive(Component, Deref, DerefMut, Debug, Default)] #[derive(Component, Deref, DerefMut, Debug, Default)]
pub struct Speed(pub f32); pub struct Speed(pub f32);
fn update_position(mut query: Query<(&Velocity, &mut Transform)>, time: Res<Time>) { pub fn resolve_velocity(mut query: Query<(&Velocity, &mut Transform)>, time: Res<Time>) {
for (velocity, mut transform) in query.iter_mut() { for (velocity, mut transform) in query.iter_mut() {
transform.translation += (velocity.0 * time.delta_seconds()).extend(0.); transform.translation += (velocity.0 * time.delta_seconds()).extend(0.);
} }
} }
fn mover_movement(mut query: Query<(&mut Velocity, &Speed, &Heading)>) { pub fn update_velocity_by_heading(mut query: Query<(&mut Velocity, &Speed, &Heading)>) {
if let Ok((mut velocity, speed, heading)) = query.get_single_mut() { if let Ok((mut velocity, speed, heading)) = query.get_single_mut() {
**velocity = heading.normalize_or_zero() * (**speed); **velocity = heading.normalize_or_zero() * (**speed);
} }
} }
fn ysort(mut q: Query<&mut Transform>) { pub fn ysort(mut q: Query<&mut Transform>) {
q.iter_mut() q.iter_mut()
.for_each(|mut tf| tf.translation.z = -tf.translation.y) .for_each(|mut tf| tf.translation.z = -tf.translation.y)
} }
@ -32,19 +35,3 @@ pub struct Mover {
pub heading: Heading, pub heading: Heading,
pub speed: Speed, pub speed: Speed,
} }
pub struct Movement;
impl Plugin for Movement {
fn build(&self, app: &mut App) {
// TODO: these systems probably needs ordering?
app.add_systems(
Update,
(
update_position,
mover_movement.after(update_position),
ysort.after(mover_movement),
),
);
}
}

View file

@ -2,7 +2,7 @@ use bevy::sprite::MaterialMesh2dBundle;
use bevy::sprite::Mesh2dHandle; use bevy::sprite::Mesh2dHandle;
use bevy::{prelude::*, window::PrimaryWindow}; use bevy::{prelude::*, window::PrimaryWindow};
use crate::camera::CameraTarget; use crate::camera::Watched;
use crate::{ use crate::{
assets::AssetLoader, assets::AssetLoader,
movement::{Heading, Mover, Speed, Velocity}, movement::{Heading, Mover, Speed, Velocity},
@ -17,14 +17,7 @@ pub struct Player;
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct Crosshair; pub struct Crosshair;
impl Plugin for Player { pub fn startup(
fn build(&self, app: &mut App) {
app.add_systems(PostStartup, spawn_player)
.add_systems(Update, (sprite_select, controls.after(sprite_select)));
}
}
fn spawn_player(
mut commands: Commands, mut commands: Commands,
assets: Res<AssetLoader>, assets: Res<AssetLoader>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
@ -37,7 +30,7 @@ fn spawn_player(
commands commands
.spawn(( .spawn((
Player, Player,
CameraTarget, Watched,
SpriteSheetBundle { SpriteSheetBundle {
texture, texture,
atlas: assets.images.player.clone().into(), atlas: assets.images.player.clone().into(),
@ -95,7 +88,7 @@ fn spawn_player(
}); });
} }
fn controls( pub fn controls(
mut commands: Commands, mut commands: Commands,
mut player_q: Query<(&mut Sprite, &mut TextureAtlas, &mut Heading, Entity), With<Player>>, mut player_q: Query<(&mut Sprite, &mut TextureAtlas, &mut Heading, Entity), With<Player>>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
@ -104,6 +97,7 @@ fn controls(
assets: Res<AssetLoader>, assets: Res<AssetLoader>,
win_q: Query<&Window, With<PrimaryWindow>>, win_q: Query<&Window, With<PrimaryWindow>>,
cam_q: Query<(&Camera, &GlobalTransform), With<Camera>>, cam_q: Query<(&Camera, &GlobalTransform), With<Camera>>,
mut next_state: ResMut<NextState<crate::View>>,
mut crosshair_q: Query<&mut Transform, With<Crosshair>>, mut crosshair_q: Query<&mut Transform, With<Crosshair>>,
) { ) {
let (mut sprite, mut texture, mut heading, player_entity) = player_q.single_mut(); let (mut sprite, mut texture, mut heading, player_entity) = player_q.single_mut();
@ -153,6 +147,7 @@ fn controls(
} }
for key in input.get_just_pressed() { for key in input.get_just_pressed() {
match key { match key {
KeyCode::Escape => next_state.set(crate::View::MainMenu),
KeyCode::Space => { KeyCode::Space => {
let child = commands let child = commands
.spawn(AudioSourceBundle { .spawn(AudioSourceBundle {
@ -192,13 +187,15 @@ fn controls(
} }
} }
fn sprite_select( pub fn sprite_select(
mut q: Query<(&Handle<TextureAtlasLayout>, &mut TextureAtlas), With<Player>>, mut q: Query<(&Handle<TextureAtlasLayout>, &mut TextureAtlas), With<Player>>,
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
texture_atlases: Res<Assets<TextureAtlasLayout>>, texture_atlases: Res<Assets<TextureAtlasLayout>>,
) { ) {
if let Ok((texture, mut sprite)) = q.get_single_mut() { if keyboard_input.just_pressed(KeyCode::KeyN) {
if keyboard_input.just_pressed(KeyCode::KeyN) { info!("pressed N");
if let Ok((texture, mut sprite)) = q.get_single_mut() {
info!("next tex: {}", sprite.index);
let t = texture_atlases let t = texture_atlases
.get(texture) .get(texture)
.expect("could not load player texture"); .expect("could not load player texture");
@ -210,3 +207,9 @@ fn sprite_select(
} }
} }
} }
pub fn exit(mut commands: Commands, q: Query<Entity, With<Player>>) {
for id in q.iter() {
commands.entity(id).despawn_recursive();
}
}

View file

@ -1,17 +1,10 @@
use bevy::prelude::*;
use crate::assets::AssetLoader; use crate::assets::AssetLoader;
use bevy::prelude::*;
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct Statue; pub struct Statue;
impl Plugin for Statue { pub fn startup(mut commands: Commands, assets: Res<AssetLoader>, asset_server: Res<AssetServer>) {
fn build(&self, app: &mut App) {
app.add_systems(PostStartup, spawn_statue);
}
}
fn spawn_statue(mut commands: Commands, assets: Res<AssetLoader>, asset_server: Res<AssetServer>) {
let texture = asset_server.load("img/Props.png"); let texture = asset_server.load("img/Props.png");
commands.spawn(( commands.spawn((
Statue, Statue,
@ -38,3 +31,9 @@ fn spawn_statue(mut commands: Commands, assets: Res<AssetLoader>, asset_server:
}, },
)); ));
} }
pub fn exit(mut commands: Commands, q: Query<Entity, With<Statue>>) {
for id in q.iter() {
commands.entity(id).despawn_recursive();
}
}