Graphics & Games 进阶 25m
将键鼠游戏体验带到 iPad
Bring keyboard and mouse gaming to iPad
2020年6月24日
一句话判断
iPad 上的键鼠操作体验和传统 PC 不同——如果你在移植 PC 游戏到 iPad,这场 Session 会告诉你哪些坑必须避开。
这场 Session 讲了什么
这场 Session 详细讲解了如何在 iPadOS 上为游戏适配键盘和鼠标输入。iPadOS 13.4 引入了鼠标触控板支持,iPadOS 14 进一步增强了键鼠输入能力。对于游戏开发者来说,这意味着 iPad 可以成为一台便携的游戏设备,前提是你正确适配了输入方式。
演讲者首先介绍了键盘输入的处理。UIKey 和 UIKeyCommand 是处理键盘事件的标准 API,但游戏场景需要更低延迟的输入处理。Session 讲解了如何使用 UIEvent 的 keyboard 事件来捕获按键的按下和释放状态,这对于需要同时检测多个按键的游戏(如 WASD 移动)至关重要。
鼠标和触控板部分是 Session 的重点。iPad 的鼠标/触控板有”指针”概念——一个跟随手指移动的光标。但对于 FPS 或 RTS 游戏来说,你需要的是”直接模式”——鼠标移动直接映射到视角旋转,光标不显示。Session 讲解了如何使用 UIGestureRecognizer 的子类或直接处理 UIEvent 来实现这种直接映射。
最后讨论了如何同时支持触摸和键鼠两套输入方案,以及如何在 UI 中让用户选择偏好的输入方式。
值得深挖的点
- Pointer Lock 模式的实现:FPS 游戏需要锁定鼠标指针,让鼠标移动量直接映射到视角旋转。在 iPadOS 上,这需要通过
UIPointerLockState或自定义的手势识别器来实现。关键是处理好指针隐藏和恢复的时机。 - 触摸与键鼠的并行设计:同一个游戏功能(如”移动”)在触摸上是虚拟摇杆,在键鼠上是 WASD。你需要维护两套并行的输入映射,并在用户切换输入方式时无缝过渡。这不仅是技术问题,更是 UX 设计问题。
代码片段
// 处理键盘按键的按下和释放
class KeyboardInputHandler {
private var pressedKeys = Set<UIKeyboardHIDUsage>()
func handleKeyEvent(_ event: UIEvent) {
guard let key = event.allTouches?.first else { return }
switch event.type {
case .keyDown:
pressedKeys.insert(key.key.keyCode)
handleMovement()
case .keyUp:
pressedKeys.remove(key.key.keyCode)
default:
break
}
}
// WASD 移动检测
func handleMovement() {
var direction = SIMD2<Float>(0, 0)
if pressedKeys.contains(.keyboardW) { direction.y += 1 }
if pressedKeys.contains(.keyboardS) { direction.y -= 1 }
if pressedKeys.contains(.keyboardA) { direction.x -= 1 }
if pressedKeys.contains(.keyboardD) { direction.x += 1 }
if length(direction) > 0 {
player.move(in: normalize(direction))
}
}
}
// 实现鼠标直接映射(Pointer Lock 模式)
class DirectMouseHandler: UIGestureRecognizer {
private var lastPosition: CGPoint?
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
guard let touch = touches.first else { return }
let currentPos = touch.location(in: view)
if let last = lastPosition {
let dx = Float(currentPos.x - last.x)
let dy = Float(currentPos.y - last.y)
// 直接映射到视角旋转
player.rotateCamera(yaw: dx * sensitivity,
pitch: dy * sensitivity)
}
lastPosition = currentPos
}
}
// 检测当前输入方式并切换 UI
func updateInputMode() {
let hasHardwareKeyboard =
GCKeyboard.coordinator?.keyboard != nil
let hasMouse =
GCMouse.coordinator?.mouse != nil
if hasHardwareKeyboard && hasMouse {
showKeyboardMouseUI()
hideVirtualJoystick()
} else {
showTouchUI()
showVirtualJoystick()
}
}
// 监听输入设备变化
NotificationCenter.default.addObserver(
forName: .GCKeyboardDidConnect, object: nil, queue: .main
) { _ in self.updateInputMode() }
NotificationCenter.default.addObserver(
forName: .GCMouseDidConnect, object: nil, queue: .main
) { _ in self.updateInputMode() }
最佳实践
- 键鼠和触摸两套输入方案都要完整实现,不要只做其中一套
- FPS 类游戏的鼠标灵敏度提供可调节选项,不同用户偏好差异很大
- 键鼠操作时隐藏虚拟摇杆和触摸按钮,释放屏幕空间
- 支持自定义键位映射,PC 游戏玩家的键位偏好各不相同
- 使用
UIKeyCommand为菜单操作注册快捷键
还有什么值得关注
- iPadOS 的键鼠支持后来在 iPadOS 16 中进一步增强
- 搭配 “Advancements in Game Controllers” 一起看,可以覆盖 iPad 上所有的输入方案
- 如果你在做跨平台游戏移植,键鼠适配只是第一步——还需要考虑性能优化和触屏降级
WWDC 2020