iOS13から、iPhoneでPS4やXboxのコントローラーが利用可能になりました。
コントローラーに対応したアプリで利用することができます。
どんなふうに使うのか試しに触ってみました。
コントローラーと接続する
コントローラーの接続は以下の2つのNotificationでハンドリングすることができます。
GCControllerDidConnect - NSNotification.Name | Apple Developer Documentation
GCControllerDidDisconnect - NSNotification.Name | Apple Developer Documentation
これらを使うには GameController
をimportする必要があります。
import GameController
NotificationCenterを登録します。
NotificationCenter.default.addObserver(self,
selector: #selector(handleControllerDidConnect),
name: .GCControllerDidConnect,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(handleControllerDidConnect),
name: .GCControllerDidDisconnect,
object: nil)
これでコントローラーとの接続が確立したときと、切断されたときにそれぞれ通知を受け取ることができます。
コントローラーの入力をハンドリングする
コントローラの入力のハンドリングは GCExtendedGamepad - Game Controller | Apple Developer Documentation を使います。
これは Notification#object
から取得することができます。
@objc func handleControllerDidConnect(notification: Notification) {
guard let controller = notification.object as? GCController,
let gamepad = controller.extendedGamepad else {
return
}
・・・
}
GCExtendedGamepad
はコントローラーの入力値や入力に対するハンドラを持っており、それぞれ以下のプロパティでアクセスできます。
※一部ボタンは省略しています。
現在値を取得する場合は
// ×ボタンが押されているかどうか
let isButtonAPressed = extendedGamepad.buttonA.isPressed
// 右スティックの水平方向の傾き(-1~1)
let rightThumbstickX = extendedGamepad.rightThumbstick.xAxis.value
// 右スティックが上方向に傾けられているかどうか
let isRightThumbstickUpPressed = extendedGamepad.rightThumbstick.up.isPressed
入力を受け取ったタイミングでハンドリングしたい場合は
// ○ボタンが押された
gamepad.buttonB.pressedChangedHandler = { (input, value, isPressed) in
・・・
}
// OPTIONSボタンが押された
gamepad.buttonMenu.pressedChangedHandler = { (input, value, isPressed) in
・・・
}
// 右スティックが傾けられた
gamepad.rightThumbstick.valueChangedHandler = { (pad, xAxis, yAxis) in
・・・
}
というように使います。
UIViewを操作してみる
以下のようなサンプルを作ってみました。
- ランダムな
UIColor
のリストを表示し、セルをタップするとその色のHEX値をアラートで表示- 左スティックでTableViewをスクロール
- 右スティックでポインターを動かす
- ○ボタンでセルを選択
- OPTIONSボタンで配列の中身を新しくする
コントローラーとiPhoneの接続は以下のように行います。
- iPhoneのBluetoothをONにしておく
- SHAREボタンを長押ししながら、ライトバーが点滅するまでPSボタンを長押しする
- Bluetoothの設定画面上にDUALSHOCKが表示されるのでタップするとペアリングされます
この状態でアプリを起動するとすぐに GCControllerDidConnect
が呼ばれ入力を受け取ることができます。
簡単ですが実装してみた所感を以下にまとめます。
スクロールやポインターの移動など継続的な動きは現在値を使う
最初右スティックを使ってポインターを動かすのに rightThumbstick.valueChangedHandler
を使ってみたのですが、これは値が変化したときにのみ発火するものなので、傾けたままの場合、最初に傾けたあとポインタが動かず止まってしまいました。
なので、 GCControllerDidConnect
の通知で受け取った GCExtendedGamepad
を保持しておき CADisplayLink
を使ってフレームごとに leftThumbstick.xAxis
leftThumbstick.yAxis
を見てポインタを動かすようにしました。
逆に、ボタンの入力などその時々で処理したいイベントは pressedChangedHandler
などのハンドラーを使います。
ボタンの押下イベントは isPressed
をみる
closure の引数に isPressed
があるので容易に想像できるかも知れませんが、ボタンの入力は押したときと離したときの2回呼ばれます。
GCControllerButtonValueChangedHandler
最初なにも気にせずに pressedChangedHandler
の中に処理を書いていたら、処理が2回呼ばれていました...
ボタンが押し込まれたときに処理したい場合は isPressed == true
であることを確認する必要があります。
まとめ
ゲームに使うものと思っていましたが、UIKit製のアプリでも普通に使えるのだなと思いました。
コントローラーの入力が受け取れるだけなので、それを受け取ってどうするかは実装次第で、何でもできます。
とはいえゲーム以外の需要はほとんどないと思いますが、普段PS4でAmazonビデオやNetflixで見ている私としてはiPadで動画アプリで視聴しているときに再生/一時停止/早送り/巻き戻しとかをコントローラーで操作できるのもありな気がしました。