Highlight
iOS 15 推出虚拟屏幕控制器
GCVirtualController,将触摸输入转换为游戏控制器输入,与现有GCController代码兼容;同时支持 PlayStation DualSense 控制器的自适应扳机效果。
核心内容
移动游戏控制的痛点
从主机平台移植到 iOS/iPadOS 的游戏通常设计为使用两个模拟摇杆、方向键和数字按钮。开发者反馈说,为这些游戏构建一致、手感好、外观优美的触摸输入很困难。触摸输入处理代码与键盘、鼠标和游戏控制器输入代码路径不同,增加了维护成本。(06:13)
虚拟控制器的解决方案
Apple 推出了新的虚拟屏幕控制器,专门解决这些问题。这些屏幕控件:
- 外观精美:针对不同手型抓握位置精心调校,响应迅速
- 代码统一:虚拟控制器在代码中表现为
GCController对象,与物理控制器使用相同的输入处理逻辑 - 可定制:支持自定义符号、颜色和样式,同时保持 iOS/iPadOS 设计语言
- 自适应布局:根据配置自动调整布局
虚拟控制器的核心原则是左右区域独立配置,布局由配置决定。每侧可以包含 0-4 个按钮,以及一个摇杆、方向键或触摸板。(07:22)
Game Controller 框架基础
Game Controller 框架的目标是让开发者轻松支持 Apple 平台上各种高效、低延迟的输入方式。通过通用 API 抽象输入硬件,开发者只需编写一次输入代码,无需担心输入如何重新映射或不同控制器之间的差异。(00:40)
在 iOS、iPadOS 和 tvOS 上,玩家可以创建系统级和每应用的游戏控制器输入重新映射,让游戏更可定制、更易访问。(01:04)
详细内容
控制器连接和断开处理
处理控制器连接和断开的基本模式:(01:27)
import GameController
// 监听控制器连接通知
NotificationCenter.default.addObserver(
self,
selector: #selector(controllerDidConnect),
name: .GCControllerDidConnect,
object: nil
)
// 监听控制器断开通知
NotificationCenter.default.addObserver(
self,
selector: #selector(controllerDidDisconnect),
name: .GCControllerDidDisconnect,
object: nil
)
@objc func controllerDidConnect(_ notification: Notification) {
let controller = notification.object as? GCController
// 设置输入处理或轮询控制器状态
}
@objc func controllerDidDisconnect(_ notification: Notification) {
let controller = notification.object as? GCController
// 清理控制器相关资源
}
关键点:
- 控制器连接或断开时,系统创建或移除
GCController对象 - 发送
GCControllerDidConnect和GCControllerDidDisconnect通知 - 相同模式适用于
GCKeyboard和GCMouse对象 - 可以选择设置值变化处理器或轮询输入状态
创建虚拟控制器
创建虚拟控制器的步骤:(08:42)
import GameController
// 创建配置对象
let configuration = GCVirtualControllerConfiguration()
// 设置左侧元素
configuration.leftThumbstick = true
configuration.leftButtons = [GCInputButtonOptions.a]
// 设置右侧元素
configuration.rightThumbstick = true
configuration.rightButtons = [
GCInputButtonOptions.a,
GCInputButtonOptions.b
]
// 创建虚拟控制器
let virtualController = GCVirtualController(configuration: configuration)
// 连接控制器,触发 GCControllerDidConnect 通知
virtualController.connect()
关键点:
GCVirtualControllerConfiguration定义虚拟控制器的布局- 每侧可以配置摇杆、方向键或触摸板(三选一)
- 每侧可以配置 0-4 个按钮
- 调用
connect()后,虚拟控制器表现为标准的GCController对象 - 配置在创建时固定,可以显示和隐藏元素,但不能添加或删除
自定义按钮外观
自定义虚拟控制器的按钮外观:(09:17)
// 获取按钮配置
if let buttonA = virtualController.controller?.extendedGamepad?.buttonA {
// 创建自定义形状(如旋转攻击图标)
let attackPath = UIBezierPath()
attackPath.move(to: CGPoint(x: 0.5, y: 0))
attackPath.addLine(to: CGPoint(x: 1, y: 1))
attackPath.addLine(to: CGPoint(x: 0, y: 1))
attackPath.close()
buttonA.path = attackPath
}
if let buttonB = virtualController.controller?.extendedGamepad?.buttonB {
// 创建跳跃图标
let jumpPath = UIBezierPath()
jumpPath.move(to: CGPoint(x: 0.2, y: 0.8))
jumpPath.addLine(to: CGPoint(x: 0.5, y: 0.2))
jumpPath.addLine(to: CGPoint(x: 0.8, y: 0.8))
jumpPath.close()
buttonB.path = jumpPath
}
// 隐藏按钮
if let buttonX = virtualController.controller?.extendedGamepad?.buttonX {
buttonA.isHidden = true
}
关键点:
- 通过
path属性设置按钮的自定义形状 - 使用
UIBezierPath创建矢量图形 isHidden属性可以显示或隐藏按钮- 自定义形状应该简洁清晰,适合快速识别
DualSense 自适应扳机
PlayStation DualSense 控制器的自适应扳机技术可以根据玩家行为动态应用不同的阻力,增强沉浸感。可以模拟拉动弓弦、弹弓在张力下的感觉等。(10:45)
支持自适应扳机的步骤:(12:06)
import GameController
// 获取当前控制器
guard let controller = GCController.current else { return }
// 验证是否为 DualSense 控制器
guard let dualSense = controller.physicalInputProfile as? GCDualSenseGamepad else {
return
}
// 获取右自适应扳机
let rightTrigger = dualSense.rightAdaptiveTrigger
// 玩家瞄准时应用阻力
func updateSlingshotResistance(isAiming: Bool, pullAmount: Float) {
if isAiming {
if pullAmount < 1.0 {
// 未完全拉回,应用反馈效果
// FeedbackEffect 提供恒定阻力
let feedbackEffect = GCAdaptiveTriggerFeedbackEffect(
strength: pullAmount
)
rightTrigger.applyEffect(feedbackEffect)
} else {
// 完全拉回,应用振动效果
// VibrationEffect 让扳机振动,模拟保持张力
let vibrationEffect = GCAdaptiveTriggerVibrationEffect(
strength: pullAmount,
frequency: 0.5
)
rightTrigger.applyEffect(vibrationEffect)
}
} else {
// 不瞄准时关闭效果
rightTrigger.applyEffect(nil)
}
}
// 玩家发射弹弓后关闭效果
func fireSlingshot() {
rightTrigger.applyEffect(nil)
}
关键点:
- 检查
physicalInputProfile是否为GCDualSenseGamepad类型 rightAdaptiveTrigger和leftAdaptiveTrigger访问左右自适应扳机GCAdaptiveTriggerFeedbackEffect提供恒定阻力GCAdaptiveTriggerVibrationEffect让扳机振动- 使用完记得调用
applyEffect(nil)关闭效果
App Store 标记和自定义
在 Xcode 中添加 Game Controllers capability 来标记应用支持游戏控制器:(03:10)
- 打开项目设置
- 选择 Signing & Capabilities
- 添加 Game Controllers capability
这会在应用的 Info.plist 中添加游戏控制器能力,App Store 会为应用添加”控制器支持”徽章。应用也会被包含在游戏控制器偏好设置的每应用自定义部分。(03:22)
UI 中的正确字形
在 UI 中显示正确的按钮字形很重要。不同控制器的按钮布局不同:
- MFi 或 Xbox 控制器:显示 Y 按钮
- PlayStation 控制器:显示三角形按钮
即使玩家在系统重新映射 UI 中重新映射了按钮,也应显示正确的字形。(03:36)
// 获取与动作关联的按钮
guard let buttonA = controller.extendedGamepad?.buttonA else { return }
// 获取按钮的 SF Symbols 名称
let symbolName = buttonA.sfSymbolsName
// 创建系统图像
if let symbolName = symbolName, let image = UIImage(systemName: symbolName) {
// 在 UI 中显示图像
buttonImageView.image = image
}
关键点:
sfSymbolsName属性返回按钮的系统符号名称- 使用
UIImage(systemName:)创建图像 - 这样会自动反映系统重新映射
- 另一种方法是显示位置关系(如”上方按钮”)而非具体按钮名称
Share 按钮和屏幕捕获
支持 Share 按钮的控制器可以让玩家捕获屏幕和录制视频:(03:46)
- 双击 Share 按钮:捕获屏幕截图到相册
- 长按 Share 按钮:开始和停止 ReplayKit 录制
iOS 15 新增了 15 秒高光片段功能。玩家可以开启自动后台缓冲,随时长按保存最后 15 秒的游戏画面。(13:31)
可以在游戏控制器偏好面板中切换全局或每应用设置。如果想在游戏中的重要时刻触发自己的高光片段,有专门的 API 可用。
核心启发
-
做什么:为移植游戏创建统一的控制方案
- 为什么值得做:虚拟控制器让触摸输入表现为
GCController,与物理控制器使用相同的代码路径,减少维护成本 - 怎么开始:创建
GCVirtualControllerConfiguration,设置左右两侧的摇杆和按钮,调用connect()连接控制器
- 为什么值得做:虚拟控制器让触摸输入表现为
-
做什么:实现 DualSense 自适应扳机的沉浸感
- 为什么值得做:自适应扳机可以根据游戏状态动态调整阻力,增强武器拉弓、弹弓拉拽等动作的真实感
- 怎么开始:检查
physicalInputProfile是否为GCDualSenseGamepad,获取自适应扳机,应用GCAdaptiveTriggerFeedbackEffect或GCAdaptiveTriggerVibrationEffect
-
做什么:支持控制器输入重新映射
- 为什么值得做:玩家可以在系统设置中重新映射按钮,提升游戏的可访问性和可定制性
- 怎么开始:在 Xcode 中添加 Game Controllers capability,使用
sfSymbolsName属性显示正确的按钮字形
-
做什么:集成 Share 按钮的屏幕捕获功能
- 为什么值得做:玩家可以轻松捕获游戏精彩瞬间,分享给朋友,提升游戏传播度
- 怎么开始:支持 ReplayKit API,在游戏重要时刻触发 15 秒高光片段录制
-
做什么:为触摸设备设计可定制的控制布局
- 为什么值得做:不同游戏需要不同的控制方案,虚拟控制器的灵活配置可以满足各种需求
- 怎么开始:根据游戏类型选择合适的摇杆、方向键和按钮组合,自定义按钮形状提升可识别性
关联 Session
- Discover rolling clips in ReplayKit — 学习 ReplayKit 的新功能,包括 15 秒高光片段
- What’s new in Game Center — Game Center 的更新内容
- Explore game audio with AVAudioEngine — 学习使用 AVAudioEngine 构建游戏音频系统
- Bring your game to iPad, Mac, and more — 将游戏移植到更多平台
评论
GitHub Issues · utterances