如何在Bevy中实现全局输入?第三方库使用遇阻求助
如何在Bevy中实现全局播放/暂停按键检测(窗口失焦时仍有效)
我正在用Bevy和Rust开发音乐播放器,需要实现全局输入检测——即使窗口未获得焦点,也能响应播放/暂停按键的按下操作。我尝试了三款第三方库,但都遇到了问题:
bevy_global_input插件添加失败
添加插件时触发语法错误,错误信息如下:error[E0277]: the trait bound `GlobalInputPlugins: Plugins<_>` is not satisfied --> src\gui\mod.rs:20:18 | 20 | .add_plugins(GlobalInputPlugins) | ----------- ^^^^^^^^^^^^^^^^^^ the trait `bevy::bevy_app::plugin::sealed::Plugins<_>` is not implemented for `GlobalInputPlugins`, which is required by `GlobalInputPlugins: Plugins<_>` | | | required by a bound introduced by this call | = help: the following other types implement trait `bevy::bevy_app::plugin::sealed::Plugins<Marker>`: `()` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker,)>` `(S0, S1)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1)>` `(S0, S1, S2)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2)>` `(S0, S1, S2, S3)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3)>` `(S0, S1, S2, S3, S4)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4)>` `(S0, S1, S2, S3, S4, S5)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5)>` `(S0, S1, S2, S3, S4, S5, S6)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5, P6)>` `(S0, S1, S2, S3, S4, S5, S6, S7)` implements `bevy::bevy_app::plugin::sealed::Plugins<(bevy::bevy_app::plugin::sealed::PluginsTupleMarker, P0, P1, P2, P3, P4, P5, P6, P7)>` and 8 others = note: required for `GlobalInputPlugins` to implement `Plugins<_>` note: required by a bound in `bevy::prelude::App::add_plugins` --> C:\Users\samsj\.cargo\registry\src\index.crates.io-6f17d22bba15001f\bevy_app-0.14.2\src\app.rs:543:52 | 543 | pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self { | ^^^^^^^^^^ required by this bound in `App::add_plugins`inputbot库无法适配Bevy帧逻辑
该库仅支持绑定按键按下的回调函数,无法直接检测按键是否在上一帧被按下。我尝试将其放入独立线程,通过通道传递数据到Bevy资源,但通道的Receiver导致结构体不满足Synctrait,无法作为Bevy资源使用。hookmap-core库同样存在Sync问题
与inputbot类似,需要在资源中持有Receiver,同样触发结构体不满足Synctrait的错误。
解决方案
1. 修复bevy_global_input的插件添加问题
这个错误通常是版本不兼容或调用方式错误导致的:
- 先检查
Cargo.toml中bevy_global_input的版本是否与你使用的Bevy版本匹配(比如Bevy 0.14需要对应支持该版本的插件)。 - 若版本没问题,尝试修改插件添加方式为实例化调用:
多数Bevy插件需要通过.add_plugins(GlobalInputPlugins::default())default()创建实例,而非直接传递类型。
2. 用线程安全封装适配inputbot/hookmap-core
针对通道Receiver的Sync问题,可以用Arc<Mutex<>>包裹接收器,让结构体满足Send + Sync要求。以inputbot为例:
use bevy::prelude::*; use inputbot::{KeybdKey}; use std::sync::{Arc, Mutex}; use std::thread; // 全局输入资源:用Arc<Mutex>包裹触发标记 #[derive(Resource)] struct GlobalInput { play_pause_triggered: Arc<Mutex<bool>>, } fn setup_global_input(mut commands: Commands) { let trigger = Arc::new(Mutex::new(false)); let trigger_clone = trigger.clone(); // 启动独立线程监听全局按键 thread::spawn(move || { KeybdKey::MediaPlayPauseKey.bind(move || { *trigger_clone.lock().unwrap() = true; }); inputbot::handle_input_events(); }); commands.insert_resource(GlobalInput { play_pause_triggered: trigger }); } // 在Bevy系统中检测并处理触发事件 fn check_play_pause(mut input: ResMut<GlobalInput>, mut audio_state: ResMut<AudioPlaybackState>) { let mut triggered = input.play_pause_triggered.lock().unwrap(); if *triggered { audio_state.toggle(); *triggered = false; // 重置标记避免重复触发 } } fn main() { App::new() .add_plugins(DefaultPlugins) .add_startup_system(setup_global_input) .add_system(check_play_pause) .run(); }
3. 替代方案:使用global-hotkey库
如果上述库适配麻烦,可以试试global-hotkey,它支持跨平台全局快捷键,且适配Bevy逻辑更顺畅:
use bevy::prelude::*; use global_hotkey::{GlobalHotKeyManager, HotKey, Key}; use std::sync::{Arc, Mutex}; #[derive(Resource)] struct HotkeyTrigger(Arc<Mutex<bool>>); fn setup_hotkeys(mut commands: Commands) { let trigger = Arc::new(Mutex::new(false)); let trigger_clone = trigger.clone(); let mut manager = GlobalHotKeyManager::new().unwrap(); let play_pause_hotkey = HotKey::new(None, Key::MediaPlayPause); manager.register(play_pause_hotkey, move || { *trigger_clone.lock().unwrap() = true; }).unwrap(); commands.insert_resource(HotkeyTrigger(trigger)); } fn handle_hotkey_trigger(mut hotkey: ResMut<HotkeyTrigger>, mut audio_state: ResMut<AudioPlaybackState>) { let mut triggered = hotkey.0.lock().unwrap(); if *triggered { audio_state.toggle(); *triggered = false; } } fn main() { App::new() .add_plugins(DefaultPlugins) .add_startup_system(setup_hotkeys) .add_system(handle_hotkey_trigger) .run(); }
内容的提问来源于stack exchange,提问作者Mr. Sja
相关产品推荐
相关产品推荐

