Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/gamepad support #111

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ doc/_build
*.egg-info
.DS_Store
examples_dev
/venv
36 changes: 36 additions & 0 deletions examples/basic/gamepad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
WIDTH = 500
HEIGHT = 100
TITLE = "gamepad"

# GAMEPAD_1_X_AXIS = 0
# GAMEPAD_1_Y_AXIS = 1
# GAMEPAD_2_X_AXIS = 3
# GAMEPAD_2_Y_AXIS = 4

def draw():
screen.fill((0, 0, 0))

def update():
# You can use an attribute to get the button that was pressed
if gamepad_1.UP:
print('UP ON ONE')

# It can be lower or uppercase
if gamepad_1.a:
print('A ON ONE')

# You can also index with the button constant
if gamepad_1[gamepad.DOWN]:
print('DOWN ON ONE')

def on_gamepad_1_pressed(button):
print('GAMEPAD 1 PRESSED:', gamepad.name(button))

def on_gamepad_1_released(button):
print('GAMEPAD 1 RELEASED:', gamepad.name(button))

def on_gamepad_2_pressed(button):
print('GAMEPAD 2 PRESSED:', gamepad.name(button))

def on_gamepad_2_released(button):
print('GAMEPAD 2 RELEASED:', gamepad.name(button))
80 changes: 80 additions & 0 deletions examples/basic/gamepad_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
WIDTH = 300
HEIGHT = 400
TITLE = "gamepad demo"

# GAMEPAD_1_X_AXIS = 3
# GAMEPAD_1_Y_AXIS = 4

RED = (255,0,0)
GREEN = (0,255,0)

def draw():
global pad1, pad2
screen.fill((255, 255, 255))
pad1.draw()
pad2.draw()

def update():
pass

def on_gamepad_1_pressed(button):
print('GAMEPAD 1:', gamepad.name(button))
pad1[gamepad.name(button)] = True

def on_gamepad_1_released(button):
pad1[gamepad.name(button)] = False

def on_gamepad_2_pressed(button):
print('GAMEPAD 2:', gamepad.name(button))
pad2[gamepad.name(button)] = True

def on_gamepad_2_released(button):
pad2[gamepad.name(button)] = False

class Gamepad:

def __init__(self, number, offset):
self.number = number
self.offset = offset
self.A = False
self.B = False
self.UP = False
self.DOWN = False
self.RIGHT = False
self.LEFT = False

def c(self,button):
if button:
return RED
else:
return GREEN

def draw(self):
oy = self.offset
screen.draw.filled_rect(Rect(40,30+oy,220,150), (200, 200, 200))
screen.draw.filled_circle((100,70+oy), 20, self.c(self.UP))
screen.draw.filled_circle((130,100+oy), 20, self.c(self.RIGHT))
screen.draw.filled_circle((100,130+oy), 20, self.c(self.DOWN))
screen.draw.filled_circle((70,100+oy), 20, self.c(self.LEFT))

screen.draw.filled_circle((180,130+oy), 20, self.c(self.B))
screen.draw.filled_circle((230,130+oy), 20, self.c(self.A))

screen.draw.text('gamepad {}'.format(self.number), (170, 35+oy))

def __setitem__(self, key, value):
if key == 'A':
self.A = value
elif key == 'B':
self.B = value
elif key == 'UP':
self.UP = value
elif key == 'RIGHT':
self.RIGHT = value
elif key == 'DOWN':
self.DOWN = value
elif key == 'LEFT':
self.LEFT = value

pad1 = Gamepad(1, 0)
pad2 = Gamepad(2, 180)
3 changes: 3 additions & 0 deletions pgzero/builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from . import tone
from .actor import Actor
from .keyboard import keyboard
from .gamepad import gamepad_1
from .gamepad import gamepad_2
from .gamepad import gamepad
from .animation import animate
from .rect import Rect, ZRect

Expand Down
12 changes: 10 additions & 2 deletions pgzero/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
import pgzero.clock
import pgzero.keyboard
import pgzero.screen
import pgzero.gamepad

from . import constants


screen = None
DISPLAY_FLAGS = 0


def exit():
"""Wait for up to a second for all sounds to play out
and then exit
Expand Down Expand Up @@ -46,6 +45,13 @@ def __init__(self, mod):
self.icon = None
self.keyboard = pgzero.keyboard.keyboard
self.handlers = {}
self.setup_gamepads()

def setup_gamepads(self):
self.gamepad_1 = pgzero.gamepad.gamepad_1
self.gamepad_1.prepare(self)
self.gamepad_2 = pgzero.gamepad.gamepad_2
self.gamepad_2.prepare(self)

def reinit_screen(self):
"""Reinitialise the window.
Expand Down Expand Up @@ -244,6 +250,8 @@ def mainloop(self):
self.keyboard._press(event.key)
elif event.type == pygame.KEYUP:
self.keyboard._release(event.key)
self.gamepad_1.handle(event)
self.gamepad_2.handle(event)
self.dispatch_event(event)

