Skip to content

触摸:一窝带编号的“光标”

触屏看客最省事,设备就是手指头。但翻译到程序里,触摸反而是几台设备里最“多线程”的:光标全屏只有一个,手指能同时落下好几根,每根有自己的轨迹、自己的生灭。Bevy 的安排照旧是“流水 + 快照”:流水是 TouchInput 消息(带相位 StartedMovedEndedCanceled),快照是资源 Touches——把每根在场的手指按 id 记账。

rust
/// 落指即插旗:触点的窗口坐标走光标同一条反算路
fn taps(
    touches: Res<Touches>,
    camera: Single<(&Camera, &GlobalTransform)>,
    mut flag: Single<(&mut Transform, &mut Visibility), With<Flag>>,
    mut target: ResMut<DashTarget>,
    mut last_count: Local<usize>,
) {
    let (camera, camera_transform) = *camera;
    for touch in touches.iter_just_pressed() {
        let Ok(point) = camera.viewport_to_world_2d(camera_transform, touch.position()) else {
            continue;
        };
        let (flag_transform, flag_visibility) = &mut *flag;
        flag_transform.translation = point.extend(0.5);
        **flag_visibility = Visibility::Visible;
        target.0 = Some(point.x.clamp(-STAGE_HALF, STAGE_HALF));
        println!(
            "场记:{} 号手指点了 ({:.0}, {:.0}),旗插上了。",
            touch.id(),
            point.x,
            point.y
        );
    }

    // 同时按着几根手指?Touches 替每个触点记着账
    let count = touches.iter().count();
    if count != *last_count {
        *last_count = count;
        if count > 1 {
            println!("场记:台口同时按着 {count} 根手指。");
        }
    }
}

Listing 17-7:落指即插旗——Touches 按手指记账(examples/listing-17-07.rs)

读法处处是熟面孔:

  • 三问换成了三个迭代器iter_just_pressed()(本帧新落下的手指)、iter_just_released()(本帧抬起的)、iter()(所有按着的),跟 ButtonInput 的三问一一对应,只是每一问吐出的是零到多根手指;
  • 每根手指是一个 Touch,身上有 id()(落下时分配,抬起前不变——跨帧追踪就靠它)、position()(当前位置)、delta()(本帧挪了多少)、distance()(离落点的累计位移)。双指缩放这类手势,本质就是对两个 id 的 position 做几何;
  • 坐标与光标同一个体系position() 给的是窗口坐标,于是上一节那条“反算世界”的路原封不动能走——Listing 17-7 的插旗代码与鼠标版逐行同构,连 dash 系统都是原样照搬;
  • 多一种生灭Canceled。手指没抬、追踪却被系统掐了——窗口失焦、iOS 检测到设备贴上了脸。做拖拽手势时记得把它当“松手”处理,否则会留下一根永远按着的幽灵手指。
console
cargo run -p ch17-input --example listing-17-07
text
老雷:触屏看客请上手。点哪儿走哪儿,几根手指我们都数着。

跟手柄那节一样交个底:本书的写作机器没有触屏,跑起来 Touches 恒为空,上面就是全部输出——鼠标点击不会伪装成触摸。这一节的写法以 bevy_input 源码和官方示例 examples/input/touch_input.rs 为据;带触屏的笔记本或安卓设备(第 38 章部署移动端时会回来)上,每根手指落下都会插旗并报出它的 id,两根手指同按会看到“台口同时按着 2 根手指”。

还有一族更高层的手势消息PinchGesture(双指缩放)、RotationGesture(双指旋转)、DoubleTapGesturePanGesture,由系统的手势识别器直接折算好递来。源码标注得很清楚:目前只在 macOS 与 iOS 可用(iOS 还须显式开启)——跨平台游戏别拿它当唯一通道,老老实实用 Touches 自己算。

至此四路看客的方言都会说了:键盘的三问、鼠标的反算、手柄的模拟量、触屏的多指记账。只剩一个问题——这四套话,怎么不打架地塞进同一台戏?