Please select an option to see content specific to your location and shop online.

  • Free delivery on orders over £60 Free delivery on orders over £60
  • Next day delivery (ordered before 16:00 GMT) Next day delivery (ordered before 16:00 GMT)
  • Super Fast Shipping from £2.99 Super Fast Shipping from £2.99

Project

Make a simple game controller

Scroll down

1. Get your Pi ready

• You should start with a switched-on Raspberry Pi connected to mouse, keyboard and monitor, you can learn here how to do it using an Okdo Pi kit.

2. Build the circuit

  • Connect jumper A from the 7th pin on the top row of the Pi to the ground (blue rail) of the breadboard
  • Connect jumper B from the 19th pin on the top row of the Pi to A13 on the breadboard
  • Connect switch C with its pins bridging E13 and E15

The switch pins should easily fit into the patch points of the breadboard, if they don’t, the switch is not being positioned correctly.

  • Connect jumper D between A15 and the ground (blue rail) of the breadboard
  • Connect jumper E between A25 and the ground (blue rail) of the breadboard
  • Connect switch F with its pins bridging E25 and E27
  • Connect jumper G between A27 and the 18th pin on the top row of the Pi
  • Connect jumper H between A39 and the ground (blue rail) of the breadboard
  • Connect switch I with its pins bridging E39 and E41
  • Connect jumper J between A41 and the 20th pin on the top row of the Pi

3. Load the block game

  • Open “Thonny Python IDE” from the “Programming“ menu
#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Very simple block_game implementation
# 
# Control keys:
# Down - Drop stone faster
# Left/Right - Move stone
# Up - Rotate Stone clockwise
# Escape - Quit game
# P - Pause game
#
# Have fun!

# Copyright (c) 2010 "Kevin Chabowski"<kevin@kch42.de>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from random import randrange as rand
import pygame, sys
from gpiozero import Button
leftButton = Button(21)
rightButton = Button(20)
middleButton = Button(16)


# The configuration
config = {
     'cell_size':     20,
     'cols':          8,
     'rows':          16,
     'delay':     750,
     'maxfps':     30
}

colors = [
(0,   0,   0  ),
(255, 0,   0  ),
(0,   150, 0  ),
(0,   0,   255),
(255, 120, 0  ),
(255, 255, 0  ),
(180, 0,   255),
(0,   220, 220)
]

# Define the shapes of the single parts
block_game_shapes = [
     [[1, 1, 1],
      [0, 1, 0]],
     
     [[0, 2, 2],
      [2, 2, 0]],
     
     [[3, 3, 0],
      [0, 3, 3]],
     
     [[4, 0, 0],
      [4, 4, 4]],
     
     [[0, 0, 5],
      [5, 5, 5]],
     
     [[6, 6, 6, 6]],
     
     [[7, 7],
      [7, 7]]
]

def rotate_clockwise(shape):
     return [ [ shape[y][x]
               for y in range(len(shape)) ]
          for x in range(len(shape[0]) - 1, -1, -1) ]

def check_collision(board, shape, offset):
     off_x, off_y = offset
     for cy, row in enumerate(shape):
          for cx, cell in enumerate(row):
               try:
                    if cell and board[ cy + off_y ][ cx + off_x ]:
                         return True
               except IndexError:
                    return True
     return False

def remove_row(board, row):
     del board[row]
     return [[0 for i in range(config['cols'])]] + board
     
def join_matrixes(mat1, mat2, mat2_off):
     off_x, off_y = mat2_off
     for cy, row in enumerate(mat2):
          for cx, val in enumerate(row):
               mat1[cy+off_y-1     ][cx+off_x] += val
     return mat1

def new_board():
     board = [ [ 0 for x in range(config['cols']) ]
               for y in range(config['rows']) ]
     board += [[ 1 for x in range(config['cols'])]]
     return board