pgzclock.tick(dt)
Expand Down
162 changes: 162 additions & 0 deletions pgzero/gamepad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import pygame
from warnings import warn

class Gamepad:
_pressed = set()

def __init__(self, joystick, key_bindings):
self.joystick_number = joystick
self.key_bindings = key_bindings
self.reverse_key_bindings = {}
self.handler = None
self.press_handler = None
self.release_handler = None
for name, key in self.key_bindings.items():
self.reverse_key_bindings[key] = name
pygame.joystick.init()
try:
self.joystick = pygame.joystick.Joystick(joystick-1)
self.joystick.init()
except pygame.error:
pass

def prepare(self, game):
handler = getattr(game.mod, 'on_gamepad_{}'.format(self.joystick_number), None)
if(callable(handler)):
self.handler = handler
press_handler = getattr(game.mod, 'on_gamepad_{}_pressed'.format(self.joystick_number), None)
if(callable(press_handler)):
self.press_handler = press_handler
release_handler = getattr(game.mod, 'on_gamepad_{}_released'.format(self.joystick_number), None)
if(callable(release_handler)):
self.release_handler = release_handler
self.x_axis = getattr(game.mod, 'GAMEPAD_{}_X_AXIS'.format(self.joystick_number), 0)
self.y_axis = getattr(game.mod, 'GAMEPAD_{}_Y_AXIS'.format(self.joystick_number), 1)

def press(self, button):
self._pressed.add(button)
self.dispatch(pygame.KEYDOWN, button)

def release(self, button):
self._pressed.discard(button)
self.dispatch(pygame.KEYUP, button)

def dispatch(self, type, button):
if self.handler:
self.handler(Event(type, button))
if type == pygame.KEYDOWN and self.press_handler:
self.press_handler(button)
if type == pygame.KEYUP and self.release_handler:
self.release_handler(button)

def handle(self, event):
if event.type == pygame.KEYDOWN:
if event.key in self.reverse_key_bindings.keys():
name = self.reverse_key_bindings[event.key]
button = buttons[name]
self.press(button)
if event.type == pygame.KEYUP:
if event.key in self.reverse_key_bindings.keys():
name = self.reverse_key_bindings[event.key]
button = buttons[name]
self.release(button)
if event.type == pygame.JOYBUTTONDOWN and event.joy == self.joystick_number-1:
if event.button == 1:
self.press(A)
if event.button == 2:
self.press(B)
if event.type == pygame.JOYBUTTONUP and event.joy == self.joystick_number-1:
if event.button == 1:
self.release(A)
if event.button == 2:
self.release(B)
if event.type == pygame.JOYAXISMOTION:
if event.joy == self.joystick_number-1:
value = int(round(event.value))
if event.axis == self.x_axis:
if value == -1:
self.press(LEFT)
if value == 1:
self.press(RIGHT)
if value == 0:
if LEFT in self._pressed:
self.release(LEFT)
if RIGHT in self._pressed:
self.release(RIGHT)
if event.axis == self.y_axis:
if value == -1:
self.press(UP)
if value == 1:
self.press(DOWN)
if value == 0:
if UP in self._pressed:
self.release(UP)
if DOWN in self._pressed:
self.release(DOWN)

def __getattr__(self, buttonName):
button = buttons[buttonName.upper()]
return button in self._pressed

def __getitem__(self, button):
return button in self._pressed

class Buttons:

def __init__(self):
self.button_names = {}
for name, key in buttons.items():
self.button_names[key] = name

def __getattr__(self, button):
return buttons[button]

def name(self, button):
return self.button_names[button]

class Event:

def __init__(self, type, button):
self.type = type
self.button = button

keybindings_1 = {
'UP': pygame.K_w,
'RIGHT': pygame.K_d,
'DOWN': pygame.K_s,
'LEFT': pygame.K_a,
'A': pygame.K_SPACE,
'B': None
}

keybindings_2 = {
'UP': pygame.K_i,
'RIGHT': pygame.K_l,
'DOWN': pygame.K_k,
'LEFT': pygame.K_j,
'A': pygame.K_m,
'B': None
}

UP = (0)
RIGHT = (1)
DOWN = (2)
LEFT = (3)
A = (4)
B = (5)

buttons = {
'UP': UP,
'RIGHT': RIGHT,
'DOWN': DOWN,
'LEFT': LEFT,
'A': A,
'B': B
}

gamepad_1 = Gamepad(1, keybindings_1)
gamepad_2 = Gamepad(2, keybindings_2)
gamepad = Buttons()
gamepad.PRESSED = pygame.KEYDOWN
gamepad.RELEASED = pygame.KEYUP