#気軽にジョイスティックを使いたい
と思いませんか?
…思わないですか、じゃあ終了です。
#それだと終了してしまうので
思うことにしましょう。してください。
ジョイスティック入力を使用すれば、ウインドウやターミナルがアクティブになってる必要がありません。便利でしょう?、便利だと言え。
#pygameは標準
で、Pythonと言えばpygameなんで、pygameを使います。
本当はDirectInputを使っても良かったのですが、使い方がさっぱりわからなかったマルチプラットフォームじゃなくなるのでpygameを使いました。pygameがWindows以外でどう動くのかしらんけど。
#とりあえずググると
pygameでjoystick入力だけを使いたくても、画面ださないといけないみたいなことがあちこちに書かれていましたが、画面ださなくても入力を取得しているソースを書いてある所がありました。それを元に作ってみます。
#ソースコード
import pygame
import threading, time, copy
as_xbox = ['A','B','X','Y','LT','RT','LB','RB','LS','RS','BACK','START']
as_nes = ['A','B','SELECT','START']
as_snes = ['A','B','X','Y','L','R','SELECT','START']
as_ps4 = ['CIRCLE','CROSS','TRIANGLE','SQUARE','L1','R1','L2','R2','L3','R3']
def assign(status , assignlist = []):
pushlist = []
if len(status.axis) != 0:
if status.axis[0] < 0:
pushlist.append('left')
elif status.axis[0] > 0:
pushlist.append('right')
if status.axis[1] < 0:
pushlist.append('up')
elif status.axis[1] > 0:
pushlist.append('down')
if len(status.hat) != 0:
if status.hat[0] < 0:
pushlist.append('left')
elif status.hat[0] > 0:
pushlist.append('right')
if status.hat[1] < 0:
pushlist.append('down')
elif status.hat[1] > 0:
pushlist.append('up')
if len(assignlist) == 0:
for i,j in enumerate(status.button):
if j == 1:
pushlist.append(str(i))
else:
for i,j in enumerate(assignlist):
if i < len(status.button):
if status.button[i] == 1:
pushlist.append(j)
return pushlist
def reset():
pygame.init()
pygame.joystick.init()
global joysticks
global interval
joysticks = pygame.joystick.get_count()
interval = 1/60
reset()
def numjoystick():
return joysticks
class Joy:
def __init__(self, id):
self.joystick = pygame.joystick.Joystick(id)
self.joystick.init()
self.id = id
self.name = self.joystick.get_name()
self.buttons = self.joystick.get_numbuttons()
self.axes = self.joystick.get_numaxes()
self.hats = self.joystick.get_numhats()
self._status = status(self.id, self.name, self.axes, self.buttons, self.hats)
self._bstatus = status(self.id, self.name, self.axes, self.buttons, self.hats)
self._oneshot = status(self.id, self.name, self.axes, self.buttons, self.hats)
self._th = False
self.clearoneshot = True
def start(self, callback = ''):
self._callback = callback
self._th = True
polling = threading.Thread(target=self._polling)
polling.setDaemon(True)
polling.start()
def _polling(self):
stat = self._status
bstat = self._bstatus
ost = self._oneshot
while self._th:
s = False
time.sleep(interval)
bstat.axis = copy.deepcopy(stat.axis)
for i in range(self.axes):
stat.axis[i] = self.joystick.get_axis(i)
if ost.axis[i] != 0.0:
if stat.axis[i] == 0.0:
ost.axis[i] = 0.0
elif stat.axis[i] < 0.0:
ost.axis[i] = min(ost.axis[i],stat.axis[i])
else:
ost.axis[i] = max(ost.axis[i],stat.axis[i])
if bstat.axis[i] == 0.0 and stat.axis[i] != 0.0:
ost.axis[i] = stat.axis[i]
s = True
bstat.button = copy.deepcopy(stat.button)
for i in range(self.buttons):
stat.button[i] = self.joystick.get_button(i)
if bstat.button[i] == 0 and stat.button[i] == 1:
ost.button[i] += 1
s = True
bstat.hat = copy.deepcopy(stat.hat)
for i in range(self.hats):
hat = self.joystick.get_hat(i)
stat.hat[i*2] = hat[0]
stat.hat[i*2+1] = hat[1]
for j in [i*2,i*2+1]:
if bstat.hat[j] == 0 and stat.hat[j] != 0:
ost.hat[j] = stat.hat[j]
s = True
pygame.event.pump()
if s and self._callback != '':
self._callback(self._status)
def stop(self):
self._th = False
def get_status(self):
return self._status
def get_pushed(self):
st = copy.deepcopy(self._oneshot)
if self.clearoneshot:
self._oneshot.axis = [0.0] * self.axes
self._oneshot.button = [0] * self.buttons
self._oneshot.hat = [0] * self.hats * 2
return st
def clear_pushed(self):
self._oneshot.axis = [0.0] * self.axes
self._oneshot.button = [0] * self.buttons
self._oneshot.hat = [0] * self.hats * 2
def namecheck(self, name):
return name == self.name
def quit(self):
self._th = False
class status:
def __init__(self,id, name , axes, buttons, hats):
self.id = id
self.name = name
self.button = [0] * buttons
self.axis = [0] * axes
self.hat = [0] * hats * 2
if __name__ == '__main__':
print('Joy Module')
Joyと言うクラスと、いくつかの関数で出来ています
#使い方
###関数
#####num = joy.numjoystick()
接続されたジョイスティックの数を返します
#####joy.reset()
初期化します。モジュールのインポート時に呼び出されています。
これ以外ではジョイスティックの接続数が変わったときなどに使用します。また、この関数内のintervalの値を変えるとジョイスティックの入力をどれくらいの頻度で検出するか変えられます。
#####joy.assign( status, [assignlist])
後述するstatusは全ての軸、全てのボタン、全てのハットスイッチの状態が押されているか押されていないかで返されます。
これを、押されているボタンのみの配列に直します。
assignlistは省略可能で、省略した場合、ボタンは0~の数字です
以下の指定がある場合、それぞれのボタン名が返されます
また、ハットスイッチと1番目の軸は同じ物とされ、"up","down","left","right"とデジタル入力化されます。2番目以降の軸、ハットスイッチは無視されます。
- joy.as_nes
- 'A','B','SELECT','START'
- joy.as_snes
- 'A','B','X','Y','L','R','SELECT','START'
- joy.as_ps4
- 'CIRCLE','CROSS','TRIANGLE','SQUARE','L1','R1','L2','R2','L3','R3'
- joy.as_xbox
- 'A','B','X','Y','LT','RT','LB','RB','LS','RS','BACK','START'
###クラス
#####js = joy.Joy( id )
n番目のジョイスティックを初期化します。
####プロパティ
js.id
ジョイスティックのID
js.name
ジョイスティックの製品名
js.buttons
ジョイスティックが持つボタン数
js.axes
ジョイスティックが持つ方向軸の数
js.hats
ジョイスティックのハットボタンの数
####メソッド
#####js.start( [callback] )
ジョイスティックの監視を開始します。callbackに任意の関数をいれておくと操作があった場合にその関数が呼び出されます。
#####js.stop()
監視を停止します
#####st = js.get_status()
現在のジョイスティックの状態を返します
#####st = js.get_pushed()
前回にジョイスティックの状態を取得してからされたジョイスティックの動作を返します
※get_statusではボタンを押しっぱなしの時は常に押されていると判定しますが、get_pushedは押しっぱなしの場合押した最初の呼び出しのみ押されている判定がされます
#####js.clear_pushed()
js.get_pushed()
の状態をリセットします
####コールバック関数
#####callback(status)
####状態の見方
ボタンの押下状態は、以下の形で返されます
st.id
ジョイスティックのid
st.name
ジョイスティックの名前
st.button[]
ボタンnの状態 (0 = 押されていない,1 = 押されている)
st.axes[]
n軸の状態 (0~1.0)
st.hat[]
ハットスイッチの状態(-1 , 0 , 1)
#注意事項
pygameを使った物とは多分共存できません。
pygame.event.pump()
が多分フレームの強制スキップなのだと思うのですが、なにやってるかイマイチ理解してないし、pygame内で何が起こってるかわからないので・・・
同じように、2つ以上のジョイスティックを使用してどうなるかも詳しく検証はしていません。