天下一反省会!

電子工作、プログラミング、読書など

\今月のイチオシ記事/

おススメ記事1
おススメ記事2
おススメ記事3
おススメ記事4

PS4コントローラーの入力をpythonプログラム上で取得する方法を紹介

今回は、pythonプログラムでPS4のコントローラー(DUALSHOCK4)の入力を取得する方法を紹介します。

pythonプログラムをPS4コントローラーで動かしたい、と考えている方はぜひ参考にしてください。

この記事はこんな人におススメ・PS4コントローラーとpythonを組み合わせて何か作りたい
・電子工作にPS4コントローラーを活かしたい

今回使用するもの

・DUALSHOCK4(PS4のコントローラ)
・python3.5が使用可能な環境(今回はAnaconda上に作成した仮想環境を使用します)

下準備について

pythonプログラム上でDUALSHOCK4の入力を受け取るためには、"pygame"というライブラリを使用すると便利です。

ただし、pygameは最新のpythonバージョンには対応していないため、古いバージョンのpython環境を作成したうえでpygameをインストールする必要があります。

そこで、今回はAnaconda上にpython3.5をインストールした仮想環境を用意し、ここにpygameをインストールすることにします。


詳しい手順はこちらの記事に記載しているので参考にしてください。

potala123.hatenablog.com


とりあえず動かしてみよう

コードをコピペする


pygameが使用できる環境が整ったら、とりあえずPS4コントローラーの入力がpygameによって取得できることを試してみましょう。

新しくファイルを作成し、以下のコードをそのままコピペしてください。使用しているライブラリはpygameとtime(標準ライブラリ)なので、すぐに動かすことができるはずです。

import pygame
import time

pygame.init()

j = pygame.joystick.Joystick(0)
j.init()

