mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-02 13:00:53 +00:00
implement on screen virtual controller for iOS
This commit is contained in:
parent
d7cb051fb7
commit
3b2a6ffb6a
@ -2,6 +2,10 @@ import Foundation
|
||||
import SDL3
|
||||
import QuartzCore.CAMetalLayer
|
||||
|
||||
#if canImport(GameController)
|
||||
import GameController
|
||||
#endif
|
||||
|
||||
public class Application {
|
||||
private let cfg: ApplicationConfiguration
|
||||
private var del: GameDelegate!
|
||||
@ -12,12 +16,24 @@ public class Application {
|
||||
private var lastCounter: UInt64 = 0
|
||||
private var time: Duration = .zero
|
||||
|
||||
#if os(iOS)
|
||||
private var onScreenVirtualController: GCVirtualController? = nil
|
||||
private var onScreenVirtualControllerShown: Bool = false
|
||||
#endif
|
||||
|
||||
public init(delegate: GameDelegate, configuration: ApplicationConfiguration) {
|
||||
self.cfg = configuration
|
||||
self.del = delegate
|
||||
}
|
||||
|
||||
private func initialize() -> ApplicationExecutionState {
|
||||
#if os(iOS)
|
||||
if cfg.flags.contains(.onScreenVirtualController) {
|
||||
onScreenVirtualController = initializeOnScreenVirtualController()
|
||||
self.showVirtualGameController(true)
|
||||
}
|
||||
#endif
|
||||
|
||||
guard SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) else {
|
||||
printErr("SDL_Init() error: \(String(cString: SDL_GetError()))")
|
||||
return .exitFailure
|
||||
@ -31,6 +47,12 @@ public class Application {
|
||||
if cfg.flags.contains(.highDPI) {
|
||||
windowFlags |= SDL_WindowFlags(SDL_WINDOW_HIGH_PIXEL_DENSITY)
|
||||
}
|
||||
if cfg.flags.contains(.borderless) {
|
||||
windowFlags |= SDL_WindowFlags(SDL_WINDOW_BORDERLESS)
|
||||
}
|
||||
if cfg.flags.contains(.fullscreen) {
|
||||
windowFlags |= SDL_WindowFlags(SDL_WINDOW_FULLSCREEN)
|
||||
}
|
||||
window = SDL_CreateWindow(cfg.title, cfg.frame.w, cfg.frame.h, windowFlags)
|
||||
guard window != nil else {
|
||||
printErr("SDL_CreateWindow() error: \(String(cString: SDL_GetError()))")
|
||||
@ -70,6 +92,22 @@ public class Application {
|
||||
return .running
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private func initializeOnScreenVirtualController() -> GCVirtualController {
|
||||
let configuration = GCVirtualController.Configuration()
|
||||
configuration.elements = [
|
||||
GCInputLeftThumbstick,
|
||||
GCInputRightThumbstick,
|
||||
GCInputLeftTrigger,
|
||||
GCInputRightTrigger,
|
||||
GCInputButtonA,
|
||||
GCInputButtonB,
|
||||
]
|
||||
let controller = GCVirtualController(configuration: configuration)
|
||||
return controller
|
||||
}
|
||||
#endif
|
||||
|
||||
private func deinitialize() {
|
||||
self.del = nil
|
||||
self.renderer = nil
|
||||
@ -141,6 +179,33 @@ public class Application {
|
||||
}
|
||||
}
|
||||
|
||||
private func showVirtualGameController(_ shown: Bool) {
|
||||
#if os(iOS)
|
||||
guard let onScreenVirtualController = self.onScreenVirtualController else {
|
||||
return
|
||||
}
|
||||
|
||||
if shown {
|
||||
if !onScreenVirtualControllerShown {
|
||||
let semaphore = DispatchSemaphore(value: 1)
|
||||
DispatchQueue.global().async {
|
||||
Task.detached {
|
||||
try? await onScreenVirtualController.connect()
|
||||
semaphore.signal()
|
||||
}
|
||||
}
|
||||
semaphore.wait()
|
||||
onScreenVirtualControllerShown = true
|
||||
}
|
||||
} else {
|
||||
if onScreenVirtualControllerShown {
|
||||
onScreenVirtualController.disconnect()
|
||||
onScreenVirtualControllerShown = false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func update() -> ApplicationExecutionState {
|
||||
let deltaTime = getDeltaTime()
|
||||
time += deltaTime
|
||||
@ -201,6 +266,9 @@ public struct ApplicationConfiguration {
|
||||
|
||||
static let resizable = Flags(rawValue: 1 << 0)
|
||||
static let highDPI = Flags(rawValue: 1 << 1)
|
||||
static let borderless = Flags(rawValue: 1 << 2)
|
||||
static let fullscreen = Flags(rawValue: 1 << 3)
|
||||
static let onScreenVirtualController = Flags(rawValue: 1 << 4)
|
||||
}
|
||||
|
||||
public enum VSyncMode: Equatable {
|
||||
|
@ -36,5 +36,7 @@
|
||||
<false/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -4,14 +4,33 @@ import Foundation
|
||||
@objc public static func run() -> Int32 {
|
||||
Thread.current.qualityOfService = .userInteractive
|
||||
|
||||
var flags: ApplicationConfiguration.Flags = [ .resizable, .highDPI, .onScreenVirtualController ]
|
||||
if enableFullscreenWindow() {
|
||||
flags = flags.union(.fullscreen)
|
||||
}
|
||||
|
||||
let app = Application(
|
||||
delegate: Game(),
|
||||
configuration: ApplicationConfiguration(
|
||||
frame: Size(1280, 720),
|
||||
title: "Voxelotl Demo",
|
||||
flags: [ .resizable, .highDPI ],
|
||||
flags: flags,
|
||||
vsyncMode: .on(interval: 1)))
|
||||
|
||||
return app.run()
|
||||
}
|
||||
|
||||
static func enableFullscreenWindow() -> Bool {
|
||||
return Program.isFrontAndCenterGamingDevice()
|
||||
}
|
||||
|
||||
static func isFrontAndCenterGamingDevice() -> Bool {
|
||||
#if os(iOS)
|
||||
return !(ProcessInfo.processInfo.isiOSAppOnMac || ProcessInfo.processInfo.isMacCatalystApp)
|
||||
#elseif os(tvOS)
|
||||
return true
|
||||
#else
|
||||
return false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user