屏幕控制器 (OnscreenController)

适用于 iOS 的 SwiftUI 虚拟游戏控制器

OnscreenController 提供了一个 “虚拟” 屏幕游戏控制器,可以包含在使用 SwiftUI 的 iOS 或 iPadOS 应用中。它生成一个 View,其中包含类似于任天堂娱乐系统 (NES) 控制器上的按钮。

它为控制器上的每个按钮接受一个回调(在该按钮开始或停止被按下时调用),从而使其易于集成到项目中。OnscreenController 是一个 SwiftUI 组件,内部使用了一些 UIKit 来进行手势识别。它最低需要 Xcode 14 以及 iOS 16 或 iPadOS 16。

OnscreenController 用于 Blackbox NES 模拟器,并根据 MIT 许可证提供,可用于其他项目。OnscreenControllerBlackbox 均由 Grady Haynes 编写,并根据 MIT 许可证免费提供。

分支 feature/addVisionOSSupport 添加了 visionOS 支持,但只经过了最基本的测试。

OnscreenController in landscape orientation, dark mode, iPhone 14 Pro Simulator

既然存在 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)
        }
    }
}

请注意,您可能希望在 overlayZStack 中使用 OnscreenController,该 overlayZStack 覆盖的区域比组件用于显示其按钮的区域更大。这样,OnscreenController 可以通过跟踪从其视觉上明显的区域之外开始但继续与其按钮相交的触摸来提供更好的用户体验。这为玩家提供了明显更好的体验。

需要改进的地方

OnscreenController 是一个学习更多关于 SwiftUI 功能的项目,包括锚点偏好、与 UIKit 交互以及绘图(PathShape)。我非常乐意收到关于它的反馈,因此欢迎提出建议、贡献和 PR。

以下是一些将来可能会添加的功能