sprite working

This commit is contained in:
Back777space
2025-04-06 00:52:18 +02:00
22 changed files with 1424 additions and 79 deletions

1225
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,4 +6,5 @@ edition = "2024"
[dependencies] [dependencies]
bevy = { version="0.15.3", features = ["jpeg"]} bevy = { version="0.15.3", features = ["jpeg"]}
bevy_asset_loader = { version ="0.22.0", features = ["standard_dynamic_assets"] } bevy_asset_loader = { version ="0.22.0", features = ["standard_dynamic_assets"] }
bevy_egui = "0.33.0"
bevy_rapier3d = "0.29.0" bevy_rapier3d = "0.29.0"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/space1.blend Normal file

Binary file not shown.

BIN
assets/space2.blend Normal file

Binary file not shown.

View File

@@ -31,8 +31,6 @@ pub(crate) struct AudioAssets {}
#[derive(AssetCollection, Resource, Clone)] #[derive(AssetCollection, Resource, Clone)]
pub(crate) struct GltfAssets { pub(crate) struct GltfAssets {
#[asset(key = "house")]
pub(crate) house: Handle<Gltf>,
#[asset(key = "library", collection(typed, mapped))] #[asset(key = "library", collection(typed, mapped))]
pub(crate) library: HashMap<String, Handle<Gltf>>, pub(crate) library: HashMap<String, Handle<Gltf>>,
} }

View File

@@ -0,0 +1 @@

8
src/interaction/mod.rs Normal file
View File

@@ -0,0 +1,8 @@
use bevy::prelude::*;
mod objects;
mod ui;
pub fn plugin(app: &mut App) {
app.add_plugins((ui::plugin, objects::plugin));
}

View File

@@ -0,0 +1,40 @@
use bevy::{gltf::GltfMesh, math::Vec3A, prelude::*, render::mesh::MeshAabb};
use bevy_rapier3d::prelude::{Collider, RigidBody};
use crate::{GameState, asset_loading::GltfAssets};
pub fn plugin(app: &mut App) {
app.add_systems(OnEnter(GameState::Playing), spawn);
}
#[derive(Component)]
pub struct Interact;
fn spawn(mut commands: Commands, models: Res<Assets<Gltf>>, gltf_assets: Res<GltfAssets>) {
let hammer = gltf_assets
.library
.get("meshes/library/hammer.glb")
.unwrap();
let hammer = models.get(hammer).unwrap();
let asset = hammer.default_scene.as_ref().unwrap();
// hammer
commands
.spawn((
Transform::from_xyz(0.0, 100.0, 0.0).with_scale(Vec3::splat(0.1)),
Interact,
RigidBody::Dynamic,
SceneRoot(asset.clone()),
))
.with_children(|parent| {
parent
.spawn(Collider::cuboid(0.8, 10f32, 0.8))
.insert(Transform::from_xyz(0.0, -5.0, 0.0));
parent
.spawn(Collider::cuboid(1.0, 1.0, 4.5))
// Position the collider relative to the rigid-body.
.insert(Transform::from_xyz(0.0, 4.2, 1.0));
});
//tools
}

78
src/interaction/ui.rs Normal file
View File

