beginning of interaction
This commit is contained in:
3571
Cargo.lock
generated
3571
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,3 +6,6 @@ edition = "2024"
|
||||
[dependencies]
|
||||
bevy = { version="0.15.3", features = ["jpeg"]}
|
||||
bevy_asset_loader = { version ="0.22.0", features = ["standard_dynamic_assets"] }
|
||||
bevy_egui = "0.33.0"
|
||||
bevy_rapier3d = "0.29.0"
|
||||
bevy_xpbd_3d = "0.5.0"
|
||||
|
||||
BIN
assets/meshes/library/hammer.glb
Normal file
BIN
assets/meshes/library/hammer.glb
Normal file
Binary file not shown.
1
src/interaction/dialog.rs
Normal file
1
src/interaction/dialog.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
8
src/interaction/mod.rs
Normal file
8
src/interaction/mod.rs
Normal 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));
|
||||
}
|
||||
28
src/interaction/objects.rs
Normal file
28
src/interaction/objects.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
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, 1.0, 0.0).with_scale(Vec3::splat(0.1)),
|
||||
Interact,
|
||||
SceneRoot(asset.clone()),
|
||||
));
|
||||
|
||||
//tools
|
||||
}
|
||||
78
src/interaction/ui.rs
Normal file
78
src/interaction/ui.rs
Normal 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);
|
||||
}
|
||||
@@ -3,20 +3,23 @@ use bevy::prelude::*;
|
||||
|
||||
mod asset_loading;
|
||||
mod bevy_plugin;
|
||||
mod interaction;
|
||||
mod level_instantiation;
|
||||
mod main_menu;
|
||||
mod physics;
|
||||
mod player;
|
||||
mod util;
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins((
|
||||
bevy_plugin::plugin,
|
||||
asset_loading::plugin,
|
||||
main_menu::plugin,
|
||||
physics::plugin,
|
||||
level_instantiation::map_plugin,
|
||||
player::plugin,
|
||||
main_menu::plugin,
|
||||
interaction::plugin,
|
||||
))
|
||||
.init_state::<GameState>()
|
||||
.add_systems(OnEnter(GameState::Playing), setup)
|
||||
|
||||
@@ -52,6 +52,7 @@ pub fn init_player(
|
||||
commands
|
||||
.spawn((
|
||||
Player,
|
||||
PlayerAction::default(),
|
||||
CameraSensitivity::default(),
|
||||
Transform::from_xyz(0.0, 1.0, 0.0),
|
||||
Visibility::default(),
|
||||
@@ -124,37 +125,62 @@ 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(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut query: Query<(&Transform, &mut AccumulatedInput, &mut Velocity), With<Player>>,
|
||||
mut query: Query<
|
||||
(
|
||||
&Transform,
|
||||
&mut AccumulatedInput,
|
||||
&mut Velocity,
|
||||
&mut PlayerAction,
|
||||
),
|
||||
With<Player>,
|
||||
>,
|
||||
) {
|
||||
const SPEED: f32 = 2.0;
|
||||
for (transform, mut input, mut velocity) in query.iter_mut() {
|
||||
for (transform, mut input, mut velocity, mut action) in query.iter_mut() {
|
||||
let forward = transform.forward(); // Forward direction (z axis)
|
||||
let right = transform.right(); // Right direction (x axis)
|
||||
|
||||
if keyboard_input.pressed(KeyCode::KeyW) {
|
||||
input.x += forward.x;
|
||||
input.z += forward.z;
|
||||
*action = PlayerAction::Move;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyS) {
|
||||
input.x -= forward.x;
|
||||
input.z -= forward.z;
|
||||
*action = PlayerAction::Move;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyA) {
|
||||
input.x -= right.x;
|
||||
input.z -= right.z;
|
||||
*action = PlayerAction::Move;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyD) {
|
||||
input.x += right.x;
|
||||
input.z += right.z;
|
||||
*action = PlayerAction::Move;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::Space) {
|
||||
input.y += 1.0;
|
||||
*action = PlayerAction::Jump;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::ShiftLeft) {
|
||||
input.y -= 1.0;
|
||||
}
|
||||
if keyboard_input.pressed(KeyCode::KeyE) {
|
||||
*action = PlayerAction::Interact
|
||||
}
|
||||
velocity.0 = input.normalize_or_zero() * SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/util.rs
Normal file
24
src/util.rs
Normal 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;
|
||||
Reference in New Issue
Block a user