WIP UI stuff and some cleanup
This commit is contained in:
parent
01251d735a
commit
9d5b554f7e
|
@ -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)
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,8 +16,14 @@ 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)]
|
||||||
|
@ -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),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
112
src/player.rs
112
src/player.rs
|
@ -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,22 +193,20 @@ 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 t = texture_atlases
|
||||||
let (texture, mut sprite) = query.single_mut();
|
.get(texture)
|
||||||
let t = texture_atlases
|
.expect("could not load player texture");
|
||||||
.get(texture)
|
if sprite.index < t.textures.len() - 1 {
|
||||||
.expect("could not load player texture");
|
sprite.index += 1;
|
||||||
if sprite.index < t.textures.len() - 1 {
|
} else {
|
||||||
sprite.index += 1;
|
sprite.index = 0;
|
||||||
} else {
|
}
|
||||||
sprite.index = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue