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 SDL3
|
||||||
import QuartzCore.CAMetalLayer
|
import QuartzCore.CAMetalLayer
|
||||||
|
|
||||||
|
#if canImport(GameController)
|
||||||
|
import GameController
|
||||||
|
#endif
|
||||||
|
|
||||||
public class Application {
|
public class Application {
|
||||||
private let cfg: ApplicationConfiguration
|
private let cfg: ApplicationConfiguration
|
||||||
private var del: GameDelegate!
|
private var del: GameDelegate!
|
||||||
@ -12,12 +16,24 @@ public class Application {
|
|||||||
private var lastCounter: UInt64 = 0
|
private var lastCounter: UInt64 = 0
|
||||||
private var time: Duration = .zero
|
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) {
|
public init(delegate: GameDelegate, configuration: ApplicationConfiguration) {
|
||||||
self.cfg = configuration
|
self.cfg = configuration
|
||||||
self.del = delegate
|
self.del = delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initialize() -> ApplicationExecutionState {
|
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 {
|
guard SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) else {
|
||||||
printErr("SDL_Init() error: \(String(cString: SDL_GetError()))")
|
printErr("SDL_Init() error: \(String(cString: SDL_GetError()))")
|
||||||
return .exitFailure
|
return .exitFailure
|
||||||
@ -31,6 +47,12 @@ public class Application {
|
|||||||
if cfg.flags.contains(.highDPI) {
|
if cfg.flags.contains(.highDPI) {
|
||||||
windowFlags |= SDL_WindowFlags(SDL_WINDOW_HIGH_PIXEL_DENSITY)
|
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)
|
window = SDL_CreateWindow(cfg.title, cfg.frame.w, cfg.frame.h, windowFlags)
|
||||||
guard window != nil else {
|
guard window != nil else {
|
||||||
printErr("SDL_CreateWindow() error: \(String(cString: SDL_GetError()))")
|
printErr("SDL_CreateWindow() error: \(String(cString: SDL_GetError()))")
|
||||||
@ -70,6 +92,22 @@ public class Application {
|
|||||||
return .running
|
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() {
|
private func deinitialize() {
|
||||||
self.del = nil
|
self.del = nil
|
||||||
self.renderer = 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 {
|
private func update() -> ApplicationExecutionState {
|
||||||
let deltaTime = getDeltaTime()
|
let deltaTime = getDeltaTime()
|
||||||
time += deltaTime
|
time += deltaTime
|
||||||
@ -201,6 +266,9 @@ public struct ApplicationConfiguration {
|
|||||||
|
|
||||||
static let resizable = Flags(rawValue: 1 << 0)
|
static let resizable = Flags(rawValue: 1 << 0)
|
||||||
static let highDPI = Flags(rawValue: 1 << 1)
|
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 {
|
public enum VSyncMode: Equatable {
|
||||||
|
@ -36,5 +36,7 @@
|
|||||||
<false/>
|
<false/>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>UIRequiresFullScreen</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -4,14 +4,33 @@ import Foundation
|
|||||||
@objc public static func run() -> Int32 {
|
@objc public static func run() -> Int32 {
|
||||||
Thread.current.qualityOfService = .userInteractive
|
Thread.current.qualityOfService = .userInteractive
|
||||||
|
|
||||||
|
var flags: ApplicationConfiguration.Flags = [ .resizable, .highDPI, .onScreenVirtualController ]
|
||||||
|
if enableFullscreenWindow() {
|
||||||
|
flags = flags.union(.fullscreen)
|
||||||
|
}
|
||||||
|
|
||||||
let app = Application(
|
let app = Application(
|
||||||
delegate: Game(),
|
delegate: Game(),
|
||||||
configuration: ApplicationConfiguration(
|
configuration: ApplicationConfiguration(
|
||||||
frame: Size(1280, 720),
|
frame: Size(1280, 720),
|
||||||
title: "Voxelotl Demo",
|
title: "Voxelotl Demo",
|
||||||
flags: [ .resizable, .highDPI ],
|
flags: flags,
|
||||||
vsyncMode: .on(interval: 1)))
|
vsyncMode: .on(interval: 1)))
|
||||||
|
|
||||||
return app.run()
|
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