输入处理

input基础

本教程基于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. 错误处理

  • 验证输入数据的有效性
  • 处理设备连接和断开
  • 提供合理的默认输入配置
  • 记录输入错误便于调试

扩展资源

官方文档

社区资源

学习路径建议

  1. 初学者: 从基础教程开始,掌握各种输入设备的基本使用
  2. 进阶用户: 学习高级教程,了解输入映射、状态管理等
  3. 高级用户: 探索自定义输入系统、性能优化、输入预测等

结语

Bevy的输入系统提供了强大而灵活的输入处理能力,从基础的键盘鼠标输入到高级的游戏手柄震动和自定义输入类型。通过本教程的学习,您应该能够:

  • 处理各种输入设备的输入
  • 实现复杂的输入映射和绑定
  • 管理输入状态和上下文
  • 优化输入系统的性能
  • 创建自定义输入类型
  • 实现输入预测和插值

建议在实际项目中逐步应用这些知识,并根据具体需求选择合适的特性组合。Bevy的ECS架构和模块化设计使得输入系统易于扩展和维护,是开发高质量交互体验的优秀选择。