Introduction¶
Note: This code (or any PyGame joystick code) will cause a crazy amount of spurious output in certain versions of PyGame. This is not my code's fault. This is a bug in PyGame where some baffoon left a printf in his code when he checked in and it somehow made it into the public release. 27 Turtle Graphic Drawing Dot Pattern|. 6 Optical Illusion Drawing Techniques & Patterns - Duration: 5:58. Amy Pearce 315,951 views. Game Development in Python 3 With PyGame - 1.
Jupyter has a beautiful notebook that lets you write and execute code, analyze data, embed content, and share reproducible work. Jupyter Notebook (previously referred to as IPython Notebook) allows you to easily share your code, data, plots, and explanation in a sinle notebook. Publishing is flexible: PDF, HTML, ipynb, dashboards, slides, and more. Code cells are based on an input and output format. For example:
Installation¶
There are a few ways to use a Jupyter Notebook:
- Install with
pip
. Open a terminal and type:$ pip install jupyter
. - Windows users can install with
setuptools
. - Anaconda and Enthought allow you to download a desktop version of Jupyter Notebook.
- nteract allows users to work in a notebook enviornment via a desktop application.
- Microsoft Azure provides hosted access to Jupyter Notebooks.
- Domino Data Lab offers web-based Notebooks.
- tmpnb launches a temporary online Notebook for individual users.
Getting Started¶
Once you've installed the Notebook, you start from your terminal by calling
$ jupyter notebook
. This will open a browser on a localhost to the URL of your Notebooks, by default http://127.0.0.1:8888. Windows users need to open up their Command Prompt. You'll see a dashboard with all your Notebooks. You can launch your Notebooks from there. The Notebook has the advantage of looking the same when you're coding and publishing. You just have all the options to move code, run cells, change kernels, and use Markdown when you're running a NB.Helpful Commands¶
- Tab Completion: Jupyter supports tab completion! You can type
- Help: provides an introduction and overview of features.
object_name.<TAB>
to view an object’s attributes. For tips on cell magics, running Notebooks, and exploring objects, check out the Jupyter docs.- Help: provides an introduction and overview of features.
- Quick Reference: open quick reference by running:
- Keyboard Shortcuts:
Shift-Enter
will run a cell, Ctrl-Enter
will run a cell in-place, Alt-Enter
will run a cell and insert another below. See more shortcuts here.Languages¶
The bulk of this tutorial discusses executing python code in Jupyter notebooks. You can also use Jupyter notebooks to execute R code. Skip down to the [R section] for more information on using IRkernel with Jupyter notebooks and graphing examples.
Package Management¶
When installing packages in Jupyter, you either need to install the package in your actual shell, or run the
!
prefix, e.g.:You may want to reload submodules if you've edited the code in one. IPython comes with automatic reloading magic. You can reload all changed modules before executing a new line.
Some useful packages that we'll use in this tutorial include:
- Pandas: import data via a url and create a dataframe to easily handle data for analysis and graphing. See examples of using Pandas here: https://plot.ly/pandas/.
- NumPy: a package for scientific computing with tools for algebra, random number generation, integrating with databases, and managing data. See examples of using NumPy here: https://plot.ly/numpy/.
- SciPy: a Python-based ecosystem of packages for math, science, and engineering.
- Plotly: a graphing library for making interactive, publication-quality graphs. See examples of statistic, scientific, 3D charts, and more here: https://plot.ly/python.
Import Data¶
You can use pandas
read_csv()
function to import data. In the example below, we import a csv hosted on github and display it in a table using Plotly:Use
dataframe.column_title
to index the dataframe:Most pandas functions also work on an entire dataframe. For example, calling
std()
calculates the standard deviation for each column.Plotting Inline¶
You can use Plotly's python API to plot inside your Jupyter Notebook by calling
plotly.plotly.iplot()
or plotly.offline.iplot()
if working offline. Plotting in the notebook gives you the advantage of keeping your data analysis and plots in one place. Now we can do a bit of interactive plotting. Head to the Plotly getting started page to learn how to set your credentials. Calling the plot with iplot
automaticallly generates an interactive version of the plot inside the Notebook in an iframe. See below:Plotting multiple traces and styling the chart with custom colors and titles is simple with Plotly syntax. Additionally, you can control the privacy with
sharing
set to public
, private
, or secret
.Now we have interactive charts displayed in our notebook. Hover on the chart to see the values for each bar, click and drag to zoom into a specific section or click on the legend to hide/show a trace.
![Pygame draw polygon Pygame draw polygon](/uploads/1/2/5/8/125864779/540774943.png)
Plotting Interactive Maps¶
Plotly is now integrated with Mapbox. In this example we'll plot lattitude and longitude data of nuclear waste sites. To plot on Mapbox maps with Plotly you'll need a Mapbox account and a Mapbox Access Token which you can add to your Plotly settings.
3D Plotting¶
Using Numpy and Plotly, we can make interactive 3D plots in the Notebook as well.
Animated Plots¶
Checkout Plotly's animation documentation to see how to create animated plots inline in Jupyter notebooks like the Gapminder plot displayed below:
Plot Controls & IPython widgets¶
Add sliders, buttons, and dropdowns to your inline chart:
Additionally, IPython widgets allow you to add sliders, widgets, search boxes, and more to your Notebook. See the widget docs for more information. For others to be able to access your work, they'll need IPython. Or, you can use a cloud-based NB option so others can run your work.
Executing R Code¶
IRkernel, an R kernel for Jupyter, allows you to write and execute R code in a Jupyter notebook. Checkout the IRkernel documentation for some simple installation instructions. Once IRkernel is installed, open a Jupyter Notebook by calling
$ jupyter notebook
and use the New dropdown to select an R notebook.See a full R example Jupyter Notebook here: https://plot.ly/~chelsea_lyn/14069
Additional Embed Features¶
We've seen how to embed Plotly tables and charts as iframes in the notebook, with
IPython.display
we can embed additional features, such a videos. For example, from YouTube:LaTeX¶
We can embed LaTeX inside a Notebook by putting a
$$
around our math, then run the cell as a Markdown cell. For example, the cell below is $$c = sqrt{a^2 + b^2}$$
, but the Notebook renders the expression.![Dashed Dashed](/uploads/1/2/5/8/125864779/180733858.png)
Or, you can display output from Python, as seen here.
$displaystyle F(k) = int_{-infty}^{infty} f(x) e^{2pi i k} dx$
Exporting & Publishing Notebooks¶
We can export the Notebook as an HTML, PDF, .py, .ipynb, Markdown, and reST file. You can also turn your NB into a slideshow. You can publish Jupyter Notebooks on Plotly. Simply visit plot.ly and select the
+ Create
button in the upper right hand corner. Select Notebook and upload your Jupyter notebook (.ipynb) file!The notebooks that you upload will be stored in your Plotly organize folder and hosted at a unique link to make sharing quick and easy.See some example notebooks:Publishing Dashboards¶
Users publishing interactive graphs can also use Plotly's dashboarding tool to arrange plots with a drag and drop interface. These dashboards can be published, embedded, and shared.
Publishing Dash Apps¶
For users looking to ship and productionize Python apps, dash is an assemblage of Flask, Socketio, Jinja, Plotly and boiler plate CSS and JS for easily creating data visualization web-apps with your Python data analysis backend.
Jupyter Gallery¶
For more Jupyter tutorials, checkout Plotly's python documentation: all documentation is written in jupyter notebooks that you can download and run yourself or checkout these user submitted examples!
import time
import pygame
from pygame.locals import *
# The individual event object that is returned
# This serves as a proxy to pygame's event object
# and the key field is one of the strings in the button list listed below
# in the InputManager's constructor
# This comment is actually longer than the class definition itself.
class InputEvent:
def __init__(self, key, down):
self.key = key
self.down = down
self.up = not down
# This is where all the magic happens
class InputManager:
def __init__(self):
self.init_joystick()
# I like SNES button designations. My decision to use them are arbitrary
# and are only used internally to consistently identify buttons.
# Or you could pretend that these were XBox button layout designations.
# Either way. Up to you. You could change them altogether if you want.
self.buttons = ['up', 'down', 'left', 'right', 'start', 'A', 'B', 'X', 'Y', 'L', 'R']
# If you would like there to be a keyboard fallback configuration, fill those out
# here in this mapping. If you wanted the keyboard keys to be configurable, you could
# probably copy the same sort of system I use for the joystick configuration for the
# keyboard. But that's getting fancy for a simple tutorial.
self.key_map = {
K_UP : 'up',
K_DOWN : 'down',
K_LEFT : 'left',
K_RIGHT : 'right',
K_RETURN : 'start',
K_a : 'A',
K_b : 'B',
K_x : 'X',
K_y : 'Y',
K_l : 'L',
K_r : 'R'
}
# This dictionary will tell you which logical buttons are pressed, whether it's
# via the keyboard or joystick
self.keys_pressed = {}
for button in self.buttons:
self.keys_pressed[button] = False
# This is a list of joystick configurations that will be populated during the
# configuration phase
self.joystick_config = {}
# Quitting the window is raised as an input event. And typically you also want
# that event raised when the user presses escape which is not something you
# want to configure on the joystick. That's why it's wired separately from
# everything else. When escape is pressed or the user closes the window via its
# chrome, this flag is set to True.
self.quit_attempt = False
# button is a string of the designation in the list above
def is_pressed(self, button):
return self.keys_pressed[button]
# This will pump the pygame events. If this is not called every frame,
# then the PyGame window will start to lock up.
# This is basically a proxy method for pygame's event pump and will likewise return
# a list of event proxies.
def get_events(self):
events = []
for event in pygame.event.get():
if event.type QUIT or (event.type KEYDOWN and event.key K_ESCAPE):
self.quit_attempt = True
# This is where the keyboard events are checked
if event.type KEYDOWN or event.type KEYUP:
key_pushed_down = event.type KEYDOWN
button = self.key_map.get(event.key)
if button != None:
events.append(InputEvent(button, key_pushed_down))
self.keys_pressed[button] = key_pushed_down
# And this is where each configured button is checked...
for button in self.buttons:
# determine what something like 'Y' actually means in terms of the joystick
config = self.joystick_config.get(button)
if config != None:
# if the button is configured to an actual button...
if config[0] 'is_button':
pushed = self.joystick.get_button(config[1])
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to a hat direction...
elif config[0] 'is_hat':
status = self.joystick.get_hat(config[1])
if config[2] 'x':
amount = status[0]
else:
amount = status[1]
pushed = amount config[3]
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to a trackball direction...
elif config[0] 'is_ball':
status = self.joystick.get_ball(config[1])
if config[2] 'x':
amount = status[0]
else:
amount = status[1]
if config[3] 1:
pushed = amount > 0.5
else:
pushed = amount < -0.5
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to an axis direction...
elif config[0] 'is_axis':
status = self.joystick.get_axis(config[1])
if config[2] 1:
pushed = status > 0.5
else:
pushed = status < -0.5
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
return events
# Any button that is currently pressed on the game pad will be toggled
# to the button designation passed in as the 'button' parameter.
# (as long as it isn't already in use for a different button)
def configure_button(self, button):
js = self.joystick
# check buttons for activity...
for button_index in range(js.get_numbuttons()):
button_pushed = js.get_button(button_index)
if button_pushed andnot self.is_button_used(button_index):
self.joystick_config[button] = ('is_button', button_index)
returnTrue
# check hats for activity...
# (hats are the basic direction pads)
for hat_index in range(js.get_numhats()):
hat_status = js.get_hat(hat_index)
if hat_status[0] < -.5andnot self.is_hat_used(hat_index, 'x', -1):
self.joystick_config[button] = ('is_hat', hat_index, 'x', -1)
returnTrue
elif hat_status[0] > .5andnot self.is_hat_used(hat_index, 'x', 1):
self.joystick_config[button] = ('is_hat', hat_index, 'x', 1)
returnTrue
if hat_status[1] < -.5andnot self.is_hat_used(hat_index, 'y', -1):
self.joystick_config[button] = ('is_hat', hat_index, 'y', -1)
returnTrue
elif hat_status[1] > .5andnot self.is_hat_used(hat_index, 'y', 1):
&nbnbsp; self.joystick_config[button] = ('is_hat', hat_index, 'y', 1)
returnTrue
# check trackballs for activity...
# (I don't actually have a gamepad with a trackball on it. So this code
# is completely untested! Let me know if it works and is typo-free.)
for ball_index in range(js.get_numballs()):
ball_status = js.get_ball(ball_index)
if ball_status[0] < -.5andnot self.is_ball_used(ball_index, 'x', -1):
self.joystick_config[button] = ('is_ball', ball_index, 'x', -1)
returnTrue
elif ball_status[0] > .5andnot self.is_ball_used(ball_index, 'x', 1):
self.joystick_config[button] = ('is_ball', ball_index, 'x', 1)
returnTrue
if ball_status[1] < -.5andnot self.is_ball_used(ball_index, 'y', -1):
self.joystick_config[button] = ('is_ball', ball_index, 'y', -1)
returnTrue
elif ball_status[1] > .5andnot self.is_ball_used(ball_index, 'y', 1):
self.joystick_config[button] = ('is_ball', ball_index, 'y', 1)
returnTrue
# check axes for activity...
# (that's plural of axis. Not a tree chopping tool. Although a USB Axe would be awesome!)
for axis_index in range(js.get_numaxes()):
axis_status = js.get_axis(axis_index)
if axis_status < -.5andnot self.is_axis_used(axis_index, -1):
self.joystick_config[button] = ('is_axis', axis_index, -1)
returnTrue
elif axis_status > .5andnot self.is_axis_used(axis_index, 1):
self.joystick_config[button] = ('is_axis', axis_index, 1)
returnTrue
returnFalse
# The following 4 methods are helper methods used by the above method
# to determine if a particular button/axis/hat/trackball are already
# configured to a particular button designation
def is_button_used(self, button_index):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_button'and config[1] button_index:
returnTrue
returnFalse
def is_hat_used(self, hat_index, axis, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_hat':
if config[1] hat_index and config[2] axis and config[3] direction:
returnTrue
returnFalse
def is_ball_used(self, ball_index, axis, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_ball':
if config[1] ball_index and config[2] axis and config[3] direction:
returnTrue
returnFalse
def is_axis_used(self, axis_index, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_axis':
if config[1] axis_index and config[2] direction:
returnTrue
returnFalse
# Set joystick information.
# The joystick needs to be plugged in before this method is called (see main() method)
def init_joystick(self):
joystick = pygame.joystick.Joystick(0)
joystick.init()
self.joystick = joystick
self.joystick_name = joystick.get_name()
# A simple player object. This only keeps track of position.
class Player:
def __init__(self):
self.x = 320
self.y = 240
self.speed = 4
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
def move_up(self):
self.y -= self.speed
def move_down(self):
self.y += self.speed
# The main method...duh!
def main():
fps = 30
print('Plug in a USB gamepad. Do it! Do it now! Press enter after you have done this.')
wait_for_enter()
pygame.init()
num_joysticks = pygame.joystick.get_count()
if num_joysticks < 1:
print('You didn't plug in a joystick. FORSHAME!')
return
input_manager = InputManager()
screen = pygame.display.set_mode((640, 480))
button_index = 0
player = Player()
# The main game loop
whilenot input_manager.quit_attempt:
start = time.time()
screen.fill((0,0,0))
# There will be two phases to our 'game'.
is_configured = button_index >= len(input_manager.buttons)
# In the first phase, the user will be prompted to configure the joystick by pressing
# the key that is indicated on the screen
# You would probably do this in an input menu in your real game.
ifnot is_configured:
success = configure_phase(screen, input_manager.buttons[button_index], input_manager)
# if the user pressed a button and configured it...
if success:
# move on to the next button that needs to be configured
button_index += 1
# In the second phase, the user will control a 'character' on the screen (which will
# be represented by a simple blue ball) that obeys the directional commands, whether
# it's from the joystick or the keyboard.
else:
interaction_phase(screen, player, input_manager)
pygame.display.flip()
# maintain frame rate
difference = start - time.time()
delay = 1.0 / fps - difference
if delay > 0:
time.sleep(delay)
def configure_phase(screen, button, input_manager):
# need to pump windows events otherwise the window will lock up and die
input_manager.get_events()
# configure_button looks at the state of ALL buttons pressed on the joystick
# and will map the first pressed button it sees to the current button you pass
# in here.
success = input_manager.configure_button(button)
# tell user which button to configure
write_text(screen, 'Press the ' + button + ' button', 100, 100)
# If a joystick button was successfully configured, return True
return success
def interaction_phase(screen, player, input_manager):
# I dunno. This doesn't do anything. But this is how
# you would access key hit events and the like.
# Ideal for 'shooting a weapon' or 'jump' sort of events
for event in input_manager.get_events():
if event.key 'A'and event.down:
pass# weeeeeeee
if event.key 'X'and event.up:
input_manager.quit_attempted = True
# ...but for things like 'move in this direction', you want
# to know if a button is pressed and held
if input_manager.is_pressed('left'):
player.move_left()
elif input_manager.is_pressed('right'):
player.move_right()
if input_manager.is_pressed('up'):
player.move_up()
elif input_manager.is_pressed('down'):
player.move_down()
# Draw the player
pygame.draw.circle(screen, (0, 0, 255), (player.x, player.y), 20)
# There was probably a more robust way of doing this. But
# command line interaction was not the point of the tutorial.
def wait_for_enter():
try: input()
except: pass
# This renders text on the game screen.
# Also not the point of this tutorial.
cached_text = {}
cached_font = None
def write_text(screen, text, x, y):
global cached_text, cached_font
image = cached_text.get(text)
if image None:
if cached_font None:
cached_font = pygame.font.Font(pygame.font.get_default_font(), 12)
image = cached_font.render(text, True, (255, 255, 255))
cached_text[text] = image
screen.blit(image, (x, y - image.get_height()))
# Kick things off.
main()
# fin.
import pygame
from pygame.locals import *
# The individual event object that is returned
# This serves as a proxy to pygame's event object
# and the key field is one of the strings in the button list listed below
# in the InputManager's constructor
# This comment is actually longer than the class definition itself.
class InputEvent:
def __init__(self, key, down):
self.key = key
self.down = down
self.up = not down
# This is where all the magic happens
class InputManager:
def __init__(self):
self.init_joystick()
# I like SNES button designations. My decision to use them are arbitrary
# and are only used internally to consistently identify buttons.
# Or you could pretend that these were XBox button layout designations.
# Either way. Up to you. You could change them altogether if you want.
self.buttons = ['up', 'down', 'left', 'right', 'start', 'A', 'B', 'X', 'Y', 'L', 'R']
# If you would like there to be a keyboard fallback configuration, fill those out
# here in this mapping. If you wanted the keyboard keys to be configurable, you could
# probably copy the same sort of system I use for the joystick configuration for the
# keyboard. But that's getting fancy for a simple tutorial.
self.key_map = {
K_UP : 'up',
K_DOWN : 'down',
K_LEFT : 'left',
K_RIGHT : 'right',
K_RETURN : 'start',
K_a : 'A',
K_b : 'B',
K_x : 'X',
K_y : 'Y',
K_l : 'L',
K_r : 'R'
}
# This dictionary will tell you which logical buttons are pressed, whether it's
# via the keyboard or joystick
self.keys_pressed = {}
for button in self.buttons:
self.keys_pressed[button] = False
# This is a list of joystick configurations that will be populated during the
# configuration phase
self.joystick_config = {}
# Quitting the window is raised as an input event. And typically you also want
# that event raised when the user presses escape which is not something you
# want to configure on the joystick. That's why it's wired separately from
# everything else. When escape is pressed or the user closes the window via its
# chrome, this flag is set to True.
self.quit_attempt = False
# button is a string of the designation in the list above
def is_pressed(self, button):
return self.keys_pressed[button]
# This will pump the pygame events. If this is not called every frame,
# then the PyGame window will start to lock up.
# This is basically a proxy method for pygame's event pump and will likewise return
# a list of event proxies.
def get_events(self):
events = []
for event in pygame.event.get():
if event.type QUIT or (event.type KEYDOWN and event.key K_ESCAPE):
self.quit_attempt = True
# This is where the keyboard events are checked
if event.type KEYDOWN or event.type KEYUP:
key_pushed_down = event.type KEYDOWN
button = self.key_map.get(event.key)
if button != None:
events.append(InputEvent(button, key_pushed_down))
self.keys_pressed[button] = key_pushed_down
# And this is where each configured button is checked...
for button in self.buttons:
# determine what something like 'Y' actually means in terms of the joystick
config = self.joystick_config.get(button)
if config != None:
# if the button is configured to an actual button...
if config[0] 'is_button':
pushed = self.joystick.get_button(config[1])
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to a hat direction...
elif config[0] 'is_hat':
status = self.joystick.get_hat(config[1])
if config[2] 'x':
amount = status[0]
else:
amount = status[1]
pushed = amount config[3]
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to a trackball direction...
elif config[0] 'is_ball':
status = self.joystick.get_ball(config[1])
if config[2] 'x':
amount = status[0]
else:
amount = status[1]
if config[3] 1:
pushed = amount > 0.5
else:
pushed = amount < -0.5
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
# if the button is configured to an axis direction...
elif config[0] 'is_axis':
status = self.joystick.get_axis(config[1])
if config[2] 1:
pushed = status > 0.5
else:
pushed = status < -0.5
if pushed != self.keys_pressed[button]:
events.append(InputEvent(button, pushed))
self.keys_pressed[button] = pushed
return events
# Any button that is currently pressed on the game pad will be toggled
# to the button designation passed in as the 'button' parameter.
# (as long as it isn't already in use for a different button)
def configure_button(self, button):
js = self.joystick
# check buttons for activity...
for button_index in range(js.get_numbuttons()):
button_pushed = js.get_button(button_index)
if button_pushed andnot self.is_button_used(button_index):
self.joystick_config[button] = ('is_button', button_index)
returnTrue
# check hats for activity...
# (hats are the basic direction pads)
for hat_index in range(js.get_numhats()):
hat_status = js.get_hat(hat_index)
if hat_status[0] < -.5andnot self.is_hat_used(hat_index, 'x', -1):
self.joystick_config[button] = ('is_hat', hat_index, 'x', -1)
returnTrue
elif hat_status[0] > .5andnot self.is_hat_used(hat_index, 'x', 1):
self.joystick_config[button] = ('is_hat', hat_index, 'x', 1)
returnTrue
if hat_status[1] < -.5andnot self.is_hat_used(hat_index, 'y', -1):
self.joystick_config[button] = ('is_hat', hat_index, 'y', -1)
returnTrue
elif hat_status[1] > .5andnot self.is_hat_used(hat_index, 'y', 1):
&nbnbsp; self.joystick_config[button] = ('is_hat', hat_index, 'y', 1)
returnTrue
# check trackballs for activity...
# (I don't actually have a gamepad with a trackball on it. So this code
# is completely untested! Let me know if it works and is typo-free.)
for ball_index in range(js.get_numballs()):
ball_status = js.get_ball(ball_index)
if ball_status[0] < -.5andnot self.is_ball_used(ball_index, 'x', -1):
self.joystick_config[button] = ('is_ball', ball_index, 'x', -1)
returnTrue
elif ball_status[0] > .5andnot self.is_ball_used(ball_index, 'x', 1):
self.joystick_config[button] = ('is_ball', ball_index, 'x', 1)
returnTrue
if ball_status[1] < -.5andnot self.is_ball_used(ball_index, 'y', -1):
self.joystick_config[button] = ('is_ball', ball_index, 'y', -1)
returnTrue
elif ball_status[1] > .5andnot self.is_ball_used(ball_index, 'y', 1):
self.joystick_config[button] = ('is_ball', ball_index, 'y', 1)
returnTrue
# check axes for activity...
# (that's plural of axis. Not a tree chopping tool. Although a USB Axe would be awesome!)
for axis_index in range(js.get_numaxes()):
axis_status = js.get_axis(axis_index)
if axis_status < -.5andnot self.is_axis_used(axis_index, -1):
self.joystick_config[button] = ('is_axis', axis_index, -1)
returnTrue
elif axis_status > .5andnot self.is_axis_used(axis_index, 1):
self.joystick_config[button] = ('is_axis', axis_index, 1)
returnTrue
returnFalse
# The following 4 methods are helper methods used by the above method
# to determine if a particular button/axis/hat/trackball are already
# configured to a particular button designation
def is_button_used(self, button_index):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_button'and config[1] button_index:
returnTrue
returnFalse
def is_hat_used(self, hat_index, axis, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_hat':
if config[1] hat_index and config[2] axis and config[3] direction:
returnTrue
returnFalse
def is_ball_used(self, ball_index, axis, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_ball':
if config[1] ball_index and config[2] axis and config[3] direction:
returnTrue
returnFalse
def is_axis_used(self, axis_index, direction):
for button in self.buttons:
config = self.joystick_config.get(button)
if config != Noneand config[0] 'is_axis':
if config[1] axis_index and config[2] direction:
returnTrue
returnFalse
# Set joystick information.
# The joystick needs to be plugged in before this method is called (see main() method)
def init_joystick(self):
joystick = pygame.joystick.Joystick(0)
joystick.init()
self.joystick = joystick
self.joystick_name = joystick.get_name()
# A simple player object. This only keeps track of position.
class Player:
def __init__(self):
self.x = 320
self.y = 240
self.speed = 4
def move_left(self):
self.x -= self.speed
def move_right(self):
self.x += self.speed
def move_up(self):
self.y -= self.speed
def move_down(self):
self.y += self.speed
# The main method...duh!
def main():
fps = 30
print('Plug in a USB gamepad. Do it! Do it now! Press enter after you have done this.')
wait_for_enter()
pygame.init()
num_joysticks = pygame.joystick.get_count()
if num_joysticks < 1:
print('You didn't plug in a joystick. FORSHAME!')
return
input_manager = InputManager()
screen = pygame.display.set_mode((640, 480))
button_index = 0
player = Player()
# The main game loop
whilenot input_manager.quit_attempt:
start = time.time()
screen.fill((0,0,0))
# There will be two phases to our 'game'.
is_configured = button_index >= len(input_manager.buttons)
# In the first phase, the user will be prompted to configure the joystick by pressing
# the key that is indicated on the screen
# You would probably do this in an input menu in your real game.
ifnot is_configured:
success = configure_phase(screen, input_manager.buttons[button_index], input_manager)
# if the user pressed a button and configured it...
if success:
# move on to the next button that needs to be configured
button_index += 1
# In the second phase, the user will control a 'character' on the screen (which will
# be represented by a simple blue ball) that obeys the directional commands, whether
# it's from the joystick or the keyboard.
else:
interaction_phase(screen, player, input_manager)
pygame.display.flip()
# maintain frame rate
difference = start - time.time()
delay = 1.0 / fps - difference
if delay > 0:
time.sleep(delay)
def configure_phase(screen, button, input_manager):
# need to pump windows events otherwise the window will lock up and die
input_manager.get_events()
# configure_button looks at the state of ALL buttons pressed on the joystick
# and will map the first pressed button it sees to the current button you pass
# in here.
success = input_manager.configure_button(button)
# tell user which button to configure
write_text(screen, 'Press the ' + button + ' button', 100, 100)
# If a joystick button was successfully configured, return True
return success
def interaction_phase(screen, player, input_manager):
# I dunno. This doesn't do anything. But this is how
# you would access key hit events and the like.
# Ideal for 'shooting a weapon' or 'jump' sort of events
for event in input_manager.get_events():
if event.key 'A'and event.down:
pass# weeeeeeee
if event.key 'X'and event.up:
input_manager.quit_attempted = True
# ...but for things like 'move in this direction', you want
# to know if a button is pressed and held
if input_manager.is_pressed('left'):
player.move_left()
elif input_manager.is_pressed('right'):
player.move_right()
if input_manager.is_pressed('up'):
player.move_up()
elif input_manager.is_pressed('down'):
player.move_down()
# Draw the player
pygame.draw.circle(screen, (0, 0, 255), (player.x, player.y), 20)
# There was probably a more robust way of doing this. But
# command line interaction was not the point of the tutorial.
def wait_for_enter():
try: input()
except: pass
# This renders text on the game screen.
# Also not the point of this tutorial.
cached_text = {}
cached_font = None
def write_text(screen, text, x, y):
global cached_text, cached_font
image = cached_text.get(text)
if image None:
if cached_font None:
cached_font = pygame.font.Font(pygame.font.get_default_font(), 12)
image = cached_font.render(text, True, (255, 255, 255))
cached_text[text] = image
screen.blit(image, (x, y - image.get_height()))
# Kick things off.
main()
# fin.