Code for How to Create a Space Invaders Game in Python Tutorial


View on Github

alien.py

import pygame from settings import BULLET_SIZE from bullet import Bullet class Alien(pygame.sprite.Sprite):	def __init__(self, pos, size, row_num):	super().__init__()	self.x = pos[0]	self.y = pos[1]	# alien info	img_path = f'assets/aliens/{row_num}.png'	self.image = pygame.image.load(img_path)	self.image = pygame.transform.scale(self.image, (size, size))	self.rect = self.image.get_rect(topleft = pos)	self.mask = pygame.mask.from_surface(self.image)	self.move_speed = 5	self.to_direction = "right"	# alien status	self.bullets = pygame.sprite.GroupSingle()	def move_left(self):	self.rect.x -= self.move_speed	def move_right(self):	self.rect.x += self.move_speed	def move_bottom(self):	self.rect.y += self.move_speed	def _shoot(self):	specific_pos = (self.rect.centerx - (BULLET_SIZE // 2), self.rect.centery)	self.bullets.add(Bullet(specific_pos, BULLET_SIZE, "enemy"))	def update(self):	self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))

bullet.py

import pygame from settings import BULLET_SPEED, HEIGHT class Bullet(pygame.sprite.Sprite):	def __init__(self, pos, size, side):	super().__init__()	self.x = pos[0]	self.y = pos[1]	# bullet info	img_path = f'assets/bullet/{side}-bullet.png'	self.image = pygame.image.load(img_path)	self.image = pygame.transform.scale(self.image, (size, size))	self.rect = self.image.get_rect(topleft = pos)	self.mask = pygame.mask.from_surface(self.image)	# different bullet movement direction for both player and enemy (alien)	if side == "enemy":	self.move_speed = BULLET_SPEED	elif side == "player":	self.move_speed = (- BULLET_SPEED)	def _move_bullet(self):	self.rect.y += self.move_speed	def update(self):	self._move_bullet()	self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))	# delete the bullet if it get through out of the screen	if self.rect.bottom <= 0 or self.rect.top >= HEIGHT:	self.kill()

display.py

import pygame from settings import WIDTH, HEIGHT, SPACE, FONT_SIZE, EVENT_FONT_SIZE pygame.font.init() class Display:	def __init__(self, screen):	self.screen = screen	self.score_font = pygame.font.SysFont("monospace", FONT_SIZE)	self.level_font = pygame.font.SysFont("impact", FONT_SIZE)	self.event_font = pygame.font.SysFont("impact", EVENT_FONT_SIZE)	self.text_color = pygame.Color("blue")	self.event_color = pygame.Color("red")	def show_life(self, life):	life_size = 30	img_path = "assets/life/life.png"	life_image = pygame.image.load(img_path)	life_image = pygame.transform.scale(life_image, (life_size, life_size))	life_x = SPACE // 2	if life != 0:	for life in range(life):	self.screen.blit(life_image, (life_x, HEIGHT + (SPACE // 2)))	life_x += life_size	def show_score(self, score):	score_x = WIDTH // 3	score = self.score_font.render(f'score: {score}', True, self.text_color)	self.screen.blit(score, (score_x, (HEIGHT + (SPACE // 2))))	def show_level(self, level):	level_x = WIDTH // 3	level = self.level_font.render(f'Level {level}', True, self.text_color)	self.screen.blit(level, (level_x * 2, (HEIGHT + (SPACE // 2))))	def game_over_message(self):	message = self.event_font.render('GAME OVER!!', True, self.event_color)	self.screen.blit(message, ((WIDTH // 3) - (EVENT_FONT_SIZE // 2), (HEIGHT // 2) - (EVENT_FONT_SIZE // 2)))

main.py

import pygame, sys from settings import WIDTH, HEIGHT, NAV_THICKNESS from world import World pygame.init() screen = pygame.display.set_mode((WIDTH, HEIGHT + NAV_THICKNESS)) pygame.display.set_caption("Space Invader") class Main:	def __init__(self, screen):	self.screen = screen	self.FPS = pygame.time.Clock()	def main(self):	world = World(self.screen)	while True:	self.screen.fill("black")	for event in pygame.event.get():	if event.type == pygame.QUIT:	pygame.quit()	sys.exit()	if event.type == pygame.KEYDOWN:	if event.key == pygame.K_SPACE:	world.player_move(attack = True)	world.player_move()	world.update()	pygame.display.update()	self.FPS.tick(30) if __name__ == "__main__":	play = Main(screen)	play.main()

settings.py

WIDTH, HEIGHT = 720, 450 SPACE = 30 FONT_SIZE = 20 EVENT_FONT_SIZE = 60 NAV_THICKNESS = 50 CHARACTER_SIZE = 30 PLAYER_SPEED = 10 ENEMY_SPEED = 1 BULLET_SPEED = 15 # for both sides BULLET_SIZE = 10

ship.py

import pygame from settings import PLAYER_SPEED, BULLET_SIZE from bullet import Bullet class Ship(pygame.sprite.Sprite):	def __init__(self, pos, size):	super().__init__()	self.x = pos[0]	self.y = pos[1]	# ship info	img_path = 'assets/ship/ship.png'	self.image = pygame.image.load(img_path)	self.image = pygame.transform.scale(self.image, (size, size))	self.rect = self.image.get_rect(topleft = pos)	self.mask = pygame.mask.from_surface(self.image)	self.ship_speed = PLAYER_SPEED	# ship status	self.life = 3	self.player_bullets = pygame.sprite.Group()	def move_left(self):	self.rect.x -= self.ship_speed	def move_up(self):	self.rect.y -= self.ship_speed	def move_right(self):	self.rect.x += self.ship_speed	def move_bottom(self):	self.rect.y += self.ship_speed	def _shoot(self):	specific_pos = (self.rect.centerx - (BULLET_SIZE // 2), self.rect.y)	self.player_bullets.add(Bullet(specific_pos, BULLET_SIZE, "player"))	def update(self):	self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))