@@ -0,0 +1,78 @@
use crate::GameState;
use crate::player::{Player, PlayerAction};
use crate::util::{single, single_mut};
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::{EguiContexts, EguiPlugin, egui};
use std::f32::consts::TAU;
use super::objects::Interact;
pub(super) fn plugin(app: &mut App) {
app.add_plugins(EguiPlugin)
.init_resource::<InteractionOpportunity>()
.add_systems(
Update,
(update_interaction_opportunities, display_interaction_prompt)
.chain()
.run_if(in_state(GameState::Playing)),
);
}
#[derive(Debug, Clone, Eq, PartialEq, Resource, Default)]
struct InteractionOpportunity(Option<Entity>);
fn update_interaction_opportunities(
player_query: Query<&GlobalTransform, With<Player>>,
parents: Query<&Parent>,
target_query: Query<
(Entity, &GlobalTransform),
(Without<Player>, Without<Camera>, With<Interact>),
>,
mut interaction_opportunity: ResMut<InteractionOpportunity>,
) {
interaction_opportunity.0 = None;
let player_transform = single!(player_query);
let (target_entity, target_transform) = single!(target_query);
let player_translation = player_transform.translation();
let target_translation = target_transform.translation();
if player_translation.distance(target_translation) <= 2.0 {
interaction_opportunity.0.replace(target_entity);
}
}
fn is_facing_target(
player: Vec3,
target: Vec3,
camera_transform: Transform,
camera: &Camera,
) -> bool {
let camera_to_player = camera_transform.forward();
let player_to_target = target - player;
let angle = camera_to_player.angle_between(player_to_target);
angle < TAU / 8.
}
fn display_interaction_prompt(
interaction_opportunity: Res<InteractionOpportunity>,
mut egui_contexts: EguiContexts,
action: Query<&PlayerAction, With<Player>>,
primary_windows: Query<&Window, With<PrimaryWindow>>,
) {
let Some(opportunity) = interaction_opportunity.0 else {
return;
};
let window = single!(primary_windows);
egui::Window::new("Interaction")
.collapsible(false)
.title_bar(false)
.auto_sized()
.fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.))
.show(egui_contexts.ctx_mut(), |ui| {
ui.label("E: Pick Up");
});
let action = single!(action);
}

View File

@@ -12,10 +12,14 @@ fn spawn_level(
gltf_assets: Res<GltfAssets> gltf_assets: Res<GltfAssets>
) { ) {
println!("LIBRARY: {:?}", gltf_assets.library); println!("LIBRARY: {:?}", gltf_assets.library);
let jumbo = gltf_assets.library.get("meshes/library/wall.glb").unwrap(); let mut x_offset = 0.0;
let gltf = models.get(jumbo).unwrap(); for mesh_name in ["corner_inside", "corner_outside", "wall", "door", "round_door", "round_hole"] {
let path = format!("meshes/library/space_{}.glb", mesh_name);
let handle = gltf_assets.library.get(&path).expect(&format!("Couldn't find {} in library", mesh_name));
let gltf = models.get(handle).expect(&format!("No model for {}", mesh_name));
// commands.spawn(SceneRoot(gltf.scenes[0].clone())); let asset = gltf.default_scene.as_ref().expect(&format!("No scene in {}", mesh_name));
let asset = gltf.default_scene.as_ref().unwrap(); commands.spawn((SceneRoot(asset.clone()), TransformBundle::from_transform(Transform::from_xyz(x_offset, 0.0, 0.0))));
commands.spawn(SceneRoot(asset.clone())); x_offset += 2.0;
}
} }

View File

@@ -1,20 +1,23 @@
use asset_loading::ImageAssets; use asset_loading::ImageAssets;
use bevy::prelude::*; use bevy::{prelude::*};
use bevy_rapier3d::prelude::*; use bevy_rapier3d::prelude::*;
mod asset_loading; mod asset_loading;
mod bevy_plugin; mod bevy_plugin;
mod interaction;
mod level_instantiation; mod level_instantiation;
mod main_menu; mod main_menu;
mod player; mod player;
mod util;
fn main() { fn main() {
App::new() App::new()
.add_plugins(( .add_plugins((
bevy_plugin::plugin, bevy_plugin::plugin,
asset_loading::plugin, asset_loading::plugin,
level_instantiation::map_plugin,
main_menu::plugin, main_menu::plugin,
level_instantiation::map_plugin,
interaction::plugin,
RapierPhysicsPlugin::<NoUserData>::default(), RapierPhysicsPlugin::<NoUserData>::default(),
RapierDebugRenderPlugin::default(), RapierDebugRenderPlugin::default(),
player::plugin, player::plugin,
@@ -47,16 +50,13 @@ fn setup(
MeshMaterial3d(materials.add(image.king.clone())), MeshMaterial3d(materials.add(image.king.clone())),
Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)), Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
)); ));
commands.spawn(( commands.spawn((RigidBody::Fixed, Collider::cylinder(0.1, 4.0)));
RigidBody::Fixed,
Collider::cylinder(0.1, 4.0)
));
// cube // cube
commands.spawn(( commands.spawn((
Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))), Mesh3d(meshes.add(Cuboid::new(1.0, 1.0, 1.0))),
MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))), MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
Transform::from_xyz(3.0, 0.5, 0.0), Transform::from_xyz(3.0, 0.5, 0.0),
RigidBody::Fixed, RigidBody::Fixed,
Collider::cuboid(0.5, 0.5, 0.5), Collider::cuboid(0.5, 0.5, 0.5),
)); ));
// light // light

