Program games yourself and manufacture game controllers yourself with little effort - Part 2
In the first part I showed you the idea of making your own game controller and introduced the turtle race, in which you had to press your button as quickly as possible to get your turtle across the finish line first. Today I want to show you a game with analog input devices.
Pong is the first video game to be distributed worldwide. The first version from 1972 was developed by Atari for amusement arcades. With the spread of home computers in the 1980s, there were versions for them too. This game has never disappeared, there are new ideas for implementation mainly due to the spread of the Raspberry Pi and the new wave of enthusiasm for programming.
The game principle of Pong is similar to that of Table tennis: The ball moves from left to right and back on the screen. Each of the two players controls a club or racket with a rotary knob (Paddle) up and down to play the ball back. If this does not succeed, the opponent receives one point.
To move the rackets you need two game controllers, for which creative solutions are required, because the original paddles can no longer be bought. The mouse could be a solution if the second player is replaced by the unbeatable computer. In this blog we want to use self-made paddles with 10 kOhm potentiometers (see picture), but also mention the idea of Ultrasonic distance sensors in the Raspberry Pi Foundation blog .
You have surely recognized the basic material for the game controller. After you have eaten the chocolate and removed the surprise of the Ü-egg, you can drill holes in the yellow "eggs" at the top and bottom and install the potentiometers with three cables each.
Analog signals from the potentiometer in connection with Raspberry Pi means that we need an analog-to-digital converter (ADC) such as the MCP3004 or MCP3008. These are the moments when we Raspians envy the Arduino faction for the analog inputs. Fortunately, the MCP3004 is an easy-to-use component, the evaluation of which is supported by the Python module gpiozero.
Of course, we must first activate the SPI interface in the Raspberry Pi configuration. Alternatively, with the necessary changes to the program, a ADS1115 on the I2C bus. This is done using the Python module from Adafruit ADS1x15.
For the animated graphics we use the pygamezero (pgzrun) module, a new, slimmed-down variant of the very extensive and very powerful pygame module. An introduction to programming with pgzrun is provided by Mark Vanstone in The MagPi magazine, issue 77 where I saw and further developed his version of pong.
I don't want to hide the fact that I didn't manage it all by myself. The display of the score in the graphical user interface at the end of the game caused problems, so I sought help in the forum of the Raspberry Pi organization. And got it! Because of the moderation, the best forum I know, but in English.
Here is the circuit diagram with Raspberry Pi, MCP3008 and two potentiometers. Please note that the IC requires both a supply voltage and a reference voltage for the ADC. In our case both 3.3V and GND. DOUT is connected with MISO (pin21), DIN connected with MOSI (pin19), CLK with CLK (pin23) and CS with CE0 (pin24). The counting of the analog inputs starts at 0.
In our program example, I use the MCP3004, the variant with four analog inputs. Together with a Raspi breadboard cobbler, it fits on half a breadboard (see picture above).
I confine myself to the part I have added to the explanations. For the explanations about pygamezero I refer to the above-mentioned page link to The Magpi 77.
With from gpiozero import MCP3004 we import the class gpiozero. MCP3004 and then instantiate two objects pot1=MCP3004(0) And pot2=MCP3004(1). As mentioned above, the counting of analog inputs starts at 0.
In addition to the BALLSPEED, which we initially set smaller than in the original code and then increase every 60 seconds, we define an end when the first player has scored 10 points and an automatic RESTART.
Here is the program code as a whole:
# pong game based on pygamezero # Pong was the first video game with world-wide distribution, published in 1972Pong # source The MagPi magazine issue 77, pages 32 - 35 # modified by Raspberry Pi course at JFS Bad Bramstedt, Germany # with the decisive contribution by MarkyV at raspberrypi.org/forums # also thanks to Lucy Hattersley, editor of The MagPi magazine # BALLSPEED starts rather slow, but increases every 60 seconds # Game over, when one player scores 10 # Restart: Press Space Bar import pgzrun import random from gpiozero import MCP3004 import math from time import time, sleep # new pot1 = MCP3004(0) pot2 = MCP3004(1) # Set up the colours BLACK = (0 ,0 ,0 ) WHITE = (255,255,255) p1Score = p2Score = 0 END = 10 # new: end of game, when first player has scored END RESTART = 15 # restart after xx seconds BALLSPEED = 3 # changed from 5 to 3 p1Y = 300 p2Y = 300 TIME = time() def draw(): global screen, p1Score, p2Score screen.fill(BLACK) screen.draw.line((400,0),(400,600),"green") drawPaddles() drawBall() screen.draw.text(str(p1Score) , center=(105, 40), color=WHITE, fontsize=60) screen.draw.text(str(p2Score) , center=(705, 40), color=WHITE, fontsize=60) if p2Score==END: screen.draw.text("Player2 wins!" , center=(400, 30), color=WHITE, fontsize=60) if p1Score==END: screen.draw.text("Player1 wins!", center=(400, 30), color=WHITE, fontsize=60) def on_key_down(key): global p1Score, p2Score, BALLSPEED if (p2Score == END or p1Score == END) and key.name == "SPACE": p1Score = p2Score = 0 BALLSPEED = 3 print("Restart") def update(): updatePaddles() if p2Score < END and p1Score < END: updateBall() def init(): global ballX, ballY, ballDirX, ballDirY ballX = 400 ballY = 300 a = random.randint(10, 350) while (a > 80 and a < 100) or (a > 260 and a < 280): a = random.randint(10, 350) ballDirX = math.cos(math.radians(a)) ballDirY = math.sin(math.radians(a)) def drawPaddles(): global p1Y, p2Y p1rect = Rect((100, p1Y-30), (10, 60)) p2rect = Rect((700, p2Y-30), (10, 60)) screen.draw.filled_rect(p1rect, "red") screen.draw.filled_rect(p2rect, "red") def updatePaddles(): global p1Y, p2Y p1Y = (pot1.value * 540) +30 p2Y = (pot2.value * 540) +30 if keyboard.up: if p2Y > 30: p2Y -= 2 if keyboard.down: if p2Y < 570: p2Y += 2 if keyboard.w: if p1Y > 30: p1Y -= 2 if keyboard.s: if p1Y < 570: p1Y += 2 def updateBall(): global ballX, ballY, ballDirX, ballDirY, p1Score, p2Score, BALLSPEED, TIME ballX += ballDirX*BALLSPEED ballY += ballDirY*BALLSPEED ballRect = Rect((ballX-4,ballY-4),(8,8)) p1rect = Rect((100, p1Y-30), (10, 60)) p2rect = Rect((700, p2Y-30), (10, 60)) if time() - TIME > 60: #new BALLSPEED += 1 #new TIME = time() #new print("BALLSPEED is now: ", BALLSPEED) if checkCollide(ballRect, p1rect) or checkCollide(ballRect, p2rect): ballDirX *= -1 if ballY < 4 or ballY > 596: ballDirY *= -1 if ballX < 0: p2Score += 1 init() if ballX > 800: p1Score += 1 init() def checkCollide(r1,r2): return ( r1.x < r2.x + r2.w and r1.y < r2.y + r2.h and r1.x + r1.w > r2.x and r1.y + r1.h > r2.y ) def drawBall(): screen.draw.filled_circle((ballX, ballY), 8, "white") pass init() pgzrun.go()
Have fun making and playing