LoginSignup
5

More than 3 years have passed since last update.

GameControllerを使ってDualshock4でUIViewを操作してみる

Last updated at Posted at 2019-11-26

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ボタンで配列の中身を新しくする

dualshock.gif

コントローラーとiPhoneの接続は以下のように行います。

  1. iPhoneのBluetoothをONにしておく
  2. SHAREボタンを長押ししながら、ライトバーが点滅するまでPSボタンを長押しする
  3. 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で動画アプリで視聴しているときに再生/一時停止/早送り/巻き戻しとかをコントローラーで操作できるのもありな気がしました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5