OnscreenController
提供了一个 “虚拟” 屏幕游戏控制器,可以包含在使用 SwiftUI 的 iOS 或 iPadOS 应用中。它生成一个 View
,其中包含类似于任天堂娱乐系统 (NES) 控制器上的按钮。
它为控制器上的每个按钮接受一个回调(在该按钮开始或停止被按下时调用),从而使其易于集成到项目中。OnscreenController
是一个 SwiftUI 组件,内部使用了一些 UIKit 来进行手势识别。它最低需要 Xcode 14 以及 iOS 16 或 iPadOS 16。
OnscreenController
用于 Blackbox NES 模拟器,并根据 MIT 许可证提供,可用于其他项目。OnscreenController
和 Blackbox 均由 Grady Haynes 编写,并根据 MIT 许可证免费提供。
分支 feature/addVisionOSSupport
添加了 visionOS 支持,但只经过了最基本的测试。
GCVirtualController
,为什么还要使用它?我构建 OnscreenController
是为了在 Blackbox 中使用,因为我发现 GCVirtualController
令人不满意。当尝试在 SwiftUI 应用中使用它时,我无法在某些配置中以一种可行的方式进行布局:特别是在纵向模式下的 iPhone 中。我还遇到了 GCVirtualController
未能正确响应设备方向更改以及在使用自定义 Scene
时未能正确分层的问题。这是在 Xcode 14/iOS 16 SDK 上发生的;操作系统/SDK/Xcode 的后续版本可能会缓解这些问题。
在 SwiftUI 视图中配置和使用 OnscreenController
组件的一种方式如下
enum Button {
case up, down, left, right, select, start, b, a
}
struct MyView: View {
@State private var pressedButtons: Set<Button> = []
var body: some View {
OnscreenController(
up: { set(.up, $0) },
down: { set(.down, $0) },
left: { set(.left, $0) },
right: { set(.right, $0) },
select: { set(.select, $0) },
start: { set(.start, $0) },
b: { set(.b, $0) },
a: { set(.a, $0) }
)
.onChange(of: pressedButtons) {
// Function (body not shown here) that takes a `Set<Button>` representing the pressed buttons each time that set changes.
onscreenButtonsPressed($0)
}
}
private func set(_ button: Button, _ isOn: Bool) {
if isOn {
pressedButtons.insert(button)
} else {
pressedButtons.remove(button)
}
}
}
请注意,您可能希望在 overlay
或 ZStack
中使用 OnscreenController
,该 overlay
或 ZStack
覆盖的区域比组件用于显示其按钮的区域更大。这样,OnscreenController
可以通过跟踪从其视觉上明显的区域之外开始但继续与其按钮相交的触摸来提供更好的用户体验。这为玩家提供了明显更好的体验。
OnscreenController
是一个学习更多关于 SwiftUI 功能的项目,包括锚点偏好、与 UIKit 交互以及绘图(Path
和 Shape
)。我非常乐意收到关于它的反馈,因此欢迎提出建议、贡献和 PR。
以下是一些将来可能会添加的功能
OnscreenController
的行为是不可能实现的,但随着 SwiftUI 的不断发展,这种情况很可能会改变。Path
在某些情况下会显示一些细微的视觉问题。