~ Game Design


Challenge 4

Nearly there - You have added a score variable and add a smaller GREEN ball (ball2) (inherits from the ball class) that randomly moves about the screen. You've also created the functionality that allows for when ball1 touches the green ball, the score increments by +10. The code below creates a red ball (ball 3). When ball 1 touches the red ball, the score decrements by -10.

CHALLENGE : 

1. Copy and paste the code below

2. Extend the program to do the following: 

>>Add a "Game over" feature. When the score reaches 1000 (You Win) or when the score reaches -1000 (You lose)

CODE: 

import pygame
import random
import math

#Define variables wich represent the size of the map we want
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 300

#Create the ball class
class Ball:
	def __init__(self, x, y, radius, color, screen):
		#A ball need a position (x,y), a radius, a color and the screen where we will paint it, therefore
		#the constructor will take these as arguments and save their values in variables of the ball class by using the word self
		self.x = x
		self.y = y
		self.radius = radius
		self.screen = screen
		self.color = color

	#The draw function will be responsible for drawing the ball in the screen	
	def draw(self):
		pygame.draw.circle(screen, self.color, [self.x, self.y], self.radius)

#New class for the player's ball. This class extends Ball because it has the same base features but is more specific
class PlayerBall(Ball):
	def __init__(self, x, y, radius, color, screen):
		Ball.__init__(self, x, y, radius, color, screen)
		#Add variables to the PlayerBall class for the cooldown of touching the green ball and the red ball
		self.green_cooldown = 0
		self.red_cooldown = 0

	#The move function is responsible for changing the position of the ball based on the user input
	def move(self, mv_type):
		if mv_type == "UP":
			self.y -= 2
		elif mv_type == "DOWN":
			self.y += 2
		elif mv_type == "LEFT":
			self.x -= 2
		elif mv_type == "RIGHT":
			self.x += 2

		#If the ball has passed the bounds of the map then update it's position so that it stays inside
		if self.x - self.radius < 0:
			self.x = self.radius
		elif self.x + self.radius > SCREEN_WIDTH:
			self.x = SCREEN_WIDTH - self.radius
		if self.y - self.radius < 0:
			self.y = self.radius
		elif self.y + self.radius > SCREEN_HEIGHT:
			self.y = SCREEN_HEIGHT - self.radius

	#This function checks if the player's ball is touching another ball
	def check_contact(self, greenBall, redBall):
		#This variable will be returned with the summed value of the balls that are touching the player's ball
		to_return = 0
		#If the distance between the two centers is less that the sum of the radius of both balls then they are touching
		if math.sqrt((self.y - greenBall.y) ** 2 + (self.x - greenBall.x) ** 2) < self.radius + greenBall.radius:
			if self.green_cooldown == 0:
				#Update the green ball cooldown and add 10 to the return variable
				self.green_cooldown = 10
				to_return += 10
		#If the distance between the two centers is less that the sum of the radius of both balls then they are touching
		if math.sqrt((self.y - redBall.y) ** 2 + (self.x - redBall.x) ** 2) < self.radius + redBall.radius:
			if self.red_cooldown == 0:
				#Update the red ball cooldown and subtract 10 to the return variable
				self.red_cooldown = 10
				to_return -= 10
		return to_return

#This class represents the green ball that gives points when the player's ball touches it
class GreenBall(Ball):
	#Similarly to the player's ball, this class extends Ball but has two new variable
	def __init__(self, x, y, radius, color, screen):
		Ball.__init__(self, x, y, radius, color, screen)
		#vx and vy are the velocity values for this ball and are generated randomly when a GreenBall object is created
		self.vy = random.randint(0, 4) - 2
		self.vx = random.randint(0, 4) - 2
		while self.vy == 0 or self.vx == 0:
			self.vy = random.randint(0, 4) - 2
			self.vx = random.randint(0, 4) - 2

	#This function will update the ball's position
	def move(self):
		#Add the velocity value to the position
		self.x += self.vx
		self.y += self.vy

		#If the ball is outside the bounds put it inside and multiply the velocity to -1 to change the ball's direction
		if self.x - self.radius < 0:
			self.x = self.radius
			self.vx *= -1
		elif self.x + self.radius > SCREEN_WIDTH:
			self.x = SCREEN_WIDTH - self.radius
			self.vx *= -1
		if self.y - self.radius < 0:
			self.y = self.radius
			self.vy *= -1
		elif self.y + self.radius > SCREEN_HEIGHT:
			self.y = SCREEN_HEIGHT - self.radius
			self.vy *= -1

