WIP UI stuff and some cleanup

This commit is contained in:
Daniel Flanagan 2024-07-29 14:33:14 -05:00
parent 01251d735a
commit 9d5b554f7e
5 changed files with 137 additions and 107 deletions

View file

@ -10,6 +10,14 @@ I've been trying for years to even get close, but I always choke on pathfinding.
I'm hoping I can leverage a good navmesh and pathfinding crate combination to do I'm hoping I can leverage a good navmesh and pathfinding crate combination to do
the heavy-lifting for me _plus_ it's a fun excuse to write more Rust. the heavy-lifting for me _plus_ it's a fun excuse to write more Rust.
## Running
On some AMD wayland systems, you may need to force the `RADV` driver using the ICD loader:
```shell_session
AMD_VULKAN_ICD=RADV cargo run
```
# To Do # To Do
- [ ] Basics (the stuff I can do just about anywhere) - [ ] Basics (the stuff I can do just about anywhere)

View file

@ -1,94 +1,29 @@
use bevy::{ use bevy::prelude::*;
prelude::*,
sprite::{MaterialMesh2dBundle, Mesh2dHandle},
window::PrimaryWindow,
};
use crate::player::Player;
use bevy::render::camera::Camera as BevyCamera; use bevy::render::camera::Camera as BevyCamera;
pub struct CameraPlugin; pub struct CameraPlugin;
#[derive(Component, Debug)]
pub struct CameraTarget;
impl Plugin for CameraPlugin { impl Plugin for CameraPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, spawn) app.add_systems(Startup, spawn)
.add_systems(PostUpdate, focus) .add_systems(PostUpdate, follow);
.add_systems(Update, y_sort)
.add_systems(Startup, spawn_crosshair)
.add_systems(Update, rotate_crosshair);
} }
} }
fn spawn(mut commands: Commands) { fn spawn(mut commands: Commands) {
let mut bundle = Camera2dBundle::default(); let mut bundle = (Camera2dBundle::default(), IsDefaultUiCamera);
bundle.projection.scale = 1. / 2.; bundle.0.projection.scale = 0.5;
commands.spawn(bundle); commands.spawn(bundle);
} }
#[derive(Component, Debug)] fn follow(
pub struct Crosshair; player: Query<&Transform, With<CameraTarget>>,
mut camera: Query<&mut Transform, (With<BevyCamera>, Without<CameraTarget>)>,
fn spawn_crosshair(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
let mesh = Mesh2dHandle(meshes.add(Capsule2d::new(3.0, 25.0))); if let Ok(player) = player.get_single() {
let material = materials.add(Color::hsl(360. * 1 as f32 / 3 as f32, 0.95, 0.7)); camera.single_mut().translation = player.translation
// let global_transform = GlobalTransform::from_xyz(
// 0.0, 20.0,
// // TODO: need some way to ensure this draws above everything else?
// // a UI layer or something?
// 1000.,
// );
let transform = Transform::from_xyz(
0.0, 0.0,
// TODO: need some way to ensure this draws above everything else?
// a UI layer or something?
1000.,
);
commands.spawn((
Crosshair,
MaterialMesh2dBundle {
// global_transform,
mesh,
material,
transform,
..default()
},
));
}
fn focus(
player: Query<&Transform, With<Player>>,
mut camera: Query<&mut Transform, (With<BevyCamera>, Without<Player>)>,
) {
let newpos = player.single().translation;
// println!("Cam pos: {newpos}");
camera.single_mut().translation = newpos
}
fn y_sort(mut q: Query<&mut Transform>) {
q.iter_mut()
.for_each(|mut tf| tf.translation.z = -tf.translation.y)
}
fn rotate_crosshair(
mut q: Query<&mut Transform, With<Crosshair>>,
win: Query<&Window, With<PrimaryWindow>>,
q_cam: Query<(&Camera, &GlobalTransform), With<Camera>>,
) {
let (camera, camera_transform) = q_cam.single();
if let Some(world_position) = win
.single()
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(&GlobalTransform::from_xyz(0., 0., 0.), cursor))
.map(|ray| ray.origin.truncate())
{
let mut t = q.single_mut();
t.look_at(Vec3::ZERO, world_position.extend(0.));
t.translation = camera_transform.translation();
} }
} }

