输入处理
本教程基于Bevy官方示例,提供了完整的输入系统开发指南。
教程结构
基础教程 (Bevy_Input_基础教程.md)
- 键盘输入: 基础按键、事件监听、修饰键、字符输入
- 鼠标输入: 按钮、移动、滚轮、事件监听、光标控制
- 触摸输入: 基础触摸、事件监听、多点触摸
- 游戏手柄输入: 基础手柄、事件监听、连接管理
- 文本输入: IME支持、多语言输入、特殊键处理
高级教程 (Bevy_Input_高级教程.md)
- 游戏手柄震动: 力反馈、自定义震动模式
- 输入映射和绑定: 动态绑定、配置管理
- 输入事件处理: 优先级、过滤、转换
- 输入状态管理: 状态机、上下文管理
- 自定义输入系统: 扩展输入类型、模拟回放
- 输入优化: 缓存、批处理、预测插值
核心概念速查
基础输入资源
// 键盘输入
Res<ButtonInput<KeyCode>>
// 鼠标输入
Res<ButtonInput<MouseButton>>
Res<AccumulatedMouseMotion>
Res<AccumulatedMouseScroll>
// 触摸输入
Res<Touches>
// 游戏手柄
Query<(Entity, &Gamepad)>输入事件
// 键盘事件
EventReader<KeyboardInput>
// 鼠标事件
EventReader<MouseButtonInput>
EventReader<MouseMotion>
EventReader<CursorMoved>
EventReader<MouseWheel>
// 触摸事件
EventReader<TouchInput>
// 游戏手柄事件
EventReader<GamepadConnectionEvent>
EventReader<GamepadAxisChangedEvent>
EventReader<GamepadButtonChangedEvent>输入状态检查
// 按键状态
keyboard_input.pressed(KeyCode::KeyA) // 正在按下
keyboard_input.just_pressed(KeyCode::KeyA) // 刚刚按下
keyboard_input.just_released(KeyCode::KeyA) // 刚刚释放
// 组合键检查
input.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight])常用模式
1. 基础输入处理
fn input_system(keyboard_input: Res<ButtonInput<KeyCode>>) {
if keyboard_input.pressed(KeyCode::KeyW) {
// 处理持续输入
}
if keyboard_input.just_pressed(KeyCode::Space) {
// 处理单次输入
}
}2. 组合键处理
fn combo_input_system(input: Res<ButtonInput<KeyCode>>) {
let ctrl = input.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]);
let shift = input.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]);
if ctrl && shift && input.just_pressed(KeyCode::KeyS) {
// 处理 Ctrl+Shift+S
}
}3. 游戏手柄输入
fn gamepad_system(gamepads: Query<(Entity, &Gamepad)>) {
for (entity, gamepad) in &gamepads {
if gamepad.just_pressed(GamepadButton::South) {
// 处理按钮按下
}
let left_stick_x = gamepad.get(GamepadAxis::LeftStickX).unwrap();
if left_stick_x.abs() > 0.01 {
// 处理摇杆输入
}
}
}4. 触摸输入
fn touch_system(touches: Res<Touches>) {
for touch in touches.iter_just_pressed() {
info!("触摸按下 - ID: {}, 位置: {}", touch.id(), touch.position());
}
for touch in touches.iter() {
if touches.pressed(touch.id()) {
// 处理持续触摸
}
}
}5. 输入事件监听
fn event_system(mut events: EventReader<KeyboardInput>) {
for event in events.read() {
match event.logical_key {
Key::Character(c) => {
// 处理字符输入
}
Key::Enter => {
// 处理回车键
}
_ => {}
}
}
}高级功能
1. 游戏手柄震动
fn rumble_system(
gamepads: Query<(Entity, &Gamepad)>,
mut rumble_requests: EventWriter<GamepadRumbleRequest>,
) {
for (entity, gamepad) in &gamepads {
if gamepad.just_pressed(GamepadButton::North) {
rumble_requests.send(GamepadRumbleRequest::Add {
gamepad: entity,
intensity: GamepadRumbleIntensity::strong_motor(0.5),
duration: Duration::from_secs(2),
});
}
}
}2. 输入映射系统
#[derive(Resource)]
struct InputMapping {
keyboard_bindings: HashMap<InputAction, Vec<KeyCode>>,
mouse_bindings: HashMap<InputAction, Vec<MouseButton>>,
gamepad_bindings: HashMap<InputAction, Vec<GamepadButton>>,
}
fn input_processing_system(
keyboard_input: Res<ButtonInput<KeyCode>>,
input_mapping: Res<InputMapping>,
) {
for (action, keys) in &input_mapping.keyboard_bindings {
let is_pressed = keys.iter().any(|key| keyboard_input.pressed(*key));
// 处理映射的输入
}
}3. 输入状态机
#[derive(States)]
enum InputState {
Normal,
Menu,
Combat,
}
fn input_state_system(
mut input_context: ResMut<InputContext>,
keyboard_input: Res<ButtonInput<KeyCode>>,
) {
match input_context.current_state {
InputState::Normal => {
if keyboard_input.just_pressed(KeyCode::Escape) {
input_context.current_state = InputState::Menu;
}
}
InputState::Menu => {
if keyboard_input.just_pressed(KeyCode::Escape) {
input_context.current_state = InputState::Normal;
}
}
_ => {}
}
}性能优化技巧
1. 输入缓存
#[derive(Resource)]
struct InputCache {
keyboard_cache: HashMap<KeyCode, bool>,
last_update: f32,
cache_duration: f32,
}
fn optimized_input_system(
mut input_cache: ResMut<InputCache>,
keyboard_input: Res<ButtonInput<KeyCode>>,
time: Res<Time>,
) {
let current_time = time.elapsed_secs();
if input_cache.needs_update(current_time) {
input_cache.update_cache(&keyboard_input, current_time);
}
// 使用缓存的数据
if input_cache.is_key_pressed(KeyCode::KeyW) {
// 处理输入
}
}2. 输入批处理
fn batched_input_system(
mut input_events: EventReader<InputEvent>,
mut batched_actions: EventWriter<BatchedAction>,
) {
let mut actions = Vec::new();
for event in input_events.read() {
actions.push(event.action.clone());
}
if !actions.is_empty() {
batched_actions.send(BatchedAction { actions });
}
}3. 事件优先级
fn priority_input_system(
mut high_priority: EventReader<HighPriorityEvent>,
mut normal_priority: EventReader<NormalPriorityEvent>,
) {
// 先处理高优先级事件
for event in high_priority.read() {
// 处理紧急输入
}
// 再处理正常优先级事件
for event in normal_priority.read() {
// 处理常规输入
}
}常见问题解决
1. 输入延迟
// 使用缓存减少重复检查
let cached_state = input_cache.get_key_state(key);
// 使用批处理减少事件处理开销
let batched_events = collect_events();
process_batch(batched_events);2. 输入冲突
// 使用状态机管理输入上下文
match current_input_state {
InputState::Gameplay => {
// 处理游戏输入
}
InputState::UI => {
// 处理UI输入
}
}3. 多设备支持
// 统一输入映射
fn unified_input_system(
keyboard_input: Res<ButtonInput<KeyCode>>,
gamepads: Query<&Gamepad>,
input_mapping: Res<InputMapping>,
) {
// 检查键盘输入
if keyboard_input.pressed(input_mapping.get_key(InputAction::MoveForward)) {
// 处理移动
}
// 检查游戏手柄输入
for gamepad in &gamepads {
if gamepad.pressed(input_mapping.get_gamepad_button(InputAction::MoveForward)) {
// 处理移动
}
}
}4. 输入验证
// 输入过滤器
fn input_filter_system(
keyboard_input: Res<ButtonInput<KeyCode>>,
input_filter: Res<InputFilter>,
mut filtered_events: EventWriter<FilteredInputEvent>,
) {
for key_code in KeyCode::iter() {
if keyboard_input.just_pressed(key_code) {
if !input_filter.is_disabled(key_code) {
filtered_events.send(FilteredInputEvent { key: key_code });
}
}
}
}最佳实践
1. 输入系统设计
- 使用状态机管理不同的输入上下文
- 实现输入映射系统支持自定义绑定
- 使用事件系统处理输入,避免直接修改游戏状态
- 实现输入缓冲和预测提高响应性
2. 性能优化
- 缓存输入状态减少重复检查
- 使用批处理减少事件处理开销
- 实现输入预测和插值
- 优化输入事件的分发和处理
3. 用户体验
- 支持多种输入设备
- 提供输入配置选项
- 实现输入反馈(震动、音效等)
- 处理输入冲突和优先级
4. 错误处理
- 验证输入数据的有效性
- 处理设备连接和断开
- 提供合理的默认输入配置
- 记录输入错误便于调试
扩展资源
官方文档
社区资源
学习路径建议
- 初学者: 从基础教程开始,掌握各种输入设备的基本使用
- 进阶用户: 学习高级教程,了解输入映射、状态管理等
- 高级用户: 探索自定义输入系统、性能优化、输入预测等
结语
Bevy的输入系统提供了强大而灵活的输入处理能力,从基础的键盘鼠标输入到高级的游戏手柄震动和自定义输入类型。通过本教程的学习,您应该能够:
- 处理各种输入设备的输入
- 实现复杂的输入映射和绑定
- 管理输入状态和上下文
- 优化输入系统的性能
- 创建自定义输入类型
- 实现输入预测和插值
建议在实际项目中逐步应用这些知识,并根据具体需求选择合适的特性组合。Bevy的ECS架构和模块化设计使得输入系统易于扩展和维护,是开发高质量交互体验的优秀选择。