print("コントローラのボタンを押してください")
try:
    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.JOYBUTTONDOWN: #ボタンが押された場合
                if j.get_button(0):
                    print("四角ボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(1):
                    print("バツボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(2):
                    print("丸ボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(3):
                    print("三角ボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(4):
                    print("L1が押されました")
                    time.sleep(0.1)
                elif j.get_button(5):
                    print("R1が押されました")
                    time.sleep(0.1)
                elif j.get_button(6):
                    print("L2が押されました")
                    print("L2の押し込み量")
                    while j.get_axis(5) > -1:
                        events = pygame.event.get()
                        print(j.get_axis(5))
                        time.sleep(0.1)
                elif j.get_button(7):
                    print("R2が押されました")
                    print("R2の押し込み量")
                    while j.get_axis(4) > -1:
                        events = pygame.event.get()
                        print(j.get_axis(4))
                        time.sleep(0.1)
                elif j.get_button(8):
                    print("シェアボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(9):
                    print("オプションボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(10):
                    print("左スティックが押し込まれました")
                    time.sleep(0.1)
                elif j.get_button(11):
                    print("右スティックが押し込まれました")
                    time.sleep(0.1)
                elif j.get_button(12):
                    print("psボタンが押されました")
                    time.sleep(0.1)
                elif j.get_button(13):
                    print("タッチパッドが押されました")
                    time.sleep(0.1)
            elif event.type == pygame.JOYHATMOTION:
                print("十字キー座標")
                print("("+str((j.get_hat(0))[0])+","+str((j.get_hat(0))[1])+")")
            elif event.type == pygame.JOYAXISMOTION:
                if abs((j.get_axis(0))) >= 0.5 or abs((j.get_axis(1))) >= 0.5:
                    print("左スティック座標")
                    print("("+str(j.get_axis(0))+","+ str(j.get_axis(1))+")")
                    time.sleep(0.1)
                elif abs((j.get_axis(2))) >= 0.5 or abs((j.get_axis(3))) >= 0.5:
                    print("右スティック座標")
                    print("("+str(j.get_axis(2))+","+ str(j.get_axis(3))+")")
                    time.sleep(0.1)

except KeyboardInterrupt:
    print("プログラムを終了します")
    j.quit()


なお、上のプログラムを作成した際には、以下の記事を参考にしました。

www.web-dev-qa-db-ja.com


westplain.sakuraweb.com

プログラムを実行!

コピペ出来たら、"python3.5が下インストールされた環境下"かつ"コントローラーが接続された状態"でプログラムを実行します。

プログラムを実行した様子の画像
プログラムを実行した様子。押されたボタンの種類や、スティックの移動量が表示される

プログラムを終了したい場合は、Ctrl+Cで終了できます。

仕組みについて解説

無事にコントローラーの入力が取得できたところで、上のプログラムの動作についてざっくり説明します。

初期設定部分

まずはプログラムの最初の数行について見てみます。

import pygame
import time

pygame.init()

j = pygame.joystick.Joystick(0)
j.init()

ライブラリのインポートと初期化、インスタンスの作成と初期化を行っています。これ以降、jというインスタンスを用いて、コントローラーのあらゆる入力を受け取っていきます。

ボタンが押されたことを識別する

初期設定以降の部分については、

while True:

とすることで、繰り返し実行されるようになっています。

events = pygame.event.get()

の部分では、コントローラーの操作を検出し、その操作に対応したイベント(例:ボタンが押された, スティックが傾けられた など)をeventsという変数に格納します。

さて、まずはコントローラーの各種ボタンについて、どのボタンが押されたかを表示してみます。

ボタンが押された際のイベント名は"JOYBUTTONDOWN"と設定されているようです。

そこで、”ボタンが押された場合”を表すif文は次のように書けます。

if event.type == pygame.JOYBUTTONDOWN:

この一文だけでは、コントローラーのいずれかのボタンが押されたことは検出できても、どのボタンが押されたかまでは判断することができません。

そこで、

j.get_button(n):

という関数を用いることにします。

この関数は、"n番目のボタンが押されたときTrueとなる"という機能を持っています。例えば3番目のボタン(実はPS4コントローラーでは3番は▲ボタンに対応しています。後ほど解説します)が押されたかどうか判定するには、

if j.get_button(3):

とすればよいことになります。

さて、ここで一度、各ボタンに割り当てられた番号についてまとめておきます。

各ボタンの番号割り当て0:□ボタン
1: ×ボタン
2: ○ボタン
3: △ボタン
4: L1
5: R1
6: L2
7: R2
8: シェアボタン
9: オプションボタン
10:左スティック押し込み
11:右スティック押し込み
12:psボタン
13:タッチパッド

これを踏まえると、例えば右スティックが押し込まれた場合に分岐するif文を作りたいときは、

if j.get_button(11):

とすればよいことが分かります。

今回のプログラムでは

if j.get_button(11):
 print("右スティックが押し込まれました")

という風にすることで、各ボタンが押されたとき、押されたボタンの種類が出力されるようにしました。

十字キーの入力

十字キーが押された場合のイベント名は"JOYHATMOTION"です。そこで、十字キーが押された場合に分岐するif文は、

if event.type == pygame.JOYHATMOTION:

と書くことができます。

十字キーの入力は、次のような形で受け取ることができます。

j.get_hat(0))[0]

この関数について説明しておくと、まず左の()の中の数字は何番目の十字キーの入力かを表しています。今回用いたPS4のコントローラーには十字キーは1つしかついていないため、個々の数字は0とすればよいことになります(プログラムでは0番目,1番目...と数えていくため)。


次に右の[]の中身について。この部分では縦方向の入力と横方向の入力のうち、どちらを表示するかを表しています。
0なら横方向、1なら縦方向となります。

これらを踏まえると、j.get_hat(0))[0]は十字キーの左右の入力、j.get_hat(0))[1]は上下の入力を表すことになります。

また各方向へボタンを押した場合の出力は次のようになります。

十字キー入力に対する出力右方向に入力した場合⇒ j.get_hat(0))[0]の値: 1
左方向に入力した場合⇒ j.get_hat(0))[0]の値: -1
上方向に入力した場合⇒ j.get_hat(0))[1]の値: 1
下方向に入力した場合⇒ j.get_hat(0))[1]の値: -1

スティック入力について

スティックを動かしたときに発生するイベント名はJOYAXISMOTIONです。そのため、スティックが動かされたときにTrueになるif文は次のように書けます。

if event.type == pygame.JOYAXISMOTION:

スティックの入力は、次の関数で取得できます。

j.get_axis(n) #nには整数が入ります

この関数の()の中に入れる数字によって、どのスティックのどの入力を取得するかを選べます。PS4コントローラーを用いた場合の対応関係は以下の通りです。

スティック入力との対応関係j.get_axis(0) → 左スティック横方向
j.get_axis(1) → 左スティック縦方向
j.get_axis(2) → 右スティック横方向
j.get_axis(3) → 右スティック縦方向

R2、L2ボタンの押し込み量について

PS4のコントローラーのL2,R2ボタンはアナログ式になっており、どれくらい押し込まれたかを認識することができます。

押し込み量はスティックと同様に

j.get_axis(n) #nには整数が入ります

を用いて取得することができ、次のような対応関係となります。

R2,L2ボタンの押し込み量との対応j.get_axis(4) ⇒ R2ボタンの押し込み量
j.get_axis(5) ⇒ L2ボタンの押し込み量

なお、押し込み量は-1から1の値で表され、まったく押されていない場合は-1を、最大まで押し込んだ場合に1を出力します。

まとめ

このようにpygameに用意された関数を用いれば、PS4コントローラーの各ボタンの入力、スティックの傾き、L2R2ボタンの押し込み量を取得することができます。

電子工作などへの活用方法を思いついたら、また記事にしたいと思います。