Programming Tips blog

テキトーにTIPSを紹介します

ラズパイ + Python + PySDL2でPlayStation3ゲームコントローラ(DualShock3)を扱う

留意事項

 PySDL2において、Joystick関係のクラスを使うと機種によりボタン等のインデックス(番号)が異なります。
 よって、DualShockを交換したときなどにインデックスが変わり、それに応じてプログラムの改変など、面倒くさいことになります。
 ですので、GameControllerクラスを使うのが適当かと思われます。

ハードウェア

 主なハードウェア材料は下記の通りです。

ソフトウェア

 主なソフトウェア材料は下記の通りです。

  • Raspberry Pi OS Bullseye
  • Python 3.9
  • PySDL2 0.9.14
  • PySDL2-dll 2.24.0(64bit版の場合に必要)

PySDL2インストール

 まず、PySDL2をインストールします。

pip3 install PySDL2

PySDL2-dllインストール(64bit版場合のみ)

 PySDL2-dllをインストールします。

pip3 install PySDL2-dll

Pythonプログラム

インポート

 プログラムの、PySDL2関連のインポートは下記の通りです。

import ctypes
from sdl2 import *

クラス作成

 ゲームコントローラのクラスを作成します。

# ゲームコントローラクラス
class GameController:
	##########
	# コンストラクタ
	##########
	def __init__(self):
		# メンバ変数
		self.axis_value = []	# 十字キー値
		self.button_value = []	# ボタン値(TrueかFalse)
		self.device_connected = False
		
		# SDL初期化
		SDL_Init(SDL_INIT_GAMECONTROLLER)
	
	# イベント処理
	def Update(self):
		# イベントハンドラを取得する
		event = SDL_Event()
		# イベントがある間の処理??
		while SDL_PollEvent(ctypes.byref(event)) != 0:
			# ゲームコントローラデバイスを認識した場合
			if event.type == SDL_CONTROLLERDEVICEADDED:
				# (新接続をとる場合)古いデバイスをクローズする
				if self.device_connected:
					SDL_GameControllerClose(self.device)
				# デバイスをオープンする
				self.device = SDL_GameControllerOpen(event.cdevice.which)
				# デバイス接続フラグを立てる
				self.device_connected = True
				
				# リスト領域確保と初期化
				# 十字キーについて
				for i in range(SDL_CONTROLLER_AXIS_MAX):
					self.axis_value.append(0)
				# ボタンについて
				for i in range(SDL_CONTROLLER_BUTTON_MAX):
					self.button_value.append(False)
			# ゲームコントローラデバイスをはずした場合
			elif event.type == SDL_CONTROLLERDEVICEREMOVED:
				# デバイスをクローズする
				SDL_GameControllerClose(self.device)
				# デバイス接続フラグを下ろす
				self.device_connected = False
				# リスト領域の消去
				self.axis_value = []
				self.button_value = []
			# 十字キーを動かした場合
			elif event.type == SDL_CONTROLLERAXISMOTION:
				# 値を取得する。
				self.axis_value[event.caxis.axis] = event.caxis.value
			# ボタンを押した場合
			elif event.type == SDL_CONTROLLERBUTTONDOWN:
				self.button_value[event.cbutton.button] = True
			# ボタンを離した場合
			elif event.type == SDL_CONTROLLERBUTTONUP:
				self.button_value[event.cbutton.button] = False
		return
	
	# 十字キー値設定
	# 引数:インデックス, 設定値
	# 返値:なし
	def SetAxisValue(self, index, value):
		self.axis_value[index] = value
		return
	
	# 十字キー値取得
	# 引数:インデックス
	# 返値:十字キー値
	def GetAxisValue(self, index):
		return self.axis_value[index]
	
	# ボタン値設定
	# 引数:インデックス, 設定値
	# 返値:なし
	def SetButtonValue(self, index, value):
		self.button_value[index] = value
		return
	
	# ボタン値取得
	# 引数:インデックス
	# 返値:ボタン値
	def GetButtonValue(self, index):
		return self.button_value[index]

マッピング

 PlayStation3ゲームコントローラ(DualShock3)とSDL2インデックスとのマッピングは下記の通りです。

左十字キーX軸 : SDL_CONTROLLER_AXIS_LEFTX
左十字キーY軸 : SDL_CONTROLLER_AXIS_LEFTY
右十字キーX軸 : SDL_CONTROLLER_AXIS_RIGHTX
右十字キーY軸 : SDL_CONTROLLER_AXIS_RIGHTY
L2 : SDL_CONTROLLER_AXIS_TRIGGERLEFT
R2 : SDL_CONTROLLER_AXIS_TRIGGERRIGHT

バツ : SDL_CONTROLLER_BUTTON_A
丸 : SDL_CONTROLLER_BUTTON_B
四角 : SDL_CONTROLLER_BUTTON_X
三角 : SDL_CONTROLLER_BUTTON_Y
SELECT : SDL_CONTROLLER_BUTTON_BACK
(未割当) : SDL_CONTROLLER_BUTTON_GUIDE
START : SDL_CONTROLLER_BUTTON_START
左十字キープッシュ : SDL_CONTROLLER_BUTTON_LEFTSTICK
右十字キープッシュ : SDL_CONTROLLER_BUTTON_RIGHTSTICK
L1 : SDL_CONTROLLER_BUTTON_LEFTSHOULDER
R1 : SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
上方向ボタン : SDL_CONTROLLER_BUTTON_DPAD_UP
下方向ボタン : SDL_CONTROLLER_BUTTON_DPAD_DOWN
左方向ボタン : SDL_CONTROLLER_BUTTON_DPAD_LEFT
右方向ボタン : SDL_CONTROLLER_BUTTON_DPAD_RIGHT

十字キーの軸の数 : SDL_CONTROLLER_AXIS_MAX
ボタンの数 : SDL_CONTROLLER_BUTTON_MAX

一部サードパーティ製 DualShock3について

DualShock3を認識しない

 /dev/input/js0のようにDualShock3を認識しない場合があります。
 その場合は/boot/cmdline.txtに下記を追記してみてください。

hid.ignore_special_drivers=1

十字キー等がきかない

 DualShock3のLED4つが点滅していると十字キー等がききません。
 「P3」ボタンを押すことでLED1番だけが点滅し、十字キー等操作ができるようになります。

L2キー、R2キーがきかない

 L2キー、R2キーが0-32767のようなアナログボタンとして認識されず、オン、オフのデジタルボタンとして認識される場合があります。
 いろいろ格闘しましたが、結局、SDL_GameControllerMappingメソッドで特徴的な文字列を引っかけて別処理をする方法が適当かなと思います。

ドキュメント

https://wiki.libsdl.org/