world.py

import pygame from ship import Ship from alien import Alien from settings import HEIGHT, WIDTH, ENEMY_SPEED, CHARACTER_SIZE, BULLET_SIZE, NAV_THICKNESS from bullet import Bullet from display import Display class World:	def __init__(self, screen):	self.screen = screen	self.player = pygame.sprite.GroupSingle()	self.aliens = pygame.sprite.Group()	self.display = Display(self.screen)	self.game_over = False	self.player_score = 0	self.game_level = 1	self._generate_world()	def _generate_aliens(self):	# generate opponents	alien_cols = (WIDTH // CHARACTER_SIZE) // 2	alien_rows = 3	for y in range(alien_rows):	for x in range(alien_cols):	my_x = CHARACTER_SIZE * x	my_y = CHARACTER_SIZE * y	specific_pos = (my_x, my_y)	self.aliens.add(Alien(specific_pos, CHARACTER_SIZE, y))	# create and add player to the screen	def _generate_world(self):	# create the player's ship	player_x, player_y = WIDTH // 2, HEIGHT - CHARACTER_SIZE	center_size = CHARACTER_SIZE // 2	player_pos = (player_x - center_size, player_y)	self.player.add(Ship(player_pos, CHARACTER_SIZE))	self._generate_aliens()	def add_additionals(self):	# add nav bar	nav = pygame.Rect(0, HEIGHT, WIDTH, NAV_THICKNESS)	pygame.draw.rect(self.screen, pygame.Color("gray"), nav)	# render player's life, score and game level	self.display.show_life(self.player.sprite.life)	self.display.show_score(self.player_score)	self.display.show_level(self.game_level)	def player_move(self, attack = False):	keys = pygame.key.get_pressed()	if keys[pygame.K_a] and not self.game_over or keys[pygame.K_LEFT] and not self.game_over:	if self.player.sprite.rect.left > 0:	self.player.sprite.move_left()	if keys[pygame.K_d] and not self.game_over or keys[pygame.K_RIGHT] and not self.game_over:	if self.player.sprite.rect.right < WIDTH:	self.player.sprite.move_right()	if keys[pygame.K_w] and not self.game_over or keys[pygame.K_UP] and not self.game_over:	if self.player.sprite.rect.top > 0:	self.player.sprite.move_up()	if keys[pygame.K_s] and not self.game_over or keys[pygame.K_DOWN] and not self.game_over:	if self.player.sprite.rect.bottom < HEIGHT:	self.player.sprite.move_bottom()	# game restart button	if keys[pygame.K_r]:	self.game_over = False	self.player_score = 0	self.game_level = 1	for alien in self.aliens.sprites():	alien.kill()	self._generate_world()	if attack and not self.game_over:	self.player.sprite._shoot()	def _detect_collisions(self):	# checks if player bullet hits the enemies (aliens)	player_attack_collision = pygame.sprite.groupcollide(self.aliens, self.player.sprite.player_bullets, True, True)	if player_attack_collision:	self.player_score += 10	# checks if the aliens' bullet hit the player	for alien in self.aliens.sprites():	alien_attack_collision = pygame.sprite.groupcollide(alien.bullets, self.player, True, False)	if alien_attack_collision:	self.player.sprite.life -= 1	break	# checks if the aliens hit the player	alien_to_player_collision = pygame.sprite.groupcollide(self.aliens, self.player, True, False)	if alien_to_player_collision:	self.player.sprite.life -= 1	def _alien_movement(self):	move_sideward = False	move_forward = False	for alien in self.aliens.sprites():	if alien.to_direction == "right" and alien.rect.right < WIDTH or alien.to_direction == "left" and alien.rect.left > 0:	move_sideward = True	move_forward = False	else:	move_sideward = False	move_forward = True	alien.to_direction = "left" if alien.to_direction == "right" else "right"	break	for alien in self.aliens.sprites():	if move_sideward and not move_forward:	if alien.to_direction == "right":	alien.move_right()	if alien.to_direction == "left":	alien.move_left()	if not move_sideward and move_forward:	alien.move_bottom()	def _alien_shoot(self):	for alien in self.aliens.sprites():	if (WIDTH - alien.rect.x) // CHARACTER_SIZE == (WIDTH - self.player.sprite.rect.x) // CHARACTER_SIZE:	alien._shoot()	break	def _check_game_state(self):	# check if game over	if self.player.sprite.life <= 0:	self.game_over = True	self.display.game_over_message()	for alien in self.aliens.sprites():	if alien.rect.top >= HEIGHT:	self.game_over = True	self.display.game_over_message()	break	# check if next level	if len(self.aliens) == 0 and self.player.sprite.life > 0:	self.game_level += 1	self._generate_aliens()	for alien in self.aliens.sprites():	alien.move_speed += self.game_level - 1	def update(self):	# detecting if bullet, alien, and player group is colliding	self._detect_collisions()	# allows the aliens to move	self._alien_movement()	# allows alien to shoot the player	self._alien_shoot()	# bullets rendering	self.player.sprite.player_bullets.update()	self.player.sprite.player_bullets.draw(self.screen)	[alien.bullets.update() for alien in self.aliens.sprites()]	[alien.bullets.draw(self.screen) for alien in self.aliens.sprites()]	# player ship rendering	self.player.update()	self.player.draw(self.screen)	# alien rendering	self.aliens.draw(self.screen)	# add nav	self.add_additionals()	# checks game state	self._check_game_state()