feat: Player is now using leafwing-input-manager

This commit is contained in:
Alexey 2026-03-03 16:49:28 +03:00
commit 957717671a
4 changed files with 68 additions and 24 deletions

View file

@ -1,8 +1,8 @@
use std::{any::{Any, TypeId}, collections::HashMap, hash::Hash, marker::PhantomData}; use std::{any::{Any, TypeId}, collections::HashMap, hash::Hash, marker::PhantomData};
use bevy::prelude::*; use bevy::{prelude::*, reflect::GetTypeRegistration};
use bevy_common_assets::toml::TomlAssetPlugin; use bevy_common_assets::toml::TomlAssetPlugin;
use leafwing_input_manager::{Actionlike, prelude::{Axislike, Buttonlike, DualAxislike, InputMap}}; use leafwing_input_manager::{Actionlike, plugin::InputManagerPlugin, prelude::*};
use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde::{Deserialize, Serialize, de::DeserializeOwned};
const INPUT_ASSET_EXTENSIONS: [&'static str; 1] = ["input.toml"]; const INPUT_ASSET_EXTENSIONS: [&'static str; 1] = ["input.toml"];
@ -205,13 +205,42 @@ impl<Name> From<InputMap<Name>> for InputAsset<Name>
} }
} }
#[derive(Debug, Default)] #[derive(Resource, Deref)]
pub struct InputAssetPlugin<T> (PhantomData<T>); pub struct InputAssetHandle<T: Sized + Hash + Eq + Reflect + TypePath + Actionlike> (Option<Handle<InputAsset<T>>>);
#[derive(Debug)]
pub struct InputAssetPlugin<T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration> {
_phantom: PhantomData<T>,
extensions: &'static [&'static str],
}
impl<T> InputAssetPlugin<T>
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration {
pub fn new(extensions: &'static [&'static str]) -> Self {
Self {
_phantom: PhantomData,
extensions,
}
}
}
impl<T> Default for InputAssetPlugin<T>
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration {
fn default() -> Self {
Self {
_phantom: PhantomData,
extensions: &INPUT_ASSET_EXTENSIONS,
}
}
}
impl<T> Plugin for InputAssetPlugin<T> impl<T> Plugin for InputAssetPlugin<T>
where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned where T: Sized + Hash + Eq + Reflect + TypePath + Actionlike + DeserializeOwned + GetTypeRegistration
{ {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_plugins(TomlAssetPlugin::<InputAsset<T>>::new(&INPUT_ASSET_EXTENSIONS)); app.add_plugins((
TomlAssetPlugin::<InputAsset<T>>::new(&self.extensions),
InputManagerPlugin::<T>::default()
));
} }
} }

View file

@ -5,16 +5,32 @@ pub mod input;
mod tests; mod tests;
use bevy::prelude::*; use bevy::prelude::*;
use leafwing_input_manager::prelude::*;
use serde::{Deserialize, Serialize};
pub struct ExpeditionPlugin; pub struct ExpeditionPlugin;
pub enum InputActions { #[derive(Actionlike, PartialEq, Eq, Hash, Debug, Clone, Reflect, Serialize, Deserialize)]
MoveLeft, pub enum InputAction {
MoveRight, #[actionlike(Axis)]
Move,
ToggleInventory, ToggleInventory,
Interact, Interact,
} }
impl InputAction {
pub fn default_input_map() -> InputMap<Self> {
let input_map = InputMap::default()
.with_axis(Self::Move, VirtualAxis::ad())
.with_axis(Self::Move, GamepadAxis::LeftStickX)
.with(Self::ToggleInventory, KeyCode::KeyI)
.with(Self::ToggleInventory, GamepadButton::Select)
.with(Self::Interact, KeyCode::KeyE)
.with(Self::Interact, GamepadButton::East);
input_map
}
}
fn camera_bundle() -> impl Bundle { fn camera_bundle() -> impl Bundle {
( (
Camera2d, Camera2d,
@ -38,7 +54,8 @@ fn setup_global(mut commands: Commands) {
impl Plugin for ExpeditionPlugin { impl Plugin for ExpeditionPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, (player::setup_player, setup_global)) app.add_plugins(input::InputAssetPlugin::<InputAction>::default())
.add_systems(Startup, (player::setup_player, setup_global))
.add_systems(Update, player::handle_input); .add_systems(Update, player::handle_input);
} }
} }

View file

@ -1,4 +1,7 @@
use bevy::prelude::*; use bevy::prelude::*;
use leafwing_input_manager::prelude::*;
use crate::InputAction as Action;
#[derive(Component)] #[derive(Component)]
pub struct Player { pub struct Player {
@ -14,6 +17,7 @@ fn player_bundle(asset_server: &Res<AssetServer>) -> impl Bundle {
}, },
Sprite::from_image(image), Sprite::from_image(image),
Transform::from_xyz(0f32, 0f32, 1f32), Transform::from_xyz(0f32, 0f32, 1f32),
Action::default_input_map(),
) )
} }
@ -22,20 +26,16 @@ pub fn setup_player(mut commands: Commands, asset_server: Res<AssetServer>) {
} }
pub fn handle_input( pub fn handle_input(
key_input: Res<ButtonInput<KeyCode>>,
time: Res<Time>, time: Res<Time>,
player: Query<(&Player, &mut Transform, &mut Sprite)>, mut player: Query<(&Player, &ActionState<Action>, &mut Transform, &mut Sprite)>,
) { ) {
let exclusive_pressed = key_input.pressed(KeyCode::KeyA) != key_input.pressed(KeyCode::KeyD); let player = player.single_mut().expect("Player should be single");
let direction = if !exclusive_pressed { 0f32 } let (Player {speed}, action_state, mut transform, mut sprite) = player;
else if key_input.pressed(KeyCode::KeyA) { -1f32 }
else if key_input.pressed(KeyCode::KeyD) { 1f32 } let direction = action_state.clamped_value(&Action::Move);
else { unreachable!() };
for (Player {speed}, mut transform, mut sprite) in player {
transform.translation.x += direction * speed * time.delta_secs(); transform.translation.x += direction * speed * time.delta_secs();
if direction != 0f32 { if direction != 0f32 {
sprite.flip_x = direction < 0f32; sprite.flip_x = direction < 0f32;
} }
} }
}

View file

@ -1,5 +1,3 @@
use std::any::{Any, TypeId};
use super::*; use super::*;
use leafwing_input_manager::prelude::*; use leafwing_input_manager::prelude::*;