class block_gameApp(object):
     def __init__(self):
          pygame.init()
          pygame.key.set_repeat(250,25)
          self.width = config['cell_size']*config['cols']
          self.height = config['cell_size']*config['rows']
          
          self.screen = pygame.display.set_mode((self.width, self.height))
          pygame.event.set_blocked(pygame.MOUSEMOTION) # We do not need
                                                       # mouse movement
                                                       # events, so we
                                                       # block them.
          self.init_game()
     
     def leftButtonChanged(self):
        print("left pressed")
        self.move(-1)


     def middleButtonChanged(self):
        print("middle pressed")
        self.rotate_stone()


     def rightButtonChanged(self):
        print("right pressed")
        self.move(+1)

     def new_stone(self):
          self.stone = block_game_shapes[rand(len(block_game_shapes))]
          self.stone_x = int(config['cols'] / 2 - len(self.stone[0])/2)
          self.stone_y = 0
          
          if check_collision(self.board,
                             self.stone,
                             (self.stone_x, self.stone_y)):
               self.gameover = True
     
     def init_game(self):
          self.board = new_board()
          self.new_stone()
     
     def center_msg(self, msg):
          for i, line in enumerate(msg.splitlines()):
               msg_image =  pygame.font.Font(
                    pygame.font.get_default_font(), 12).render(
                         line, False, (255,255,255), (0,0,0))
          
               msgim_center_x, msgim_center_y = msg_image.get_size()
               msgim_center_x //= 2
               msgim_center_y //= 2
          
               self.screen.blit(msg_image, (
                 self.width // 2-msgim_center_x,
                 self.height // 2-msgim_center_y+i*22))
     
     def draw_matrix(self, matrix, offset):
          off_x, off_y  = offset
          for y, row in enumerate(matrix):
               for x, val in enumerate(row):
                    if val:
                         pygame.draw.rect(
                              self.screen,
                              colors[val],
                              pygame.Rect(
                                   (off_x+x) *
                                     config['cell_size'],
                                   (off_y+y) *
                                     config['cell_size'], 
                                   config['cell_size'],
                                   config['cell_size']),0)
     
     def move(self, delta_x):
          if not self.gameover and not self.paused:
               new_x = self.stone_x + delta_x
               if new_x < 0:
                    new_x = 0
               if new_x > config['cols'] - len(self.stone[0]):
                    new_x = config['cols'] - len(self.stone[0])
               if not check_collision(self.board,
                                      self.stone,
                                      (new_x, self.stone_y)):
                    self.stone_x = new_x
     def quit(self):
          self.center_msg("Exiting...")
          pygame.display.update()
          sys.exit()
     
     def drop(self):
          if not self.gameover and not self.paused:
               self.stone_y += 1
               if check_collision(self.board,
                                  self.stone,
                                  (self.stone_x, self.stone_y)):
                    self.board = join_matrixes(
                      self.board,
                      self.stone,
                      (self.stone_x, self.stone_y))
                    self.new_stone()
                    while True:
                         for i, row in enumerate(self.board[:-1]):
                              if 0 not in row:
                                   self.board = remove_row(
                                     self.board, i)
                                   break
                         else:
                              break
     
     def rotate_stone(self):
          if not self.gameover and not self.paused:
               new_stone = rotate_clockwise(self.stone)
               if not check_collision(self.board,
                                      new_stone,
                                      (self.stone_x, self.stone_y)):
                    self.stone = new_stone
     
     def toggle_pause(self):
          self.paused = not self.paused
     
     def start_game(self):
          if self.gameover:
               self.init_game()
               self.gameover = False
     
     def run(self):
          key_actions = {
               'ESCAPE':    self.quit,
               'LEFT':      lambda:self.move(-1),
               'RIGHT':     lambda:self.move(+1),
               'DOWN':      self.drop,
               'UP':        self.rotate_stone,
               'p':         self.toggle_pause,
               'SPACE':     self.start_game
          }
          leftButton.when_pressed = self.leftButtonChanged
          middleButton.when_pressed = self.middleButtonChanged
          rightButton.when_pressed = self.rightButtonChanged
          self.gameover = False
          self.paused = False
          
          pygame.time.set_timer(pygame.USEREVENT+1, config['delay'])
          dont_burn_my_cpu = pygame.time.Clock()
          while 1:
               
                                                           
               self.screen.fill((0,0,0))
               if self.gameover:
                    self.center_msg("""Game Over!
Press space to continue""")
               else:
                    if self.paused:
                         self.center_msg("Paused")
                    else:
                         self.draw_matrix(self.board, (0,0))
                         self.draw_matrix(self.stone,
                                          (self.stone_x,
                                           self.stone_y))
               pygame.display.update()
               
               for event in pygame.event.get():
                    if event.type == pygame.USEREVENT+1:
                         self.drop()
                    elif event.type == pygame.QUIT:
                         self.quit()
                    elif event.type == pygame.KEYDOWN:
                         for key in key_actions:
                              if event.key == eval("pygame.K_"
                              +key):
                                   key_actions[key]()
                         
               dont_burn_my_cpu.tick(config['maxfps'])

if __name__ == '__main__':
     App = block_gameApp()
     App.run()
  • Paste the Block game code above into Thonny’s editor window
  • Save the file as block_game.py
  • Click Run

  • To play turn your controller around so that it’s positioned as in the image below

Tetris will start up and you’ll be able to control the left and right position of the pieces using the outer buttons and the rotation with the middle button.

#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Very simple block_game implementation
# 
# Control keys:
# Down - Drop stone faster
# Left/Right - Move stone
# Up - Rotate Stone clockwise
# Escape - Quit game
# P - Pause game
#
# Have fun!

# Copyright (c) 2010 "Kevin Chabowski"<kevin@kch42.de>
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from random import randrange as rand
import pygame, sys
from gpiozero import Button
leftButton = Button(21)
rightButton = Button(20)
middleButton = Button(16)


# The configuration
config = {
     'cell_size':     20,
     'cols':          8,
     'rows':          16,
     'delay':     750,
     'maxfps':     30
}

colors = [
(0,   0,   0  ),
(255, 0,   0  ),
(0,   150, 0  ),
(0,   0,   255),
(255, 120, 0  ),
(255, 255, 0  ),
(180, 0,   255),
(0,   220, 220)
]

# Define the shapes of the single parts
block_game_shapes = [
     [[1, 1, 1],
      [0, 1, 0]],
     
     [[0, 2, 2],
      [2, 2, 0]],
     
     [[3, 3, 0],
      [0, 3, 3]],
     
     [[4, 0, 0],
      [4, 4, 4]],
     
     [[0, 0, 5],
      [5, 5, 5]],
     
     [[6, 6, 6, 6]],
     
     [[7, 7],
      [7, 7]]
]

def rotate_clockwise(shape):
     return [ [ shape[y][x]
               for y in range(len(shape)) ]
          for x in range(len(shape[0]) - 1, -1, -1) ]

def check_collision(board, shape, offset):
     off_x, off_y = offset
     for cy, row in enumerate(shape):
          for cx, cell in enumerate(row):
               try:
                    if cell and board[ cy + off_y ][ cx + off_x ]:
                         return True
               except IndexError:
                    return True
     return False

def remove_row(board, row):
     del board[row]
     return [[0 for i in range(config['cols'])]] + board
     
def join_matrixes(mat1, mat2, mat2_off):
     off_x, off_y = mat2_off
     for cy, row in enumerate(mat2):
          for cx, val in enumerate(row):
               mat1[cy+off_y-1     ][cx+off_x] += val
     return mat1

def new_board():
     board = [ [ 0 for x in range(config['cols']) ]
               for y in range(config['rows']) ]
     board += [[ 1 for x in range(config['cols'])]]
     return board

class block_gameApp(object):
     def __init__(self):
          pygame.init()
          pygame.key.set_repeat(250,25)
          self.width = config['cell_size']*config['cols']
          self.height = config['cell_size']*config['rows']
          
          self.screen = pygame.display.set_mode((self.width, self.height))
          pygame.event.set_blocked(pygame.MOUSEMOTION) # We do not need
                                                       # mouse movement
                                                       # events, so we
                                                       # block them.
          self.init_game()
     
     def leftButtonChanged(self):
        print("left pressed")
        self.move(-1)


     def middleButtonChanged(self):
        print("middle pressed")
        self.rotate_stone()


     def rightButtonChanged(self):
        print("right pressed")
        self.move(+1)

     def new_stone(self):
          self.stone = block_game_shapes[rand(len(block_game_shapes))]
          self.stone_x = int(config['cols'] / 2 - len(self.stone[0])/2)
          self.stone_y = 0
          
          if check_collision(self.board,
                             self.stone,
                             (self.stone_x, self.stone_y)):
               self.gameover = True
     
     def init_game(self):
          self.board = new_board()
          self.new_stone()
     
     def center_msg(self, msg):
          for i, line in enumerate(msg.splitlines()):
               msg_image =  pygame.font.Font(
                    pygame.font.get_default_font(), 12).render(
                         line, False, (255,255,255), (0,0,0))
          
               msgim_center_x, msgim_center_y = msg_image.get_size()
               msgim_center_x //= 2
               msgim_center_y //= 2
          
               self.screen.blit(msg_image, (
                 self.width // 2-msgim_center_x,
                 self.height // 2-msgim_center_y+i*22))
     
     def draw_matrix(self, matrix, offset):
          off_x, off_y  = offset
          for y, row in enumerate(matrix):
               for x, val in enumerate(row):
                    if val:
                         pygame.draw.rect(
                              self.screen,
                              colors[val],
                              pygame.Rect(
                                   (off_x+x) *
                                     config['cell_size'],
                                   (off_y+y) *
                                     config['cell_size'], 
                                   config['cell_size'],
                                   config['cell_size']),0)
     
     def move(self, delta_x):
          if not self.gameover and not self.paused:
               new_x = self.stone_x + delta_x
               if new_x < 0:
                    new_x = 0
               if new_x > config['cols'] - len(self.stone[0]):
                    new_x = config['cols'] - len(self.stone[0])
               if not check_collision(self.board,
                                      self.stone,
                                      (new_x, self.stone_y)):
                    self.stone_x = new_x
     def quit(self):
          self.center_msg("Exiting...")
          pygame.display.update()
          sys.exit()
     
     def drop(self):
          if not self.gameover and not self.paused:
               self.stone_y += 1
               if check_collision(self.board,
                                  self.stone,
                                  (self.stone_x, self.stone_y)):
                    self.board = join_matrixes(
                      self.board,
                      self.stone,
                      (self.stone_x, self.stone_y))
                    self.new_stone()
                    while True:
                         for i, row in enumerate(self.board[:-1]):
                              if 0 not in row:
                                   self.board = remove_row(
                                     self.board, i)
                                   break
                         else:
                              break
     
     def rotate_stone(self):
          if not self.gameover and not self.paused:
               new_stone = rotate_clockwise(self.stone)
               if not check_collision(self.board,
                                      new_stone,
                                      (self.stone_x, self.stone_y)):
                    self.stone = new_stone
     
     def toggle_pause(self):
          self.paused = not self.paused
     
     def start_game(self):
          if self.gameover:
               self.init_game()
               self.gameover = False
     
     def run(self):
          key_actions = {
               'ESCAPE':    self.quit,
               'LEFT':      lambda:self.move(-1),
               'RIGHT':     lambda:self.move(+1),
               'DOWN':      self.drop,
               'UP':        self.rotate_stone,
               'p':         self.toggle_pause,
               'SPACE':     self.start_game
          }
          leftButton.when_pressed = self.leftButtonChanged
          middleButton.when_pressed = self.middleButtonChanged
          rightButton.when_pressed = self.rightButtonChanged
          self.gameover = False
          self.paused = False
          
          pygame.time.set_timer(pygame.USEREVENT+1, config['delay'])
          dont_burn_my_cpu = pygame.time.Clock()
          while 1:
               
                                                           
               self.screen.fill((0,0,0))
               if self.gameover:
                    self.center_msg("""Game Over!
Press space to continue""")
               else:
                    if self.paused:
                         self.center_msg("Paused")
                    else:
                         self.draw_matrix(self.board, (0,0))
                         self.draw_matrix(self.stone,
                                          (self.stone_x,
                                           self.stone_y))
               pygame.display.update()
               
               for event in pygame.event.get():
                    if event.type == pygame.USEREVENT+1:
                         self.drop()
                    elif event.type == pygame.QUIT:
                         self.quit()
                    elif event.type == pygame.KEYDOWN:
                         for key in key_actions:
                              if event.key == eval("pygame.K_"
                              +key):
                                   key_actions[key]()
                         
               dont_burn_my_cpu.tick(config['maxfps'])

if __name__ == '__main__':
     App = block_gameApp()
     App.run()

Like what you read? Why not show your appreciation by giving some love.

From a quick tap to smashing that love button and show how much you enjoyed this project.