How to read and write joystick(gamepad) events(inputs) - 조이스틱(게임패드) 이벤트 읽고 쓰기
Linux 2021. 7. 12. 22:32 |Python-evdev를 사용하면 리눅스 시스템에 연결되어 있는(/dev/input/) 조이스틱이나 게임패드를 조작할 때 발생하는 커널의 이벤트(입력)를 사용자 공간(Userspace)으로 보낼 수 있다.
조이스틱 뿐 아니라 마우스, 키보드, 터치스크린등의 이벤트를 읽고 쓸 수 있다.
evdev를 설치하자.
$ sudo pip3 install evdev # available globally
$ pip3 install --user evdev # available to the current user
evdev와 함께 설치되는 입력 기기 모니터링 프로그램을 실행하고 게임패드를 조작해 보자.
게임패드의 버튼을 누를때마다 어떤 버튼이 눌렸는지 확인 할 수 있다.
각 키에 설정된 상수는 아래와 같다.
Up - EV_ABS, ABS_Y, PRESS: 0, RELEASE: 127
Down - EV_ABS, ABS_Y, PRESS: 255, RELEASE: 127
Right - EV_ABS, ABS_X, PRESS: 255, RELEASE: 127
Left - EV_ABS, ABS_X, PRESS: 0, RELEASE: 127
X - EV_KEY, BTN_JOYSTICK or BTN_TRIGGER (288)
Y - EV_KEY, BTN_TOP (291)
A - EV_KEY, BTN_THUMB (289)
B - EV_KEY, BTN_THUMB2 (290)
L - EV_KEY, BTN_TOP2 (292)
R - EV_KEY, BTN_PINKIE (293)
SELECT - EV_KEY, BTN_BASE3 (296)
START - EV_KEY, BTN_BASE4 (297)
(VALUE - PRESS: 1, RELEASE: 0)
위 정보를 바탕으로 어떤 버튼을 눌렀는지 좀 더 쉽게 알 수 있는 프로그램을 만들어 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
import sys
import evdev
from evdev import InputDevice, categorize, ecodes
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
count = 0
for device in devices:
print(device.path, device.name, device.phys)
if device.name.startswith('usb gamepad'):
gamepad = InputDevice(device.path)
count = 1
break
if count < 1:
print("No usb gamepad found.")
sys.exit()
aBtn = 289
bBtn = 290
xBtn = 288
yBtn = 291
lBtn = 292
rBtn = 293
selBtn = 296
staBtn = 297
for event in gamepad.read_loop():
if event.type == ecodes.EV_KEY:
#print(event)
if event.value == 1: #if pressed.
if event.code == aBtn:
print("A Button Pressed.")
elif event.code == bBtn:
print("B Button Pressed.")
elif event.code == xBtn:
print("X Button Pressed.")
elif event.code == yBtn:
print("Y Button Pressed.")
elif event.code == lBtn:
print("L Button Pressed.")
elif event.code == rBtn:
print("R Button Pressed.")
elif event.code == selBtn:
print("Select Button Pressed.")
elif event.code == staBtn:
print("Start Button Pressed.")
elif event.value == 0: #if released.
print("Button Released.")
elif event.type == ecodes.EV_ABS:
absevent = categorize(event)
#print(ecodes.bytype[absevent.event.type][absevent.event.code], absevent.event.value)
if ecodes.bytype[absevent.event.type][absevent.event.code] == "ABS_X":
if absevent.event.value == 0:
print("Left.")
elif absevent.event.value == 255:
print("Right.")
elif absevent.event.value == 127:
print("Center.")
elif ecodes.bytype[absevent.event.type][absevent.event.code] == "ABS_Y":
if absevent.event.value == 0:
print("Up.")
elif absevent.event.value == 255:
print("Down.")
elif absevent.event.value == 127:
print("Center.")
|
이번엔 특정 버튼을 누르는 프로그램을 만들어 보자.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import sys, time
import evdev
from evdev import InputDevice, ecodes as e
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
count = 0
for device in devices:
print(device.path, device.name, device.phys)
if device.name.startswith('usb gamepad'):
dev = InputDevice(device.path)
count = 1
break
if count < 1:
print("No usb gamepad found.")
sys.exit()
time.sleep(10)
'''
# Jump
dev.write(e.EV_ABS, e.ABS_X, 255)
dev.write(e.EV_ABS, e.ABS_Y, 0)
dev.write(e.EV_SYN, 0, 0)
time.sleep(0.1)
dev.write(e.EV_ABS, e.ABS_X, 127)
dev.write(e.EV_ABS, e.ABS_Y, 127)
dev.write(e.EV_SYN, 0, 0)
time.sleep(0.1)
'''
'''
# Button Keep Hit
while True:
dev.write(e.EV_KEY, e.BTN_THUMB2, 1)
dev.write(e.EV_SYN, 0, 0)
time.sleep(0.1)
dev.write(e.EV_KEY, e.BTN_THUMB2, 0)
dev.write(e.EV_SYN, 0, 0)
time.sleep(0.1)
'''
# Key Holding for 1 sec
dev.write(e.EV_ABS, e.ABS_Y, 255)
dev.write(e.EV_SYN, 0, 0)
time.sleep(1)
dev.write(e.EV_ABS, e.ABS_Y, 127)
dev.write(e.EV_SYN, 0, 0)
dev.close()
|
일반적인 게임에서 '앞으로 점프', '버튼 연타', '1초간 누르고 있기' 등의 동작이 정의되었다.
조금 더 복잡한 동작을 프로그래밍 해 보자.
스트리트 파이터 2의 류 필살기인 승룡권은 아래와 같은 커맨드로 이루어져 있다.
→ + ↘ + ↓ + ↘ (→ + ↓ + ↘)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
import sys, time
import evdev
from evdev import InputDevice, ecodes as e
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
count = 0
for device in devices:
print(device.path, device.name, device.phys)
if device.name.startswith('usb gamepad'):
gamepad = InputDevice(device.path)
count = 1
break
if count < 1:
print("No usb gamepad found.")
sys.exit()
# read_loop()
# 모든 키 입력을 하나도 빼지 않고 읽어 온다. (이 루프에서 write() 되는 키 입력 포함)
# 그러므로 이 루프내에서 같은키를 두 번 이상 쓰면 무한루프에 걸린다.
for event in gamepad.read_loop():
if event.type == e.EV_KEY:
if event.value == 1 and event.code == e.BTN_TOP:
# Shoryuken
# + Right (→)
gamepad.write(e.EV_ABS, e.ABS_X, 255)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# + Down (↘)
gamepad.write(e.EV_ABS, e.ABS_Y, 255)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# - Right (↓|)
gamepad.write(e.EV_ABS, e.ABS_X, 127)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# + Right (↘)
gamepad.write(e.EV_ABS, e.ABS_X, 255)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# Punch Press
gamepad.write(e.EV_KEY, e.BTN_JOYSTICK, 1)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# Punch Release
gamepad.write(e.EV_KEY, e.BTN_JOYSTICK, 0)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
# - Right, - Down = Center
gamepad.write(e.EV_ABS, e.ABS_X, 127)
gamepad.write(e.EV_ABS, e.ABS_Y, 127)
gamepad.write(e.EV_SYN, 0, 0)
time.sleep(0.05)
dev.close()
|
위 코드를 실행하고 스트리트 파이터 2를 실행한다. 류나 켄을 선택하고 Y버튼을 누르면 작은 손 공격이 한 번 나간후 승룡권이 나간다.
버튼이 많은 컨트롤러를 사용한다면 사용하지 않는 버튼을 24번째 줄 event.code에 지정해주자.
'Linux' 카테고리의 다른 글
Linux(Ubuntu) Build Your Own Web Server - 리눅스(우분투)로 웹서버 만들기 (0) | 2021.08.25 |
---|---|
Linux(Ubuntu) Static IP Configuration - 리눅스(우분투) 고정 IP (0) | 2021.08.25 |
Dynamic Shared Object(Library) in Linux - 리눅스 동적 공유 라이브러리 (0) | 2021.02.09 |
Linux CMake를 이용한 컴파일(빌드) 자동화 (0) | 2021.02.08 |
Linux make를 이용한 컴파일(빌드) 자동화 (0) | 2021.02.07 |