View file

@ -16,10 +16,11 @@ fn main() {
.set(WindowPlugin { .set(WindowPlugin {
primary_window: Some(Window { primary_window: Some(Window {
title: "Kodo Tag".into(), title: "Kodo Tag".into(),
resolution: (640. * 2., 360. * 2.).into(), mode: bevy::window::WindowMode::BorderlessFullscreen,
..Default::default() // resolution: (640. * 2., 360. * 2.).into(),
..default()
}), }),
..Default::default() ..default()
}) })
.set(AudioPlugin { .set(AudioPlugin {
default_spatial_scale: SpatialScale::new_2d(1.), default_spatial_scale: SpatialScale::new_2d(1.),
@ -36,7 +37,8 @@ fn main() {
main_menu::MainMenu, main_menu::MainMenu,
movement::Movement, movement::Movement,
)) ))
.insert_resource(ClearColor(Color::rgb(0.3, 0., 0.5))) .add_systems(Update, exit_on_escape)
.insert_resource(ClearColor(Color::rgb(0.1, 0., 0.3)))
.insert_resource(AmbientLight { .insert_resource(AmbientLight {
color: Color::rgb(1., 1., 1.), color: Color::rgb(1., 1., 1.),
brightness: 1., brightness: 1.,
@ -44,3 +46,12 @@ fn main() {
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

@ -16,9 +16,15 @@ fn update_position(mut query: Query<(&Velocity, &mut Transform)>, time: Res<Time
} }
fn mover_movement(mut query: Query<(&mut Velocity, &Speed, &Heading)>) { fn mover_movement(mut query: Query<(&mut Velocity, &Speed, &Heading)>) {
let (mut velocity, speed, heading) = query.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>) {
q.iter_mut()
.for_each(|mut tf| tf.translation.z = -tf.translation.y)
}
#[derive(Bundle, Debug, Default)] #[derive(Bundle, Debug, Default)]
pub struct Mover { pub struct Mover {
@ -34,7 +40,11 @@ impl Plugin for Movement {
// TODO: these systems probably needs ordering? // TODO: these systems probably needs ordering?
app.add_systems( app.add_systems(
Update, Update,
(update_position, mover_movement.after(update_position)), (
update_position,
mover_movement.after(update_position),
ysort.after(mover_movement),
),
); );
} }
} }

View file

@ -1,16 +1,22 @@
use bevy::sprite::MaterialMesh2dBundle;
use bevy::sprite::Mesh2dHandle;
use bevy::{prelude::*, window::PrimaryWindow}; use bevy::{prelude::*, window::PrimaryWindow};
use crate::camera::CameraTarget;
use crate::{ use crate::{
assets::AssetLoader, assets::AssetLoader,
movement::{Heading, Mover, Speed, Velocity}, movement::{Heading, Mover, Speed, Velocity},
statue::Statue, statue::Statue,
}; };
const PLAYER_SPEED: f32 = 100.; const PLAYER_SPEED: f32 = 1000.;
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct Player; pub struct Player;
#[derive(Component, Debug)]
pub struct Crosshair;
impl Plugin for Player { impl Plugin for Player {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(PostStartup, spawn_player) app.add_systems(PostStartup, spawn_player)
@ -18,13 +24,20 @@ impl Plugin for Player {
} }
} }
fn spawn_player(mut commands: Commands, assets: Res<AssetLoader>, asset_server: Res<AssetServer>) { fn spawn_player(
mut commands: Commands,
assets: Res<AssetLoader>,
asset_server: Res<AssetServer>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let texture = asset_server.load("img/Player.png"); let texture = asset_server.load("img/Player.png");
let listener = SpatialListener::new(1.); let listener = SpatialListener::new(1.);
commands commands
.spawn(( .spawn((
Player, Player,
CameraTarget,
SpriteSheetBundle { SpriteSheetBundle {
texture, texture,
atlas: assets.images.player.clone().into(), atlas: assets.images.player.clone().into(),
@ -37,28 +50,83 @@ fn spawn_player(mut commands: Commands, assets: Res<AssetLoader>, asset_server:
speed: Speed(PLAYER_SPEED), speed: Speed(PLAYER_SPEED),
}, },
)) ))
.with_children(|parent| { .with_children(|player| {
parent.spawn((SpatialBundle::default(), listener.clone())); player.spawn((SpatialBundle::default(), listener.clone()));
player.spawn(TextBundle {
text: Text::from_section(
"You",
TextStyle {
font_size: 32.0,
font: assets.fonts.iosevkalytemin.clone(),
color: Color::WHITE,
..Default::default()
},
),
transform: Transform::from_translation(Vec3::new(30.0, -30.0, -1000.0)),
..Default::default()
});
let mesh = Mesh2dHandle(meshes.add(Capsule2d::new(3.0, 25.0)));
let material = materials.add(Color::hsl(360. * 1 as f32 / 3 as f32, 0.95, 0.7));
// let global_transform = GlobalTransform::from_xyz(
// 0.0, 20.0,
// // TODO: need some way to ensure this draws above everything else?
// // a UI layer or something?
// 1000.,
// );
let transform = Transform::from_xyz(
0.0, 0.0,
// TODO: need some way to ensure this draws above everything else?
// a UI layer or something?
1000.,
);
player.spawn((
Crosshair,
MaterialMesh2dBundle {
// global_transform,
mesh,
material,
transform,
..default()
},
));
}); });
} }
fn controls( fn controls(
mut commands: Commands, mut commands: Commands,
mut query: 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>,
input: Res<ButtonInput<KeyCode>>, input: Res<ButtonInput<KeyCode>>,
mouse_input: Res<ButtonInput<MouseButton>>, mouse_input: Res<ButtonInput<MouseButton>>,
assets: Res<AssetLoader>, assets: Res<AssetLoader>,
q_windows: Query<&Window, With<PrimaryWindow>>, win_q: Query<&Window, With<PrimaryWindow>>,
q_camera: Query<(&Camera, &GlobalTransform), With<Camera>>, cam_q: Query<(&Camera, &GlobalTransform), With<Camera>>,
mut crosshair_q: Query<&mut Transform, With<Crosshair>>,
) { ) {
let (mut sprite, mut texture, mut heading, player_entity) = query.single_mut(); let (mut sprite, mut texture, mut heading, player_entity) = player_q.single_mut();
let (camera, camera_transform) = q_camera.single(); let (camera, camera_transform) = cam_q.single();
let mouse_world_position = win_q
.single()
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(&GlobalTransform::from_xyz(0., 0., 0.), cursor))
.map(|ray| ray.origin.truncate());
if let Some(pos) = mouse_world_position {
if let Ok(mut t) = crosshair_q.get_single_mut() {
t.look_at(Vec3::ZERO, pos.extend(0.));
t.translation = camera_transform.translation();
}
}
**heading = Vec2::ZERO; **heading = Vec2::ZERO;
for button in mouse_input.get_just_pressed() { for button in mouse_input.get_just_pressed() {
match button { match button {
MouseButton::Left => { MouseButton::Left => {
if let Some(world_position) = q_windows if let Some(world_position) = win_q
.single() .single()
.cursor_position() .cursor_position()
.and_then(|cursor| camera.viewport_to_world(camera_transform, cursor)) .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor))
@ -125,15 +193,12 @@ fn controls(
} }
fn sprite_select( fn sprite_select(
mut query: 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>>,
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
texture_atlases: Res<Assets<TextureAtlasLayout>>, texture_atlases: Res<Assets<TextureAtlasLayout>>,
) { ) {
if keyboard_input.pressed(KeyCode::Escape) { if let Ok((texture, mut sprite)) = q.get_single_mut() {
app_exit_events.send(bevy::app::AppExit); if keyboard_input.just_pressed(KeyCode::KeyN) {
} else if keyboard_input.just_pressed(KeyCode::KeyN) {
let (texture, mut sprite) = query.single_mut();
let t = texture_atlases let t = texture_atlases
.get(texture) .get(texture)
.expect("could not load player texture"); .expect("could not load player texture");
@ -144,3 +209,4 @@ fn sprite_select(
} }
} }
} }
}