#This class represents the green ball that makes the player loose points when he touches it
#It is similar to the GreenBall class, but it's colour is red and the velocity values are different
class RedBall(Ball):
	def __init__(self, x, y, radius, color, screen):
		Ball.__init__(self, x, y, radius, color, screen)
		self.vy = random.randint(0, 6) - 3
		self.vx = random.randint(0, 6) - 3
		while self.vy == 0 or self.vx == 0:
			self.vy = random.randint(0, 6) - 3
			self.vx = random.randint(0, 6) - 3

	def move(self):
		#Add the velocity value to the position
		self.x += self.vx
		self.y += self.vy

		#If the ball is outside the bounds put it inside and multiply the velocity to -1 to change the ball's direction
		if self.x - self.radius < 0:
			self.x = self.radius
			self.vx *= -1
		elif self.x + self.radius > SCREEN_WIDTH:
			self.x = SCREEN_WIDTH - self.radius
			self.vx *= -1
		if self.y - self.radius < 0:
			self.y = self.radius
			self.vy *= -1
		elif self.y + self.radius > SCREEN_HEIGHT:
			self.y = SCREEN_HEIGHT - self.radius
			self.vy *= -1

#The next two lines initiate the game and set the window size by the values we defined on the variables SCREEN_HEIGHT and SCREEN_WIDTH
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
#The next variable represents if the user wants to quit the game (when the value is True) or not (when the value is False)
#Since we want the game to run we start it with as False
done = False
#The score variable will store the player's current score
score = 0

#Set the font and size that will be used
myfont = pygame.font.SysFont("monospace", 15)

#Create a clock value that allows us to set the FPS value we want
clock = pygame.time.Clock()

#Create a ball of each of the classes that are implemented
ball1 = PlayerBall(100, 100, 20, (0, 0, 0), screen)
ball2 = GreenBall(200, 200, 5, (0, 255, 0), screen)
ball3 = RedBall(250, 300, 20, (255, 0, 0), screen)

#While the user doesn't quit
while not done:
	#Listen to all events that happen
	for event in pygame.event.get():
		#If it's a quit event then set done to true so the game will finish
		if event.type == pygame.QUIT:
			done = True

	#Listen for keys pressed
	pressed = pygame.key.get_pressed()
	#Call the function to update the ball's position based on the keys that are being pressed
	if pressed[pygame.K_UP]:
		ball1.move("UP")
	if pressed[pygame.K_DOWN]:
		ball1.move("DOWN")
	if pressed[pygame.K_LEFT]:
		ball1.move("LEFT")
	if pressed[pygame.K_RIGHT]:
		ball1.move("RIGHT")

	#Paint the screen white
	screen.fill((255, 255, 255))

	#Set the label value to be black and write the actual score
	label = myfont.render("SCORE: " + str(score), 1, (0,0,0))
	#Print that label in the screen
	screen.blit(label, (10, SCREEN_HEIGHT - 20))

	#Call the move functions for the red and green ball to update their position
	ball2.move()
	ball3.move()
	#Update the score based on the balls that are touching the player's ball
	score += ball1.check_contact(ball2, ball3)

	#Draw all the balls by calling their draw function
	ball2.draw()
	ball3.draw()
	ball1.draw()

	#Update the cooldowns for touching the red and green ball
	if ball1.green_cooldown > 0:
		ball1.green_cooldown -= 1
	if ball1.red_cooldown > 0:
		ball1.red_cooldown -= 1

	#Update the screen
	pygame.display.flip()
	#Set the FPS value to 60
	clock.tick(60)