View File

@@ -1,10 +1,11 @@
use bevy::{ use bevy::{
input::mouse::AccumulatedMouseMotion, prelude::*, input::mouse::AccumulatedMouseMotion, prelude::*, render::view::RenderLayers, transform, window::{self, PrimaryWindow}
render::view::RenderLayers, window::PrimaryWindow,
}; };
use bevy_rapier3d::prelude::*; use bevy_rapier3d::prelude::*;
use crate::{asset_loading::{FlashlightAssets, ImageAssets}, GameState}; pub mod toolbar;
use crate::{asset_loading::{FlashlightAssets}, GameState};
#[derive(Debug, Component)] #[derive(Debug, Component)]
pub struct Player; pub struct Player;
@@ -26,7 +27,8 @@ pub struct PlayerInput {
} }
pub fn plugin(app: &mut App) { pub fn plugin(app: &mut App) {
app.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor)) app.add_plugins(toolbar::plugin)
.add_systems(OnEnter(GameState::Playing), (init_player, hide_cursor))
.add_systems( .add_systems(
Update, Update,
(move_camera, handle_input).run_if(in_state(GameState::Playing)), (move_camera, handle_input).run_if(in_state(GameState::Playing)),
@@ -43,14 +45,17 @@ const STATIC_LAYER: usize = 1;
pub fn init_player( pub fn init_player(
mut commands: Commands, mut commands: Commands,
flashlights: Res<FlashlightAssets>, flashlights: Res<FlashlightAssets>,
images: Res<ImageAssets>, window: Query<&Window>,
) { ) {
let window = window.single();
commands commands
.spawn(( .spawn((
Player, Player,
PlayerAction::default(),
CameraSensitivity::default(), CameraSensitivity::default(),
PlayerInput::default(), PlayerInput::default(),
toolbar::Item::none(),
// rapier // rapier
RigidBody::Dynamic, RigidBody::Dynamic,
Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 0.5, 0.0), 0.5), Collider::capsule(Vec3::new(0.0, -0.5, 0.0), Vec3::new(0.0, 0.5, 0.0), 0.5),
@@ -60,7 +65,7 @@ pub fn init_player(
linear_damping: 6.0, linear_damping: 6.0,
angular_damping: 1.0, angular_damping: 1.0,
}, },
GravityScale(3.0),
Transform::from_xyz(0.0, 1.0, 0.0), Transform::from_xyz(0.0, 1.0, 0.0),
)) ))
.with_children(|parent| { .with_children(|parent| {
@@ -75,26 +80,30 @@ pub fn init_player(
// camera voor pitslampke // camera voor pitslampke
parent.spawn(( parent.spawn((
Camera3d::default(), Camera2d::default(),
Camera { Camera {
order: 1, order: 1,
..default() ..default()
}, },
RenderLayers::layer(STATIC_LAYER), RenderLayers::layer(STATIC_LAYER),
)); ));
// pitslampke let window_size = Vec2::new(window.resolution.width(), window.resolution.height());
let sprite_size = Vec2::new(101.0, 101.0);
let scale = 2.2;
let world_size = sprite_size * scale;
let offset = 70.0;
let mut transform = Transform::from_translation(
Vec3::new(window_size.x / 2.0 - world_size.x / 2.0 - offset, -window_size.y / 2.0 + world_size.y / 2.0, 0.0)
);
transform.scale = Vec3::new(scale, scale, 1.0);
parent.spawn(( parent.spawn((
Transform::from_xyz(-3.0, -3.0, 0.0), Sprite::from_image(flashlights.flash_hold_4.clone()),
Sprite { transform,
image: images.king.clone(), RenderLayers::layer(STATIC_LAYER),
..default()
},
RenderLayers::layer(STATIC_LAYER),
)); ));
}); });
commands.spawn(Sprite::from_image(images.king.clone()));
} }
fn hide_cursor(mut windows: Query<&mut Window, With<PrimaryWindow>>) { fn hide_cursor(mut windows: Query<&mut Window, With<PrimaryWindow>>) {
@@ -125,11 +134,20 @@ pub fn move_camera(
} }
} }
#[derive(Component, Debug, Clone, Copy, Eq, PartialEq, Hash, Reflect, Default)]
pub(crate) enum PlayerAction {
#[default]
Move,
Sprint,
Jump,
Interact,
}
pub fn handle_input( pub fn handle_input(
keyboard_input: Res<ButtonInput<KeyCode>>, keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&Transform, &mut PlayerInput), With<Player>>, mut query: Query<(&Transform, &mut PlayerInput, &mut PlayerAction), With<Player>>,
) { ) {
for (transform, mut input) in query.iter_mut() { for (transform, mut input, mut action) in query.iter_mut() {
let forward = transform.forward(); let forward = transform.forward();
let right = transform.right(); let right = transform.right();
let mut movement_direction = Vec3::ZERO; let mut movement_direction = Vec3::ZERO;
@@ -152,31 +170,32 @@ pub fn handle_input(
if keyboard_input.pressed(KeyCode::ShiftLeft) { if keyboard_input.pressed(KeyCode::ShiftLeft) {
movement_direction -= Vec3::Y; movement_direction -= Vec3::Y;
} }
if keyboard_input.pressed(KeyCode::KeyE) {
*action = PlayerAction::Interact
}
input.movement_direction = movement_direction.normalize_or_zero(); input.movement_direction = movement_direction.normalize_or_zero();
} }
} }
pub fn apply_player_movement( pub fn apply_player_movement(mut player_query: Query<(&PlayerInput, &mut Velocity), With<Player>>) {
mut player_query: Query<(&PlayerInput, &mut Velocity), With<Player>>,
) {
const SPEED: f32 = 3.0; const SPEED: f32 = 3.0;
const JUMP_FORCE: f32 = 4.0; const JUMP_FORCE: f32 = 4.0;
for (input, mut velocity) in player_query.iter_mut() { for (input, mut velocity) in player_query.iter_mut() {
let horizontal_movement = Vec3::new( let horizontal_movement = Vec3::new(
input.movement_direction.x * SPEED, input.movement_direction.x * SPEED,
0.0, 0.0,
input.movement_direction.z * SPEED, input.movement_direction.z * SPEED,
); );
velocity.linvel.x = horizontal_movement.x; velocity.linvel.x = horizontal_movement.x;
velocity.linvel.z = horizontal_movement.z; velocity.linvel.z = horizontal_movement.z;
if input.movement_direction.y > 0.0 { if input.movement_direction.y > 0.0 {
velocity.linvel.y = JUMP_FORCE; velocity.linvel.y = JUMP_FORCE;
} else if input.movement_direction.y < 0.0 { } else if input.movement_direction.y < 0.0 {
velocity.linvel.y = -SPEED; velocity.linvel.y = -SPEED;
} }
} }
} }

21
src/player/toolbar.rs Normal file
View File

@@ -0,0 +1,21 @@
use bevy::prelude::*;
use std::default::Default;
use crate::GameState;
use super::Player;
#[derive(Component, Default, Debug)]
pub struct Item(Option<Entity>);
impl Item {
pub fn none() -> Self {
Default::default()
}
}
pub fn plugin(app: &mut App) {
app.add_systems(Update, show_toolbar.run_if(in_state(GameState::Playing)));
}
fn show_toolbar(player_tool_query: Query<&Item, With<Player>>) {}

24
src/util.rs Normal file
View File

@@ -0,0 +1,24 @@
macro_rules! single {
($query:expr) => {
match $query.get_single() {
Ok(q) => q,
_ => {
return;
}
}
};
}
macro_rules! single_mut {
($query:expr) => {
match $query.get_single_mut() {
Ok(q) => q,
_ => {
return;
}
}
};
}
pub(crate) use single;
pub(crate) use single_mut;