Been a while since I've been here and posted anything so, here is a little game that I've been playing around with.
Works on windows 10. I've not had a chance to modify to work on linux. You can download the files from github
Shmup. Uses pygame 1.9.6 and tkinter
Here is the coding.
Works on windows 10. I've not had a chance to modify to work on linux. You can download the files from github
Shmup. Uses pygame 1.9.6 and tkinter
Here is the coding.
# Import needed modules import pygame import os import random as rnd import tkinter as tk from functools import partial try: import pygame.freetype as freetype except ImportError: print ("No FreeType support compiled") sys.exit () # Initialize pygame and the sound mixer pygame.init() if pygame.get_sdl_version()[0] == 2: pygame.mixer.pre_init(44100, 32, 2, 1024) pygame.mixer.init() # Screen and framerate setup width = 800 height = 600 fps = 60 screen = pygame.display.set_mode((width, height)) pygame.display.set_caption('Shoot \'m Up!') clock = pygame.time.Clock() # Load font ttf file fontdir = os.path.dirname(os.path.abspath (__file__)) # font = freetype.Font(os.path.join (fontdir, "fonts", "comicbd.ttf")) # Set some colors note: all are not used black = (0, 0, 0) white = (255, 255, 255) palewhite = (240, 240, 240) grayish = (195, 195, 190) tomato = (250, 80, 100) skyblue = (0, 180, 255) green = (0, 255, 0) red = (255, 0, 0) yellow = (255, 255, 0) orange = (250, 80, 0) purple = (255, 0, 255) peru = (205, 133, 63) limegreen = (50, 205, 50) yellowgreen = (154, 205, 50) # Set the path for the image, music and sound directories imgdir = os.path.join(os.path.dirname(__file__), 'images') snddir = os.path.join(os.path.dirname(__file__), 'sounds') musicdir = os.path.join(os.path.dirname(__file__), 'music') # Set the background image background = pygame.image.load(f'{imgdir}/backgrounds/space.png') background_rect = background.get_rect() # Load all images used by the player player_img = pygame.image.load(f'{imgdir}/ships/myship.png').convert() mini_img = pygame.transform.scale(player_img, (25, 25)) mini_img.set_colorkey(black) shipbmp = pygame.image.load(f'{imgdir}/icons/myship.bmp') icon = pygame.transform.scale(shipbmp, (32, 32)) pygame.display.set_icon(icon) pygame.mouse.set_visible(0) # Load the missile image missile_img = pygame.image.load(f'{imgdir}/ammo/missile.png').convert() # Load shield images shield_100 = pygame.image.load(f'{imgdir}/shield/shield_100.png').convert() shield_75 = pygame.image.load(f'{imgdir}/shield/shield_75.png').convert() shield_50 = pygame.image.load(f'{imgdir}/shield/shield_50.png').convert() shield_25 = pygame.image.load(f'{imgdir}/shield/shield_25.png').convert() # Load powerup images power_imgs = {} power_imgs['shield_img'] = pygame.image.load(f'{imgdir}/powerups/shield.png').convert() power_imgs['full_life_img'] = pygame.image.load(f'{imgdir}/powerups/full_life.png').convert() power_imgs['partial_life_img'] = pygame.image.load(f'{imgdir}/powerups/partial_life.png').convert() # Load sound effects shoot_sound = pygame.mixer.Sound(f'{snddir}/rocket_fire.wav') enemy_explode = pygame.mixer.Sound(f'{snddir}/explosion.wav') player_hit = pygame.mixer.Sound(os.path.join(snddir, "myexplosion.wav")) player_die = pygame.mixer.Sound(os.path.join(snddir, 'player_die.wav')) shield_up = pygame.mixer.Sound(os.path.join(snddir, 'shield_up.wav')) partial_heal = pygame.mixer.Sound(os.path.join(snddir, 'partial_heal.wav')) full_heal = pygame.mixer.Sound(os.path.join(snddir, 'full_heal.wav')) # Explosion animations containers explosion_animation = {} explosion_animation['lg'] = [] explosion_animation['sm'] = [] explosion_animation['player'] = [] # Load explosion images for animation for i in range(90): filename = f'explosion1_{i}.png' img = pygame.image.load(os.path.join(f'{imgdir}/animation/enemy_explosion', filename)) img.set_colorkey(black) img_lg = pygame.transform.scale(img, (75, 75)) explosion_animation['lg'].append(img_lg) img_sm = pygame.transform.scale(img, (32, 32)) explosion_animation['sm'].append(img_sm) for i in range(9): filename = f'explosion_{i}.png' img = pygame.image.load(os.path.join(f'{imgdir}/animation/player_explosion', filename)) img.set_colorkey(black) explosion_animation['player'].append(img) # Number of enemy ships to display on the screen how_many_enemy_ships = 10 # Function for displaying text on the screen def draw_text(surface, xpos, ypos, text, color, size, myfont, style): font = freetype.Font(os.path.join(fontdir, 'fonts', myfont)) return font.render_to(surface, (round(xpos), round(ypos)), text, color, size=size, style=style) # Class for the background music. class BgMusic: def __init__(self): music_list = os.listdir(musicdir) file = rnd.choice(music_list) music = pygame.mixer.Sound(f'{musicdir}/{file}') music.play(-1) # Class for the explosion effects class Explosion(pygame.sprite.Sprite): def __init__(self, center, size): pygame.sprite.Sprite.__init__(self) self.size = size self.image = explosion_animation[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 = 20 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_animation[self.size]): self.kill() else: center = self.rect.center self.image = explosion_animation[self.size][self.frame] self.rect = self.image.get_rect() self.rect.center = center # Class for displaying various information class Display: def __init__(self): pass def rotate_image(self, rotate, x, y, ship): img = pygame.image.load(os.path.join(imgdir, ship)).convert() img = pygame.transform.scale(img, (64, 64)) img = pygame.transform.rotate(img, rotate) img.set_colorkey(black) screen.blit(img, (x, y)) # Define the title screen def title_screen(self): # Set the background screen.blit(background, background_rect) # Background music BgMusic() # Draw text to the screen draw_text(screen, 225, 15, 'Shmup!', purple, 100, 'comicbd.ttf', freetype.STYLE_NORMAL) draw_text(screen, 230, 130, 'Beta Version 0.04', grayish, 15, 'comic.ttf', freetype.STYLE_NORMAL) draw_text(screen, 410, 130, 'Powered by Pygame 1.9.6', \ green, 15, 'comic.ttf', freetype.STYLE_NORMAL|freetype.STYLE_OBLIQUE) draw_text(screen, 225, 180, 'Top Five High Scores', red, 30, 'comicbd.ttf', freetype.STYLE_UNDERLINE) # Initialize the Scores class scores = Scores() # Check for a score text file scores.check_for_score_file() # Display the top five scores i = 0 n = 1 for score in scores.get_scores(5): draw_text(screen, 225, 240+i, f'{n}. {score[1].upper()}', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) draw_text(screen, 425, 240+i, f'{score[0]}', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) i += 35 n += 1 # Draw game contols on the title screen draw_text(screen, 225, 430, f'Press Spacebar to Start', yellow, 25, 'comic.ttf', freetype.STYLE_NORMAL) draw_text(screen, 220, 460, f'Arrow or A and D keys to move, spacebar to fire.', yellow, 20, 'comic.ttf', freetype.STYLE_NORMAL) # Draw power icons and text screen.blit(power_imgs['shield_img'], (50,height-100)) draw_text(screen, 100, height-90, f'Gives a shield', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) screen.blit(power_imgs['full_life_img'], (260, height-100)) draw_text(screen, 310,height-90, f'Restores Full Life', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) screen.blit(power_imgs['partial_life_img'], (500, height-100)) draw_text(screen, 550, height-90, f'Restores Partial Life', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) # The footer draw_text(screen, 280, height-30, \ f'GamingRat Productions 2020 {chr(169)}', \ peru, 15, 'comic.ttf', freetype.STYLE_NORMAL|freetype.STYLE_OBLIQUE) img = pygame.image.load(os.path.join(f'{imgdir}/icons', 'ratt2b.bmp')) img.set_colorkey(black) img = pygame.transform.scale(img, (50, 50)) screen.blit(img, (220, height-50)) # The two ships on the title screen ships = os.listdir(f'{imgdir}/ships') self.rotate_image(-130, 25, 25, f'ships/{rnd.choice(ships)}') self.rotate_image(130, 675, 25, f'ships/{rnd.choice(ships)}') pygame.display.flip() # Keeps player on the title screen until the spacebar is pressed waiting = True while waiting: clock.tick(fps) try: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() keystate = pygame.key.get_pressed() if keystate[pygame.K_SPACE]: waiting = False except pygame.error: break # define the lives bar. The three small ships on the right top def lives_bar(self, surface, x, y, lives, img): for i in range(lives): img_rect = img.get_rect() img_rect.x = x + 30 * i img_rect.y = y surface.blit(img, img_rect) # Define the life and shield bar. # Changes color depending on hits taken def status_bar(self, surface, x, y, obj): length = 100 height = 15 fill = (obj/length) * length if fill >= 75: color = green elif fill < 75 and fill >= 60: color = yellow elif fill < 60 and fill >= 30: color = orange elif fill <= 0: color = black else: color = red fill_rect = pygame.Rect(round(x), round(y), round(fill), round(height)) pygame.draw.rect(surface, color, fill_rect) outline_rect = pygame.Rect(round(x), round(y), length, height) pygame.draw.rect(surface, palewhite, outline_rect, 1) # Define the class for the player class Player(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.image = pygame.transform.scale(player_img, (50, 50)) self.image.set_colorkey(black) self.rect = self.image.get_rect() self.radius = 25 self.rect.centerx = round(width / 2) self.rect.bottom = height - 10 self.speedx = 0 self.hidden = False self.life = 100 self.lives = 3 self.hide_timer = pygame.time.get_ticks() # Defines the player bullets def shoot(self, bullet_sprites): bullet = Bullets(self.rect.centerx, self.rect.top) bullet_sprites.add(bullet) shoot_sound.play() # Hides the player for a momment when destroyed def hide(self): self.hidden = True self.hide_timer = pygame.time.get_ticks() self.rect.center = (round(width / 2), height + 200) # Restores player after being destroyed and handles player movement. def update(self): if self.hidden and pygame.time.get_ticks() - self.hide_timer > 1000: self.hidden = False self.rect.centerx = round(width / 2) self.rect.bottom = height - 10 self.speedx = 0 keystate = pygame.key.get_pressed() if keystate[pygame.K_LEFT] or keystate[pygame.K_a]: self.speedx = -8 if keystate[pygame.K_RIGHT] or keystate[pygame.K_d]: self.speedx = 8 self.rect.x += self.speedx if self.rect.right > width - 15: self.rect.right = width - 15 if self.rect.left < 15: self.rect.left = 15 # Class for defining the bullets/missile beng shot class Bullets(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.image = pygame.transform.scale(missile_img, (18, 32)) self.image.set_colorkey(black) self.rect = self.image.get_rect() self.rect.bottom = y + 10 self.rect.centerx = x self.speedy = -10 # Update bullet movement def update(self): self.rect.y += self.speedy # Kill it if it moves off the top of the screen if self.rect.bottom < 0: self.kill() # Class for defining the shield class Shield(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.shield_img = shield_100 self.image = pygame.transform.scale(self.shield_img, (80, 50)) self.image.set_colorkey(black) self.rect = self.image.get_rect() self.radius = 30 self.rect.centerx = round(width/2) self.rect.top = height + 50 self.strength = 0 self.hidden = True # Defines the shield def raise_shield(self, x, y, sprite): shield = self sprite.add(shield) shield.rect.centerx = x shield.rect.top = y - 20 # Defines shield movement and kills it when not on use def update(self): if self.hidden: self.kill() self.speedx = 0 keystate = pygame.key.get_pressed() if keystate[pygame.K_LEFT] or keystate[pygame.K_a]: self.speedx = -8 if keystate[pygame.K_RIGHT] or keystate[pygame.K_d]: self.speedx = 8 self.rect.x += self.speedx if self.rect.right > width: self.rect.right = width if self.rect.left < 0: self.rect.left = 0 # Class for defining mob attributes class Mob(pygame.sprite.Sprite): def __init__(self): excludes = ['myship.png', 'myship2.png'] pygame.sprite.Sprite.__init__(self) ships = os.listdir(f'{imgdir}/ships') enemy_ships = [] for ship in ships: if ship not in excludes: enemy_ships.append(pygame.image.load(os.path.join(f'{imgdir}/ships', ship))) self.image_orig = pygame.transform.scale(rnd.choice(enemy_ships), (rnd.randint(20, 40), rnd.randint(20, 40))) self.image_orig.set_colorkey(black) self.image = self.image_orig.copy() self.rect = self.image.get_rect() self.radius = int(self.rect.width * .80 / 2) self.rect.x = rnd.randrange(width - self.rect.width) self.rect.y = rnd.randrange(-100, -40) self.speedy = rnd.randrange(1, 5) self.speedx = rnd.randrange(-3, 3) # Can use this to rorate objects self.rot = 0 self.rot_speed = rnd.randrange(-8, 8) self.last_update = pygame.time.get_ticks() # Creates mobs def newmob(self, mob_sprites): mob = self mob_sprites.add(mob) # Update mob movement and placement def update(self): self.rect.x += self.speedx self.rect.y += self.speedy if self.rect.top > height + 10 or self.rect.left < -25 or self.rect.right > width + 25: self.rect.x = rnd.randrange(width - self.rect.width) self.rect.y = rnd.randrange(-100, -40) self.speedy = rnd.randrange(1, 8) self.speedx = rnd.randrange(-3, 3) # Class for the powerups class PowerUps(pygame.sprite.Sprite): def __init__(self, center): pygame.sprite.Sprite.__init__(self) self.type = rnd.choice(['shield_img', 'full_life_img', 'partial_life_img']) self.image = power_imgs[self.type] self.image.set_colorkey(black) self.rect = self.image.get_rect() self.rect.center = center self.speedy = 2 def update(self): self.rect.y += self.speedy # Kill it if it moves off the bottom of the screen if self.rect.top > height: self.kill() # Class for handling writing to the scores.txt file and also reads # the file for displaying the scores on the title screen class Scores: def __init__(self): # Set the text file for writing scores self.score_file = 'scores.txt' # Write scores and name to text file # If the file contains more than 25 lines, the lowest score is deleted. def write_score(self, name, score): with open(self.score_file, 'a')as data: data.write(f'{name[0:3]}:{score}\n') self.get_scores(25) # Display scores def get_scores(self, howmany): mydict = {} with open(self.score_file, 'r') as lines: for line in lines: line = line.split(':') mydict[int(line[1].strip())] = line[0].strip() sorted_dict = sorted(mydict.items(), reverse=True) while len(sorted_dict) > howmany: sorted_dict.pop() return sorted_dict # Check if file exists. If not create it. def check_for_score_file(self): if os.path.isfile(self.score_file): pass else: with open(self.score_file, 'w') as scores: pass # tkinter form for getting player intials def form(self, score): self.root = tk.Tk() self.root.geometry('347x124') self.root.resizable(width=False, height=False) self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) img = tk.PhotoImage(file='images/backgrounds/bg2b.png') img.img = img imlabel = tk.Label(self.root, image=img) imlabel.pack(expand=1, fill='both') self.entry = tk.Entry(imlabel) self.entry.focus() self.entry['bg'] = '#555' self.entry['fg'] = 'whitesmoke' self.entry.place(x=140, y=8) btn = tk.Button(imlabel, text='Submit') btn['fg'] = '#fff' btn['bg'] = '#111' btn['activebackground'] = '#222' btn['activeforeground'] = 'tomato' btn['cursor'] = 'hand2' btn['command'] = partial(self.callback, score) btn.place(x=230, y=60) self.root.bind('<Return>', partial(self.callback, score)) self.root.bind('<KP_Enter>', partial(self.callback, score)) self.root.mainloop() # Callback for writing initials and score to text file def callback(self, score, event=None): name = self.entry.get() self.write_score(name, score) self.root.destroy() # Class for defining all the sprite collisions class Hit: def __init__(self): pass # define the method def hit(self, kwargs): # Change kwargs to simple name variables player = kwargs['player'] shield = kwargs['shield'] shield_sprites = kwargs['shield_sprites'] bullet_sprites = kwargs['bullet_sprites'] player_sprites = kwargs['player_sprites'] mob_sprites = kwargs['mob_sprites'] score = kwargs['score'] power_sprites = kwargs['power_sprites'] explosion_sprites = kwargs['explosion_sprites'] # Mob hits player # Set to false so player does not die in one hit # If player life reaches zero, destroy the ship ship_destroy = False if player.life <= 0: ship_destroy = True # Get all the sprite collisions and loop through for needed information player_hits = pygame.sprite.groupcollide(mob_sprites, player_sprites, True, ship_destroy) for hit in player_hits: # Play the explosion sound and image files player_hit.play() explosion = Explosion(hit.rect.center, 'sm') explosion_sprites.add(explosion) # Create a new mob to replace the one destroyed mob = Mob() mob.newmob(mob_sprites) mob_sprites.add(mob) # Player was destroyed, play sound and animation files # Hide the player for a momment, subtract one life # Restore life bar to full player.life -= hit.radius if player.life <= 0: player_die.play() global death_explosion death_explosion = Explosion(hit.rect.center, 'player') explosion_sprites.add(death_explosion) player.hide() player. lives -= 1 player.life = 100 # Player shoots mob # Same as above. Get loop through sprite information # Player the sound and animation files # Add score to player for destroyed mobs # Replace destroyed mobs hits = pygame.sprite.groupcollide(mob_sprites, bullet_sprites, True, True) for hit in hits: enemy_explode.play() enemy_explosion = Explosion(hit.rect.center, 'lg') explosion_sprites.add(enemy_explosion) score += (20 - hit.radius) mob = Mob() mob.newmob(mob_sprites) mob_sprites.add(mob) # Random drop of power icons if rnd.random() > 0.9: power = PowerUps(hit.rect.center) power_sprites.add(power) # Power icons fall and hit player # Same as above. powerhits = pygame.sprite.spritecollide(player, power_sprites, True) for hit in powerhits: # If the shield hits player, give the shield if hit.type == 'shield_img': shield_up.play() shield.strength = 100 shield.hidden = False shield.raise_shield(player.rect.centerx, player.rect.top, shield_sprites) # If partial life icon hits player, restore part of player life # based on mob size. If the player has full life, it's added to # the score instead elif hit.type == 'partial_life_img': partial_heal.play() if player.life == 100: score += player.life * 0.25 else: player.life += player.life * 0.25 if player.life > 100: player.life = 100 # Sames as the partial life but restores full life instead # and gives 50 points to player score elif hit.type == 'full_life_img': full_heal.play() if player.life == 100: score += 50 else: player.life = 100 # Mob hits shield # Same as above, collects the sprite collision information # Plays the sound and animation files shield_hits = pygame.sprite.groupcollide(mob_sprites, shield_sprites, True, False) for hit in shield_hits: player_hit.play() explosion = Explosion(hit.rect.center, 'sm') explosion_sprites.add(explosion) # Create new mob to replace the one destroyed mob = Mob() mob.newmob(mob_sprites) mob_sprites.add(mob) # Subtract from the shield. If it reaches zero, hide and destroy shield.strength -= 20 if shield.strength <= 0: shield.strength = 0 shield.hidden = True # Return the score for displaying on the screen return round(score) # Define the main program def main(): # Initialize the Display class and set some variables status = Display() game_over = True running = True while running: # Not playing the game, stay on the title screen # Reset the score to zero if game_over: status.title_screen() game_over = False score = 0 # Initialize player and shield sprite groups # Initialize player and shield class player_sprites = pygame.sprite.Group() player = Player() player_sprites.add(player) shield_sprites = pygame.sprite.Group() shield = Shield() # Initialize the rest of the sprite groups power_sprites = pygame.sprite.Group() mob_sprites = pygame.sprite.Group() bullet_sprites = pygame.sprite.Group() explosion_sprites = pygame.sprite.Group() # Put the mobs on the screen for i in range(how_many_enemy_ships): mob = Mob() mob.newmob(mob_sprites) mob_sprites.add(mob) # Try except to prevent the console error when exiting game try: # Loop through pygame events for event in pygame.event.get(): # Exit game if event.type == pygame.QUIT: running = False # Spacebar pressed, play game elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: player.shoot(bullet_sprites) except pygame.error: break # Set some dict variables needed in the Hits class kwargs = {} kwargs['player'] = player kwargs['shield'] = shield kwargs['player_sprites'] = player_sprites kwargs['mob'] = mob kwargs['bullet_sprites'] = bullet_sprites kwargs['mob_sprites'] = mob_sprites kwargs['power_sprites'] = power_sprites kwargs['shield_sprites'] = shield_sprites kwargs['explosion_sprites'] = explosion_sprites kwargs['score'] = score # Initialize the Hit class. Put in a variable to get the score # for display score = Hit().hit(kwargs) # Update all sprites player_sprites.update() mob_sprites.update() shield_sprites.update() bullet_sprites.update() power_sprites.update() explosion_sprites.update() # Player lost last life, write score to text file if player.lives == 0 and not death_explosion.alive(): game_over = True write_score = Scores() write_score.form(score) # Fill the pygame screen background with color and image screen.fill(black) screen.blit(background, background_rect) # Draw all sprites to the screen player_sprites.draw(screen) mob_sprites.draw(screen) shield_sprites.draw(screen) bullet_sprites.draw(screen) power_sprites.draw(screen) explosion_sprites.draw(screen) # Draw all bars and text to the screen draw_text(screen, 18, 15, f'Life', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) status.status_bar(screen, 90, 15, player.life) draw_text(screen, 18, 45, f'Shield', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) status.status_bar(screen, 90, 45, shield.strength) draw_text(screen, width/2-20, 15, f'Score: {score}', palewhite, 20, 'comic.ttf', freetype.STYLE_NORMAL) status.lives_bar(screen, width - 95, 15, player.lives, mini_img) pygame.display.flip() clock.tick(fps) if __name__ == '__main__': main()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts