DemoBots/GameInput.swift
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
An abstraction representing game input for the user currently playing the game. Manages the player's control input sources, and handles game controller connections / disconnections. |
*/ |
import GameController |
protocol GameInputDelegate: class { |
// Called whenever a control input source is updated. |
func gameInputDidUpdateControlInputSources(gameInput: GameInput) |
} |
final class GameInput { |
// MARK: Properties |
#if os(tvOS) |
/** |
The control input source that is native to tvOS (gameController). |
This property is optional to represent that a game controller may not be |
immediately available upon launch. |
*/ |
var nativeControlInputSource: ControlInputSourceType? |
#else |
/// The control input source that is native to the platform (keyboard or touch). |
let nativeControlInputSource: ControlInputSourceType |
#endif |
/// An optional secondary input source for a connected game controller. |
private(set) var secondaryControlInputSource: GameControllerInputSource? |
var isGameControllerConnected: Bool { |
var isGameControllerConnected = false |
controlsQueue.sync { |
isGameControllerConnected = (self.secondaryControlInputSource != nil) || (self.nativeControlInputSource is GameControllerInputSource) |
} |
return isGameControllerConnected |
} |
var controlInputSources: [ControlInputSourceType] { |
// Return a non-optional array of `ControlInputSourceType`s. |
let sources: [ControlInputSourceType?] = [nativeControlInputSource, secondaryControlInputSource] |
return sources.flatMap { return $0 as ControlInputSourceType? } |
} |
weak var delegate: GameInputDelegate? { |
didSet { |
// Ensure the delegate is aware of the player's current controls. |
delegate?.gameInputDidUpdateControlInputSources(gameInput: self) |
} |
} |
/// An internal queue to protect accessing the player's control input sources. |
private let controlsQueue = DispatchQueue(label: "com.example.apple-samplecode.player.controlsqueue") |
// MARK: Initialization |
init(nativeControlInputSource: ControlInputSourceType) { |
self.nativeControlInputSource = nativeControlInputSource |
registerForGameControllerNotifications() |
} |
#if os(tvOS) |
init() { |
// Search for paired game controllers. |
for pairedController in GCController.controllers() { |
update(withGameController: pairedController) |
} |
registerForGameControllerNotifications() |
} |
#endif |
/// Register for `GCGameController` pairing notifications. |
func registerForGameControllerNotifications() { |
NotificationCenter.default.addObserver(self, selector: #selector(GameInput.handleControllerDidConnectNotification(notification:)), name: NSNotification.Name.GCControllerDidConnect, object: nil) |
NotificationCenter.default.addObserver(self, selector: #selector(GameInput.handleControllerDidDisconnectNotification(notification:)), name: NSNotification.Name.GCControllerDidDisconnect, object: nil) |
} |
deinit { |
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.GCControllerDidConnect, object: nil) |
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.GCControllerDidDisconnect, object: nil) |
} |
func update(withGameController gameController: GCController) { |
controlsQueue.sync { |
#if os(tvOS) |
// Assign a controller to the `nativeControlInputSource` if one does not already exist. |
if self.nativeControlInputSource == nil { |
self.nativeControlInputSource = GameControllerInputSource(gameController: gameController) |
return |
} |
#endif |
/* |
If not already assigned, add a game controller as the player's |
secondary control input source. |
*/ |
if self.secondaryControlInputSource == nil { |
let gameControllerInputSource = GameControllerInputSource(gameController: gameController) |
self.secondaryControlInputSource = gameControllerInputSource |
gameController.playerIndex = .index1 |
} |
} |
} |
// MARK: GCGameController Notification Handling |
@objc func handleControllerDidConnectNotification(notification: NSNotification) { |
let connectedGameController = notification.object as! GCController |
update(withGameController: connectedGameController) |
delegate?.gameInputDidUpdateControlInputSources(gameInput: self) |
} |
@objc func handleControllerDidDisconnectNotification(notification: NSNotification) { |
let disconnectedGameController = notification.object as! GCController |
// Check if the player was being controlled by the disconnected controller. |
if secondaryControlInputSource?.gameController == disconnectedGameController { |
controlsQueue.sync { |
self.secondaryControlInputSource = nil |
} |
// Check for any other connected controllers. |
if let gameController = GCController.controllers().first { |
update(withGameController: gameController) |
} |
delegate?.gameInputDidUpdateControlInputSources(gameInput: self) |
} |
} |
} |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-09-13