Easy Learn Python Space Shooter game building using Pygame
Hi, coders! Today I will show you guys, how to build one of the most popular games that we used to play during our kids & used to think to built i.e “Space Shooter”. So, today we have written a blog on how to easily build python space shooter game using pygame. Following are steps to build space shooter game,
Requirements:
- A PC with any OS installed in it. You can choose Mac Book, Asus Zenbook, Dell Inspiron, or any Pc with a high processor.
- The latest version of Python3 which can be easily downloaded from https://www.python.org/downloads/[For installing you can check this blog]
- A good Code IDE [ Prefer using VS Code or Atom]
- Your focus
Tutorial:
- Firstly, open your cmd[Command Prompt] and type the following command after installing python from https://pypi.org/project/pygame/
- pip install pygame
- pip install random2
- Secondly, copy the following codes provided along with resources that you can get in the download button below.
- Lastly, run the python code with python main.py command.[Here, main.py is the file name]. That’s it!
Source Code:
from __future__ import division
import pygame
import random
from os import path
## assets folder
img_dir = path.join(path.dirname(__file__), 'assets')
sound_folder = path.join(path.dirname(__file__), 'sounds')
###############################
## to be placed in "constant.py" later
WIDTH = 480
HEIGHT = 600
FPS = 60
POWERUP_TIME = 5000
BAR_LENGTH = 100
BAR_HEIGHT = 10
# Define Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
###############################
###############################
## to placed in "__init__.py" later
## initialize pygame and create window
pygame.init()
pygame.mixer.init() # For sound
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Space Shooter")
clock = pygame.time.Clock() # For syncing the FPS
###############################
font_name = pygame.font.match_font('arial')
def main_menu():
global screen
menu_song = pygame.mixer.music.load(path.join(sound_folder, "menu.ogg"))
pygame.mixer.music.play(-1)
title = pygame.image.load(path.join(img_dir, "main.png")).convert()
title = pygame.transform.scale(title, (WIDTH, HEIGHT), screen)
screen.blit(title, (0, 0))
pygame.display.update()
while True:
ev = pygame.event.poll()
if ev.type == pygame.KEYDOWN:
if ev.key == pygame.K_RETURN:
break
elif ev.key == pygame.K_q:
pygame.quit()
quit()
elif ev.type == pygame.QUIT:
pygame.quit()
quit()
else:
draw_text(screen, "Press [ENTER] To Begin", 30, WIDTH/2, HEIGHT/2)
draw_text(screen, "or [Q] To Quit", 30, WIDTH/2, (HEIGHT/2)+40)
pygame.display.update()
#pygame.mixer.music.stop()
ready = pygame.mixer.Sound(path.join(sound_folder, 'getready.ogg'))
ready.play()
screen.fill(BLACK)
draw_text(screen, "GET READY!", 40, WIDTH/2, HEIGHT/2)
pygame.display.update()
def draw_text(surf, text, size, x, y):
## selecting a cross platform font to display the score
font = pygame.font.Font(font_name, size)
# True denotes the font to be anti-aliased
text_surface = font.render(text, True, WHITE)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
def draw_shield_bar(surf, x, y, pct):
# if pct < 0:
# pct = 0
pct = max(pct, 0)
## moving them to top
# BAR_LENGTH = 100
# BAR_HEIGHT = 10
fill = (pct / 100) * BAR_LENGTH
outline_rect = pygame.Rect(x, y, BAR_LENGTH, BAR_HEIGHT)
fill_rect = pygame.Rect(x, y, fill, BAR_HEIGHT)
pygame.draw.rect(surf, GREEN, fill_rect)
pygame.draw.rect(surf, WHITE, outline_rect, 2)
def draw_lives(surf, x, y, lives, img):
for i in range(lives):
img_rect = img.get_rect()
img_rect.x = x + 30 * i
img_rect.y = y
surf.blit(img, img_rect)
def newmob():
mob_element = Mob()
all_sprites.add(mob_element)
mobs.add(mob_element)
class Explosion(pygame.sprite.Sprite):
def __init__(self, center, size):
pygame.sprite.Sprite.__init__(self)
self.size = size
self.image = explosion_anim[self.size][0]
self.rect = self.image.get_rect()
self.rect.center = center
self.frame = 0
self.last_update = pygame.time.get_ticks()
self.frame_rate = 75
def update(self):
now = pygame.time.get_ticks()
if now - self.last_update > self.frame_rate:
self.last_update = now
self.frame += 1
if self.frame == len(explosion_anim[self.size]):
self.kill()
else:
center = self.rect.center
self.image = explosion_anim[self.size][self.frame]
self.rect = self.image.get_rect()
self.rect.center = center
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
## scale the player img down
self.image = pygame.transform.scale(player_img, (50, 38))
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = 20
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
self.shield = 100
self.shoot_delay = 250
self.last_shot = pygame.time.get_ticks()
self.lives = 3
self.hidden = False
self.hide_timer = pygame.time.get_ticks()
self.power = 1
self.power_timer = pygame.time.get_ticks()
def update(self):
## time out for powerups
if self.power >= 2 and pygame.time.get_ticks() - self.power_time > POWERUP_TIME:
self.power -= 1
self.power_time = pygame.time.get_ticks()
## unhide
if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000:
self.hidden = False
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 30
self.speedx = 0 # makes the player static in the screen by default.
# then we have to check whether there is an event hanlding being done for the arrow keys being
## pressed
## will give back a list of the keys which happen to be pressed down at that moment
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -5
elif keystate[pygame.K_RIGHT]:
self.speedx = 5
#Fire weapons by holding spacebar
if keystate[pygame.K_SPACE]:
self.shoot()
## check for the borders at the left and right
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
self.rect.x += self.speedx
def shoot(self):
## to tell the bullet where to spawn
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
self.last_shot = now
if self.power == 1:
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
shooting_sound.play()
if self.power == 2:
bullet1 = Bullet(self.rect.left, self.rect.centery)
bullet2 = Bullet(self.rect.right, self.rect.centery)
all_sprites.add(bullet1)
all_sprites.add(bullet2)
bullets.add(bullet1)
bullets.add(bullet2)
shooting_sound.play()
""" MOAR POWAH """
if self.power >= 3:
bullet1 = Bullet(self.rect.left, self.rect.centery)
bullet2 = Bullet(self.rect.right, self.rect.centery)
# Missile shoots from center of ship
missile1 = Missile(self.rect.centerx, self.rect.top)
all_sprites.add(bullet1)
all_sprites.add(bullet2)
all_sprites.add(missile1)
bullets.add(bullet1)
bullets.add(bullet2)
bullets.add(missile1)
shooting_sound.play()
missile_sound.play()
def powerup(self):
self.power += 1
self.power_time = pygame.time.get_ticks()
def hide(self):
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.rect.center = (WIDTH / 2, HEIGHT + 200)
# defines the enemies
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image_orig = random.choice(meteor_images)
self.image_orig.set_colorkey(BLACK)
self.image = self.image_orig.copy()
self.rect = self.image.get_rect()
self.radius = int(self.rect.width * .90 / 2)
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-150, -100)
# for randomizing the speed of the Mob
self.speedy = random.randrange(5, 20)
## randomize the movements a little more
self.speedx = random.randrange(-3, 3)
## adding rotation to the mob element
self.rotation = 0
self.rotation_speed = random.randrange(-8, 8)
# time when the rotation has to happen
self.last_update = pygame.time.get_ticks()
def rotate(self):
time_now = pygame.time.get_ticks()
if time_now - self.last_update > 50: # in milliseconds
self.last_update = time_now
self.rotation = (self.rotation + self.rotation_speed) % 360
new_image = pygame.transform.rotate(self.image_orig, self.rotation)
old_center = self.rect.center
self.image = new_image
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
self.rotate()
self.rect.x += self.speedx
self.rect.y += self.speedy
## now what if the mob element goes out of the screen
if (self.rect.top > HEIGHT + 10) or (self.rect.left < -25) or (self.rect.right > WIDTH + 20):
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
# for randomizing the speed of the Mob
self.speedy = random.randrange(1, 8)
## defines the sprite for Powerups
class Pow(pygame.sprite.Sprite):
def __init__(self, center):
pygame.sprite.Sprite.__init__(self)
self.type = random.choice(['shield', 'gun'])
self.image = powerup_images[self.type]
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
## place the bullet according to the current position of the player
self.rect.center = center
self.speedy = 2
def update(self):
"""should spawn right in front of the player"""
self.rect.y += self.speedy
## kill the sprite after it moves over the top border
if self.rect.top > HEIGHT:
self.kill()
## defines the sprite for bullets
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = bullet_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
## place the bullet according to the current position of the player
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
"""should spawn right in front of the player"""
self.rect.y += self.speedy
## kill the sprite after it moves over the top border
if self.rect.bottom < 0:
self.kill()
## now we need a way to shoot
## lets bind it to "spacebar".
## adding an event for it in Game loop
## FIRE ZE MISSILES
class Missile(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = missile_img
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.rect.bottom = y
self.rect.centerx = x
self.speedy = -10
def update(self):
"""should spawn right in front of the player"""
self.rect.y += self.speedy
if self.rect.bottom < 0:
self.kill()
###################################################
## Load all game images
background = pygame.image.load(path.join(img_dir, 'starfield.png')).convert()
background_rect = background.get_rect()
## ^^ draw this rect first
player_img = pygame.image.load(
path.join(img_dir, 'playerShip1_orange.png')).convert()
player_mini_img = pygame.transform.scale(player_img, (25, 19))
player_mini_img.set_colorkey(BLACK)
bullet_img = pygame.image.load(path.join(img_dir, 'laserRed16.png')).convert()
missile_img = pygame.image.load(
path.join(img_dir, 'missile.png')).convert_alpha()
# meteor_img = pygame.image.load(path.join(img_dir, 'meteorBrown_med1.png')).convert()
meteor_images = []
meteor_list = [
'meteorBrown_big1.png',
'meteorBrown_big2.png',
'meteorBrown_med1.png',
'meteorBrown_med3.png',
'meteorBrown_small1.png',
'meteorBrown_small2.png',
'meteorBrown_tiny1.png'
]
for image in meteor_list:
meteor_images.append(pygame.image.load(
path.join(img_dir, image)).convert())
## meteor explosion
explosion_anim = {}
explosion_anim['lg'] = []
explosion_anim['sm'] = []
explosion_anim['player'] = []
for i in range(9):
filename = 'regularExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
## resize the explosion
img_lg = pygame.transform.scale(img, (75, 75))
explosion_anim['lg'].append(img_lg)
img_sm = pygame.transform.scale(img, (32, 32))
explosion_anim['sm'].append(img_sm)
## player explosion
filename = 'sonicExplosion0{}.png'.format(i)
img = pygame.image.load(path.join(img_dir, filename)).convert()
img.set_colorkey(BLACK)
explosion_anim['player'].append(img)
## load power ups
powerup_images = {}
powerup_images['shield'] = pygame.image.load(
path.join(img_dir, 'shield_gold.png')).convert()
powerup_images['gun'] = pygame.image.load(
path.join(img_dir, 'bolt_gold.png')).convert()
###################################################
###################################################
### Load all game sounds
shooting_sound = pygame.mixer.Sound(path.join(sound_folder, 'pew.wav'))
missile_sound = pygame.mixer.Sound(path.join(sound_folder, 'rocket.ogg'))
expl_sounds = []
for sound in ['expl3.wav', 'expl6.wav']:
expl_sounds.append(pygame.mixer.Sound(path.join(sound_folder, sound)))
## main background music
#pygame.mixer.music.load(path.join(sound_folder, 'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
pygame.mixer.music.set_volume(0.2) # simmered the sound down a little
player_die_sound = pygame.mixer.Sound(path.join(sound_folder, 'rumble1.ogg'))
###################################################
## TODO: make the game music loop over again and again. play(loops=-1) is not working
# Error :
# TypeError: play() takes no keyword arguments
#pygame.mixer.music.play()
#############################
## Game loop
running = True
menu_display = True
while running:
if menu_display:
main_menu()
pygame.time.wait(3000)
#Stop menu music
pygame.mixer.music.stop()
#Play the gameplay music
pygame.mixer.music.load(
path.join(sound_folder, 'tgfcoder-FrozenJam-SeamlessLoop.ogg'))
# makes the gameplay sound in an endless loop
pygame.mixer.music.play(-1)
menu_display = False
## group all the sprites together for ease of update
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
## spawn a group of mob
mobs = pygame.sprite.Group()
for i in range(8): # 8 mobs
# mob_element = Mob()
# all_sprites.add(mob_element)
# mobs.add(mob_element)
newmob()
## group for bullets
bullets = pygame.sprite.Group()
powerups = pygame.sprite.Group()
#### Score board variable
score = 0
#1 Process input/events
clock.tick(FPS) # will make the loop run at the same speed all the time
# gets all the events which have occured till now and keeps tab of them.
for event in pygame.event.get():
## listening for the the X button at the top
if event.type == pygame.QUIT:
running = False
## Press ESC to exit game
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# ## event for shooting the bullets
# elif event.type == pygame.KEYDOWN:
# if event.key == pygame.K_SPACE:
# player.shoot() ## we have to define the shoot() function
#2 Update
all_sprites.update()
## check if a bullet hit a mob
## now we have a group of bullets and a group of mob
hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
## now as we delete the mob element when we hit one with a bullet, we need to respawn them again
## as there will be no mob_elements left out
for hit in hits:
score += 50 - hit.radius # give different scores for hitting big and small metoers
random.choice(expl_sounds).play()
# m = Mob()
# all_sprites.add(m)
# mobs.add(m)
expl = Explosion(hit.rect.center, 'lg')
all_sprites.add(expl)
if random.random() > 0.9:
pow = Pow(hit.rect.center)
all_sprites.add(pow)
powerups.add(pow)
newmob() # spawn a new mob
## ^^ the above loop will create the amount of mob objects which were killed spawn again
#########################
## check if the player collides with the mob
# gives back a list, True makes the mob element disappear
hits = pygame.sprite.spritecollide(
player, mobs, True, pygame.sprite.collide_circle)
for hit in hits:
player.shield -= hit.radius * 2
expl = Explosion(hit.rect.center, 'sm')
all_sprites.add(expl)
newmob()
if player.shield <= 0:
player_die_sound.play()
death_explosion = Explosion(player.rect.center, 'player')
all_sprites.add(death_explosion)
# running = False ## GAME OVER 3:D
player.hide()
player.lives -= 1
player.shield = 100
## if the player hit a power up
hits = pygame.sprite.spritecollide(player, powerups, True)
for hit in hits:
if hit.type == 'shield':
player.shield += random.randrange(10, 30)
if player.shield >= 100:
player.shield = 100
if hit.type == 'gun':
player.powerup()
## if player died and the explosion has finished, end game
if player.lives == 0 and not death_explosion.alive():
running = False
# menu_display = True
# pygame.display.update()
#3 Draw/render
screen.fill(BLACK)
## draw the stargaze.png image
screen.blit(background, background_rect)
all_sprites.draw(screen)
# 10px down from the screen
draw_text(screen, str(score), 18, WIDTH / 2, 10)
draw_shield_bar(screen, 5, 5, player.shield)
# Draw lives
draw_lives(screen, WIDTH - 100, 5, player.lives, player_mini_img)
## Done after drawing everything to the screen
pygame.display.flip()
pygame.quit()
Conclusion:
Though all the instructions are given easily but to avoid any issue while running the Dino Run game following the instruction carefully. If any issue occurs contact us by emailing us. Also, let us know whether this blog helps you or not in the comment section below. Thank you!
Love it. I can’t see any kind of license to use your assets, please let me know if I can use and which licence. I am coding a spaceshooter game and I love how some assets looks in my game. Thank you for your code
Yes my friend you can use it
can i use this for my “congressional app challenge”? or is it licensed by you.
the download resources button working
Nailed it, all I had to do was create two folders ones named assets and one named sounds get them in the same place as the code and bingo it works, amazing, thanks again!