Games in Python
Published May 09, 2024
· 1 min read
I had some fun creating simple games in Python using the pygame library. Here are some of the projects I worked on:
{
description = "Nix environment with Python and pygame for games";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.mkShell {
name = "pygame-shell";
buildInputs = with pkgs; [
python312
python312Packages.pygame
];
};
});
}
Project 1: Pong

import random
import pygame
import math
from pygame.locals import *
pygame.init()
ITEM_SIZE = 20
FRAME_RATE = 60
SCREEN_SIZE = (1080, 720)
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Python Pong")
font = pygame.font.SysFont(None, 24)
clock = pygame.time.Clock()
game_is_running = True
class Colors:
white = (200, 200, 200)
red = (200, 100, 20)
green = (20, 200, 100)
blue = (100, 20, 200)
foreground = white
background = (20, 20, 20)
class Player:
def __init__(self, name):
self.name = name
self.size = 5
self.score = 0
self.direction = 0
self.rect = Rect(0, 0, ITEM_SIZE, ITEM_SIZE * self.size)
if self.name == "Left Player":
self.rect.x = ITEM_SIZE * 3
self.rect.y = SCREEN_SIZE[1] / 2 - (ITEM_SIZE * self.size / 2)
elif self.name == "Right Player":
self.rect.x = SCREEN_SIZE[0] - ITEM_SIZE * 3 - ITEM_SIZE
self.rect.y = SCREEN_SIZE[1] / 2 - (ITEM_SIZE * self.size / 2)
else:
raise Exception('Unknown Player type: ' + name)
def scored(self):
self.score += 1
def is_in_bounds(self):
future_lowest_point = self.rect.y + self.rect.h + self.direction
future_highest_point = self.rect.y + self.direction
if future_lowest_point < SCREEN_SIZE[1] and self.direction > 0:
return True
if future_highest_point > 0 and self.direction < 0:
return True
return False
def movement(self):
if self.is_in_bounds():
self.rect.y += self.direction * ITEM_SIZE / 4
self.direction = 0
def update(self):
keys = pygame.key.get_pressed()
if self.name == "Left Player":
if keys[pygame.K_w]:
self.direction = -1
if keys[pygame.K_s]:
self.direction = 1
elif self.name == "Right Player":
if keys[pygame.K_i]:
self.direction = -1
if keys[pygame.K_k]:
self.direction = 1
self.movement()
def draw(self):
color = Colors.red if self.name == "Right Player" else Colors.blue
pygame.draw.rect(screen, color, self.rect)
class Ball:
speed = 2
radius = ITEM_SIZE / 2
variance = 0.05
def spawn(self):
rand_n = random.randint(0, 10)
rand_m = random.randint(0, 10)
rand_direction_y = random.randint(25, 100) / 100 * (-1) ** rand_n
self.x = SCREEN_SIZE[0] / 2
self.y = SCREEN_SIZE[1] / 2
self.dx = (-1) ** rand_m
self.dy = 0.9
def paddle_acuracy(self, rect, future_y):
min = rect.y
max = rect.y + rect.h
future_y_scaled = future_y - min / max - min
if future_y_scaled >= 0 and future_y_scaled <= 100:
return round(math.sin(future_y_scaled / 100 * math.pi), 2)
else:
return 0
def should_bounce(self):
future_y = self.y + self.dy * self.speed
left_height = self.paddle_acuracy(player_left.rect, future_y)
right_height = self.paddle_acuracy(player_right.rect, future_y)
future_x = self.x + self.dx * self.speed
left_width = self.x - self.radius > player_left.rect.x + player_left.rect.w and future_x < player_left.rect.x
right_width = self.x + self.radius < player_right.rect.x and future_x > player_right.rect.x + player_right.rect.x
if future_y - self.radius < 0:
return (1 + self.variance, -1 + self.variance)
if future_y + self.radius > SCREEN_SIZE[1]:
return (1 + self.variance, -1 + self.variance)
if left_height > 0 and left_width:
return (-1 + self.variance, 1 + self.variance)
if right_height > 0 and right_width:
return (-1 + self.variance, 1 + self.variance)
return (1, 1)
def check_goal(self):
future_x = self.x + self.dx * self.speed
if future_x < 0:
player_right.scored()
self.spawn()
if future_x > SCREEN_SIZE[0]:
player_left.scored()
self.spawn()
def update(self):
self.check_goal()
(dx, dy) = self.should_bounce()
self.dx = self.dx * dx
self.dy = self.dy * dy
if dx != 1 or dy != 1:
self.speed = self.speed + self.variance
self.x += self.dx * self.speed
self.y += self.dy * self.speed
def draw(self):
pygame.draw.circle(screen, Colors.white, (self.x, self.y), self.radius)
def update_logic():
ball.update()
player_left.update()
player_right.update()
def draw_screen():
screen.fill((20, 20, 20))
ball.draw()
player_left.draw()
player_right.draw()
draw_score()
draw_fps()
def draw_score():
img = font.render("Score: " + str(player_left.score) + " | " + str(player_right.score), True, (20, 200, 20))
screen.blit(img, (20, 20))
def draw_fps():
img = font.render("FPS: " + str(round(clock.get_fps())), True, (20, 200, 20))
screen.blit(img, (20, 40))
def log(message):
print("\033[91m" + message + "\033[90m")
def init():
global player_left, player_right, ball
player_left = Player("Left Player")
player_right = Player("Right Player")
ball = Ball()
ball.spawn()
print("\033[90m")
def game_loop():
global game_is_running
while game_is_running:
for event in pygame.event.get():
print(event)
if event.type == pygame.QUIT:
game_is_running = False
update_logic()
draw_screen()
pygame.display.update()
clock.tick(FRAME_RATE)
if __name__ == "__main__":
init()
game_loop()
pygame.quit()
Project 2: Snake
import random
import pygame
from pygame.locals import *
pygame.init()
ITEM_SIZE = 20
FRAME_RATE = 60
SCREEN_SIZE = (1080, 720)
screen = pygame.display.set_mode(SCREEN_SIZE)
pygame.display.set_caption("Python Snake")
font = pygame.font.SysFont(None, 24)
clock = pygame.time.Clock()
game_is_running = True
class Snake:
rect = Rect(0, 0, ITEM_SIZE, ITEM_SIZE)
direction = (0, 1)
cooldown = 0
tail = []
x = 0
y = 0
def check_death(self):
global game_is_running
if self.x < 0 or self.x + ITEM_SIZE > SCREEN_SIZE[0]:
log("DEATH because you ran into the vertical border")
game_is_running = False
if self.y < 0 or self.y + ITEM_SIZE > SCREEN_SIZE[1]:
log("DEATH because you ran into the horizontal border")
game_is_running = False
for part in self.tail:
if part[0] == self.x and part[1] == self.y:
log("DEATH because you ate yourself..")
game_is_running = False
def movement(self):
if self.cooldown > 0:
self.cooldown -= 1
return
for i in range(len(self.tail)):
if i + 1 == len(self.tail):
self.tail[i] = (self.x, self.y)
break
self.tail[i] = self.tail[i + 1]
self.x += ITEM_SIZE * self.direction[0]
self.y += ITEM_SIZE * self.direction[1]
self.check_death()
self.rect = Rect(self.x, self.y, ITEM_SIZE, ITEM_SIZE)
self.cooldown = FRAME_RATE / 12
def update(self):
self.movement()
if self.x == apple.x and self.y == apple.y:
self.tail.append((self.x, self.y))
apple.spawn_apple()
def draw(self):
for part in self.tail:
rect = Rect(part[0], part[1], ITEM_SIZE, ITEM_SIZE)
pygame.draw.rect(screen, (100, 100, 100), rect)
pygame.draw.rect(screen, (200, 200, 200), self.rect)
class Apple:
rect = Rect(0, 0, ITEM_SIZE, ITEM_SIZE)
x = 0
y = 0
def spawn_apple(self):
rand_x = random.randint(0, int(SCREEN_SIZE[0] / ITEM_SIZE - 1))
rand_y = random.randint(0, int(SCREEN_SIZE[1] / ITEM_SIZE - 1))
self.x = rand_x * ITEM_SIZE
self.y = rand_y * ITEM_SIZE
log("New apple at: " + str(self.x) + "|" + str(self.y))
self.rect = Rect(self.x, self.y, ITEM_SIZE, ITEM_SIZE)
def draw(self):
pygame.draw.rect(screen, (200, 20, 20), self.rect)
def update_logic():
snake.update()
def draw_screen():
screen.fill((20, 20, 20))
apple.draw()
snake.draw()
draw_score()
draw_fps()
def draw_score():
img = font.render("Score: " + str(len(snake.tail)), True, (20, 200, 20))
screen.blit(img, (20, 20))
def draw_fps():
img = font.render("FPS: " + str(round(clock.get_fps())), True, (20, 200, 20))
screen.blit(img, (20, 40))
def check_keyboard(event):
if event.key == K_UP:
snake.direction = (0, -1)
if event.key == K_DOWN:
snake.direction = (0, 1)
if event.key == K_LEFT:
snake.direction = (-1, 0)
if event.key == K_RIGHT:
snake.direction = (1, 0)
def log(message):
print("\033[91m" + message + "\033[90m")
def init():
global snake, apple
snake = Snake()
apple = Apple()
apple.spawn_apple()
print("\033[90m")
def game_loop():
global game_is_running
while game_is_running:
for event in pygame.event.get():
print(event)
if event.type == pygame.KEYDOWN:
check_keyboard(event)
if event.type == pygame.QUIT:
game_is_running = False
update_logic()
draw_screen()
pygame.display.update()
clock.tick(FRAME_RATE)
log("\nYou died with " + str(len(snake.tail)) + " point(s).\n")
if __name__ == "__main__":
init()
game_loop()
pygame.quit()