반응형

Python-evdev를 사용하면 리눅스 시스템에 연결되어 있는(/dev/input/) 조이스틱이나 게임패드를 조작할 때 발생하는 커널의 이벤트(입력)를 사용자 공간(Userspace)으로 보낼 수 있다.

 

조이스틱 뿐 아니라 마우스, 키보드, 터치스크린등의 이벤트를 읽고 쓸 수 있다.

 

evdev를 설치하자.

$ sudo pip3 install evdev    # available globally
$ pip3 install --user evdev  # available to the current user

 

 

이 글에서 사용할 SNES Controller

 

evdev와 함께 설치되는 입력 기기 모니터링 프로그램을 실행하고 게임패드를 조작해 보자.

python3 -m evdev.evtest

게임패드의 버튼을 누를때마다 어떤 버튼이 눌렸는지 확인 할 수 있다.

 

각 키에 설정된 상수는 아래와 같다.

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, 00)
time.sleep(1)
dev.write(e.EV_ABS, e.ABS_Y, 127)
dev.write(e.EV_SYN, 00)
 
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, 00)
            time.sleep(0.05)
            
            # + Down (↘)
            gamepad.write(e.EV_ABS, e.ABS_Y, 255)
            gamepad.write(e.EV_SYN, 00)
            time.sleep(0.05)
            
            # - Right (↓|)
            gamepad.write(e.EV_ABS, e.ABS_X, 127)
            gamepad.write(e.EV_SYN, 00)
            time.sleep(0.05)
            
            # + Right (↘)
            gamepad.write(e.EV_ABS, e.ABS_X, 255)
            gamepad.write(e.EV_SYN, 00)
            time.sleep(0.05)
            
            # Punch Press
            gamepad.write(e.EV_KEY, e.BTN_JOYSTICK, 1)
            gamepad.write(e.EV_SYN, 00)
            time.sleep(0.05)
            
            # Punch Release
            gamepad.write(e.EV_KEY, e.BTN_JOYSTICK, 0)
            gamepad.write(e.EV_SYN, 00)
            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, 00)
            time.sleep(0.05)
 
dev.close()
 

위 코드를 실행하고 스트리트 파이터 2를 실행한다. 류나 켄을 선택하고 Y버튼을 누르면 작은 손 공격이 한 번 나간후 승룡권이 나간다.

 

버튼이 많은 컨트롤러를 사용한다면 사용하지 않는 버튼을 24번째 줄 event.code에 지정해주자.

 

반응형
Posted by J-sean
: