使用pygame开发一个弹幕射击游戏(一)
本文作为开发过程记录用。
目前游戏画面:
下一个添加的功能:敌机可以进行射击。
弹幕类
from pygame.sprite import Sprite from pygame import transform import math class Barrage(Sprite): def __init__(self,raiden_settings,screen,ship,enemy,angle=0): self.barrage_type = enemy.barrage_type self.screen = screen self.raiden_settings = raiden_settings self.ship = ship self.image = pygame.image.load(raiden_settings. \ barrage_image[barrage_type-1]) self.rect = self.image.get_rect() self.rect.x = enemy.rect.x self.rect.y = enemy.rect.y self.x = float(self.rect.x) self.y = float(self.rect.y) self.angle = angle if self.barrage_type == 1: self.image = pygame.transform.rotate(self.image, raiden_settings.direction_angle) def update(self): if self.barrage_type == 1 or self.barrage_type == 2: self.x += self.raiden_settings.barrage_speed_factor * \ math.sin(self.angle) self.y += self.raiden_settings.barrage_speed_factor * \ math.cos(self.angle) if self.barrage_type == 3: tmp = ((self.x - self.ship.x) ** 2 + (self.y -self.ship.y) \ ** 2) ** 0.5 self.x += self.raiden_settings.barrage_speed_factor * \ (self.ship.x - self.x)/tmp self.y += self.raiden_settings.barrage_speed_factor * \ (self.y - self.ship.y)/tmp self.rect.x = self.x self.rect.y = self.y
子弹类
import pygame from pygame.sprite import Sprite import math from pygame import transform class Bullet(Sprite): def __init__(self,raiden_settings,screen,ship,angle): super().__init__() self.screen = screen self.image = pygame.image.load(raiden_settings.bullet_image) self.image = pygame.transform.rotate(self.image,angle) self.rect = self.image.get_rect() self.rect.centerx = ship.rect.centerx self.rect.top = ship.rect.top self.y = float(self.rect.y) self.x = float(self.rect.x) self.angle = angle self.speed_factor = raiden_settings.bullet_speed_factor def update(self): self.y -=self.speed_factor * math.cos(self.angle) self.x +=self.speed_factor * math.sin(self.angle) self.rect.y = self.y self.rect.x = self.x def draw_bullet(self): pygame.draw.rect(self.screen,self.color,self.rect)
按钮类
import pygame.font class Button(): def __init__(self,raiden_settings,screen,msg): self.screen = screen self.screen_rect = screen.get_rect() self.width = raiden_settings.button_width self.height = raiden_settings.button_height self.button_color = raiden_settings.button_color self.text_color = raiden_settings.button_text_color self.font = pygame.font.SysFont(None, raiden_settings.button_font_size) self.rect = pygame.Rect(0,0,self.width,self.height) self.rect.center = self.screen_rect.center self.prep_msg(msg) def prep_msg(self,msg): self.msg_image = self.font.render(msg,True,self.text_color, self.button_color) self.msg_image_rect = self.msg_image.get_rect() self.msg_image_rect.center = self.rect.center def draw_button(self): self.screen.fill(self.button_color,self.rect) self.screen.blit(self.msg_image,self.msg_image_rect)
掉落物类
import pygame from pygame.sprite import Sprite class DropItem(Sprite): def __init__(self,raiden_settings,screen,enemy,ship): super().__init__() self.screen = screen self.raiden_settings = raiden_settings self.image = pygame.image.load( \ raiden_settings.dropitem_image[ship.arms_level - 1]). \ convert_alpha() self.rect = self.image.get_rect() self.rect.centerx = enemy.rect.centerx self.rect.top = enemy.rect.bottom self.y = self.rect.y self.speed_factor = raiden_settings.dropitem_speed_factor self.level = ship.arms_level + 1 def update(self): self.y +=self.speed_factor self.rect.y = self.y def draw_dropitem(self): self.screen.blit(self.image,self.rect)
敌机类
import pygame from pygame.sprite import Sprite from pygame import transform from random import randint class Enemy(Sprite): def __init__(self,raiden_settings,screen): super().__init__() self.screen = screen self.raiden_settings = raiden_settings self.enemy_type = randint(1,3) self.image = pygame.image.load(raiden_settings. \ enemy_image[self.enemy_type - 1]).convert_alpha() self.image = transform.smoothscale(self.image, raiden_settings.enemy_image_rect) self.image = pygame.transform.rotate(self.image, raiden_settings.direction_angle) self.rect = self.image.get_rect() self.x = float(self.rect.x) self.y = float(self.rect.y) self.barrage_type = self.enemy_type self.set_enemy_rect() def blitme(self): self.screen.blit(self.image,self.rect) def update(self): # ~ self.x += (self.raiden_settings.enemy_speed_factor * # ~ self.raiden_settings.fleet_direction) # ~ self.rect.x = self.x if self.enemy_type == 1: self.y += self.raiden_settings.enemy_speed_factor if self.enemy_type == 2: if self.x < 100 : self.x += self.raiden_settings.enemy_speed_factor elif self.x >self.screen.get_rect().right - 100: self.x -= self.raiden_settings.enemy_speed_factor if self.enemy_type == 3: if self.x <= self.screen.get_rect().right: self.x += self.raiden_settings.enemy_speed_factor else: self.x -= self.raiden_settings.enemy_speed_factor self.y += self.raiden_settings.enemy_speed_factor self.rect.x = self.x self.rect.y = self.y def check_edges(self): screen_rect = self.screen.get_rect() if self.rect.right >= screen_rect.right: return True elif self.rect.left <= 0: return True def set_enemy_rect(self): if self.enemy_type == 1: self.x = randint(0,self.screen.get_rect().width) self.y = 0 elif self.enemy_type == 2: self.y = 200 if randint(1,2) == 1: self.x = 0 else: self.x = self.screen.get_rect().right elif self.enemy_type == 3: self.y = randint(0,300) if randint(1,2) == 1: self.x = 0 else: self.x = self.screen.get_rect().right self.rect.x = self.x self.rect.y = self.y
游戏内功能函数类
import sys import pygame from Bullet import Bullet from Enemy import Enemy from time import sleep from dropitem import DropItem from random import random from barrage import Barrage #检查输入 def check_events(raiden_settings,screen,stats,ship,enemys,bullets, play_button,sprites): for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: check_keydown_event(event,raiden_settings,screen,ship, bullets,sprites) elif event.type == pygame.KEYUP: check_keyup_event(event,ship) elif event.type == pygame.MOUSEBUTTONDOWN: mouse_x,mouse_y = pygame.mouse.get_pos() check_play_button(raiden_settings,stats,screen,play_button, mouse_x,mouse_y) elif event.type == pygame.USEREVENT: create_enemy(raiden_settings,screen,enemys,sprites) #更新屏幕 def update_screen(screen,stats,play_button,sprites): if not stats.game_active: play_button.draw_button() else: updates = sprites.draw(screen) pygame.display.update(updates) pygame.display.flip() #检查按下输入 def check_keydown_event(event,raiden_settings,screen,ship,bullets, sprites): if event.key == pygame.K_d: ship.moving_right = True elif event.key == pygame.K_a: ship.moving_left = True elif event.key == pygame.K_SPACE: fire_bullet(raiden_settings,screen,ship,bullets,sprites) elif event.key == pygame.K_w: ship.moving_up = True elif event.key == pygame.K_s: ship.moving_down = True elif event.key == pygame.K_q: sys.exit() #检查放开输入 def check_keyup_event(event,ship): if event.key == pygame.K_d: ship.moving_right = False elif event.key == pygame.K_a: ship.moving_left = False elif event.key == pygame.K_w: ship.moving_up = False elif event.key == pygame.K_s: ship.moving_down = False #更新子弹 def update_bullets(raiden_settings,screen,stats,ship,enemys,bullets, dropitems,scoreboard,sprites): for bullet in bullets.copy(): if bullet.rect.bottom <=0 or bullet.rect.x <= 0 or \ bullet.rect.x >= raiden_settings.screen_width: bullets.remove(bullet) collisions = pygame.sprite.groupcollide(bullets,enemys,True,True) if collisions: enemy_hit(raiden_settings,screen,stats,ship,enemys,dropitems, scoreboard,collisions,sprites) #敌人被击中 def enemy_hit(raiden_settings,screen,stats,ship,enemys,dropitems, scoreboard,collisions,sprites): for bullet,enemy_list in collisions.items(): stats.score += raiden_settings.score_up_factor scoreboard.prep_score() scoreboard.show_score() if check_drop(raiden_settings): for enemy in enemy_list: new_dropitem = DropItem(raiden_settings,screen,enemy, ship) dropitems.add(new_dropitem) sprites.add(new_dropitem) #更新掉落物 def update_dropitems(raiden_settings,screen,stats,ship,dropitems, sprites): for dropitem in dropitems.copy(): if dropitem.rect.bottom <= 0: sprites.remove(dropitem) dropitems.remove(dropitem) collision = pygame.sprite.spritecollideany(ship,dropitems) if collision: sprites.remove(collision) dropitems.remove(collision) if collision.level == ship.arms_level + 1: ship.update_arms() #开火 def fire_bullet(raiden_settings,screen,ship,bullets,sprites): bullet_list = [] if ship.arms_level == raiden_settings.arms_1_level: bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.vertical_angle)) elif ship.arms_level == raiden_settings.arms_2_level: bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.left_deflectical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.right_deflectical_angle)) elif ship.arms_level == raiden_settings.arms_3_level: bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.vertical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.left_deflectical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.right_deflectical_angle)) elif ship.arms_level == raiden_settings.arms_4_level: bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.vertical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.left_deflectical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.right_deflectical_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.left_horizontal_angle)) bullet_list.append(Bullet(raiden_settings,screen,ship, raiden_settings.right_horizontal_angle)) for bullet in bullet_list: bullets.add(bullet) sprites.add(bullet) #创建敌人 def create_enemy(raiden_settings,screen,enemys,sprites): enemy = Enemy(raiden_settings,screen) enemys.add(enemy) sprites.add(enemy) def update_enemys(raiden_settings,stats,lifeboard,screen,ship,enemys, bullets,sprites): if pygame.sprite.spritecollideany(ship,enemys): ship_hit(raiden_settings,stats,lifeboard,screen,ship,enemys, bullets,sprites) #玩家被击中 def ship_hit(raiden_settings,stats,lifeboard,screen,ship,enemys, bullets,sprites): if stats.ships_left > 0: stats.ships_left -= 1 ship.reset_ship() lifeboard.dead(stats,sprites) reset_game(raiden_settings,screen,ship,enemys,bullets,sprites) sleep(1) else: stats.game_active = False pygame.mouse.set_visible(False) stats.ship_left = raiden_settings.ship_init_life stats.score = 0 stats.reset_stats() ship.reset_ship() reset_game(raiden_settings,screen,ship,enemys,bullets,sprites) pygame.mouse.set_visible(True) lifeboard.dead(stats,sprites) #重置游戏 def reset_game(raiden_settings,screen,ship,enemys,bullets,sprites): for enemy in enemys: sprites.remove(enemy) enemys.empty() for bullet in bullets: sprites.remove(bullet) bullets.empty() sprites.clear(screen,clear_callback) ship.center_ship() #检查玩家是否点击play按钮 def check_play_button(raiden_settings,stats,screen,play_button, mouse_x,mouse_y): if play_button.rect.collidepoint(mouse_x,mouse_y) and not \ stats.game_active: stats.game_active = True pygame.mouse.set_visible(False) screen.fill(raiden_settings.bg_color) #是否掉落物品 def check_drop(raiden_settings): if random() < raiden_settings.chance: return True else: return False #检查掉落物是否到达屏幕底 def check_dropitem_bottom(raiden_settings,stats,screen,dropitems): screen_rect = screen.get_rect() for dropitem in dropitems.sprite(): if dropitem.rect.bottom >= screen_rect.bottom: dropitems.remove(dropitem) #清除子图形 def clear_callback(surf, rect): bg = 255,255,255 surf.fill(bg, rect)
生命值记录板类
from pygame.sprite import Sprite from pygame.sprite import Group from Ship import Ship from pygame import transform class Lifeboard(Sprite): def __init__(self,raiden_settings,screen,stats,sprites): super().__init__() self.stats = stats self.screen = screen self.rect = screen.get_rect() self.raiden_settings = raiden_settings self.ships = Group() self.ship_list = [] self.prep_ships() for ship in self.ships: sprites.add(ship) ship.image = transform.smoothscale(ship.image, raiden_settings.lifeboard_image_rect) def prep_ships(self): for ship_number in range(self.stats.ships_left): ship = Ship(self.raiden_settings,self.screen) ship.center = self.raiden_settings.lifeboard_ship_center + \ ship_number * self.raiden_settings. \ lifeboard_ship_center_growth ship.bottom = self.raiden_settings.lifeboard_ship_bottom self.ships.add(ship) self.ship_list.append(ship) def dead(self,stats,sprites): self.ships.remove(self.ship_list[-1]) sprites.remove(self.ship_list[-1]) del self.ship_list[-1]
游戏类(主程序)
import sys import pygame from Settings import Settings from Ship import Ship import game_functions as gf from pygame.sprite import Group from Enemy import Enemy from GameStats import GameStats from Button import Button from Scoreboard import Scoreboard from Lifeboard import Lifeboard def run_game(): pygame.init() raiden_settings = Settings() screen = pygame.display.set_mode((raiden_settings.screen_width, raiden_settings.screen_height)) EVENT_CREATE_ENEMY = pygame.USEREVENT pygame.time.set_timer(EVENT_CREATE_ENEMY,300) pygame.display.set_caption("Raiden") play_button = Button(raiden_settings,screen,"Play!") ship = Ship(raiden_settings,screen) bullets = Group() enemys = Group() dropitems = Group() sprites = pygame.sprite.RenderUpdates() stats = GameStats(raiden_settings) scoreboard = Scoreboard(raiden_settings,screen,stats) lifeboard = Lifeboard(raiden_settings,screen,stats,sprites) screen.fill(raiden_settings.bg_color) sprites.add(ship) sprites.add(scoreboard) while True: gf.check_events(raiden_settings,screen,stats,ship,enemys, bullets,play_button,sprites) if stats.game_active: sprites.clear(screen,gf.clear_callback) sprites.update() gf.update_bullets(raiden_settings,screen,stats,ship,enemys, bullets,dropitems,scoreboard,sprites) gf.update_dropitems(raiden_settings,screen,stats,ship, dropitems,sprites) gf.update_enemys(raiden_settings,stats,lifeboard,screen, ship,enemys,bullets,sprites) gf.update_screen(screen,stats,play_button,sprites) run_game()
计分板类
import pygame.font from pygame.sprite import Sprite from pygame.sprite import Group from Ship import Ship class Scoreboard(Sprite): def __init__(self,raiden_settings,screen,stats): super().__init__() self.screen = screen self.screen_rect = screen.get_rect() self.raiden_settings = raiden_settings self.stats = stats self.text_color = raiden_settings.scoreboard_text_color self.font = pygame.font.SysFont(None, raiden_settings.scoreboard_font_size) self.prep_score() def prep_score(self): score_str = str(self.stats.score) self.image = self.font.render(score_str,True, self.text_color,self.raiden_settings.bg_color) self.rect = self.image.get_rect() self.rect.right = self.screen_rect.right self.rect.top = 0 def show_score(self): self.screen.blit(self.image,self.rect)
常数类
import math class Settings(): def __init__(self): #屏幕设置 self.screen_width = 300 self.screen_height = 600 self.bg_color = (255,255,255) #飞行速度设置 self.ship_speed_factor = 0.3 self.enemy_speed_factor = 0.3 self.fleet_drop_speed = 8 self.enemy_speed_up_factor = 0.1 self.bullet_speed_factor = 0.7 self.barrage_speed_factor = 0.7 #飞行方向设置 self.fleet_direction = 1 #生命值 self.ship_init_life = 3 #分数设置 self.score_up_factor = 10 #掉落物设置 self.dropitem_speed_factor = 0.1 #掉落指数设置 self.chance = 0.5 #武器等级设置 self.init_arms_level = 1 self.max_arms_level = 4 self.arms_1_level = 1 self.arms_2_level = 2 self.arms_3_level = 3 self.arms_4_level = 4 #角度 self.vertical_angle = 0 self.right_deflectical_angle = math.pi/6 self.left_deflectical_angle = -math.pi/6 self.left_horizontal_angle = -math.pi/2 self.right_horizontal_angle = math.pi/2 self.direction_angle = 180 self.angle_left_10 = -10 self.angle_right_10 = 10 #图片位置 self.enemy_image = [ 'image/enemy1.png', 'image/enemy2.png', 'image/enemy3.png', ] self.ship_image = 'image/9.png' self.bullet_image = 'image/bullet.png' self.dropitem_image = [ 'image/dropitem1.png', 'image/dropitem2.png', 'image/dropitem3.png', 'image/dropitem4.png', ] self.barrage_image = [ 'image\barrage1.png', 'image\barrage2.png', 'image\barrage3.png', ] #enemy图片大小 self.enemy_image_rect = (30,30) #lifeboard图片大小 self.lifeboard_image_rect = (30,30) #ship图片大小 self.ship_image_rect = (50,50) #计分板字体颜色 self.scoreboard_text_color = (30,30,30) #计分板字体大小 self.scoreboard_font_size = 48 #计分板位置 self.lifeboard_ship_center = 25 self.lifeboard_ship_bottom = 50 self.lifeboard_ship_center_growth = 15 #button设置 self.button_text_color = (255,255,255) self.button_color = (0,255,0) self.button_width = 200 self.button_height = 50 self.button_font_size = 48
飞船类
import pygame from Settings import Settings from pygame.sprite import Sprite from pygame import transform class Ship(Sprite): def __init__(self,raiden_settings,screen): super().__init__() self.screen = screen self.raiden_settings = raiden_settings self.image = pygame.image.load(raiden_settings.ship_image). \ convert_alpha() self.image = transform.smoothscale(self.image, raiden_settings.ship_image_rect) self.rect = self.image.get_rect() self.screen_rect = screen.get_rect() self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom self.arms_level = raiden_settings.init_arms_level self.center = float(self.rect.centerx) self.bottom = float(self.rect.bottom) self.moving_right = False self.moving_left = False self.moving_up = False self.moving_down = False def update(self): if self.moving_right and self.rect.right < \ self.screen_rect.right: self.center += self.raiden_settings.ship_speed_factor if self.moving_left and self.rect.left > 0: self.center -= self.raiden_settings.ship_speed_factor if self.moving_up and self.rect.top > 0: self.bottom -=self.raiden_settings.ship_speed_factor if self.moving_down and self.rect.bottom < \ self.screen_rect.bottom: self.bottom +=self.raiden_settings.ship_speed_factor self.rect.centerx = self.center self.rect.bottom = self.bottom def blitme(self): self.screen.blit(self.image,self.rect) def center_ship(self): self.center = self.screen_rect.centerx def update_arms(self): if self.arms_level < self.raiden_settings.max_arms_level: self.arms_level += 1 def reset_ship(self): self.rect.centerx = self.screen_rect.centerx self.rect.bottom = self.screen_rect.bottom self.center = self.screen_rect.centerx self.bottom = self.screen_rect.bottom self.arms_level = self.raiden_settings.init_arms_level
游戏内部记录类
class GameStats(): def __init__(self,raiden_settings): self.raiden_settings = raiden_settings self.reset_stats() self.game_active = False self.score = 0 def reset_stats(self): self.ships_left = self.raiden_settings.ship_init_life