用pygame制作飞机大战的游戏
这属于练手的代码,之前写过一个,但没有结构看得跟屎一样,不好添加功能,这次打算从零开始,一步一步写出便于开发与维护的代码。
至于pygame的一些基础知识,我都写到之前的文章里,可以看看。
虽然但是还是感觉自己写了一坨,就当个反面例子了。
完整代码放在gitte里了:https://gitee.com/mansujiaosheng/airplane
逆向好难好坐牢,写个游戏换一换换脑子。
拼尽全力,仍被ai评做低级代码。
一,创建文件
为了便于维护,我们需要在同一个文件下创建4个文件,3个文件夹。
4个文件:一个是主函数文件,用来运行游戏,我称为main,一个存放其他函数的文件,我称为set,一个存放类的文件,我称为class_set,一个存放配置的文件,我称为config_set,这些都是.py结尾。
3个文件夹:一个pic,存放图片,一个sound,存放声音,一个font,存放字体。
二,分析游戏
我打算这个游戏要有个菜单,要有记录分数,记录时间,要有己方飞机,要有敌方飞机,要有子弹
要通过键盘来控制移动。
菜单里要有按钮,所以要有按钮类,
记录时间要有时间类,
己方飞机,敌方飞机,子弹,都要有自己的类,
这些都是做前的分析,后面实现的可能不同。
三,初始化
这里我创建了一个游戏类,让我更好的去理清我代码。
这里有三个方法。
第一个是初始化,设置屏幕大小,颜色和标题。
第二个是处理事件,因为现在什么都没有,所以只处理退出事件。
第三个是游戏运行,运行时会处理哪些代码都放在这里。
最后主函数就创建个对象,然后使用run_game方法来运行。
import pygame,sys
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
pygame.init()
pygame.display.set_caption(CONFIG['capation'])
self.screen = pygame.display.set_mode(CONFIG['screen_size'])
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill(CONFIG['background_color'])
self.run = True
# 处理事件
def handle_event(self):
for event in pygame.event.get():
if event.type == QUIT:
self.run = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self.run = False
#游戏运行
def run_game(self):
while self.run:
self.handle_event()
self.screen.blit(self.background,(0,0))
pygame.display.flip()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
不过我感觉初始化代码代码有点多,有必要去封装一下,封装的函数我都放在set里面,下面是封装的函数和封装后主函数的样子。
封装的函数
import pygame,sys
from pygame.locals import *
from config_set import CONFIG
#游戏初始化
def game_init():
pygame.init()
pygame.display.set_caption(CONFIG['capation'])
screen = pygame.display.set_mode(CONFIG['screen_size'])
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill(CONFIG['background_color'])
return screen,background
#处理退出
def handle_exit(run):
for event in pygame.event.get():
if event.type == QUIT:
run = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
run = False
return run
封装后的主函数
import pygame,sys
import set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
self.screen,self.background= set.game_init()
self.run = True
# 处理事件
def handle_event(self):
self.run =set.handle_exit(self.run)
#游戏运行
def run_game(self):
while self.run:
self.handle_event()
self.screen.blit(self.background,(0,0))
pygame.display.flip()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
是不是一下子就看的非常的简单。
四,加载资源
在之前的初始化步骤里,我们已经搭好了框架,现在需要向里面填东西,比如菜单啊,按钮啊什么的东西,不过在此之前,需要加载好资源,以便后面创建类的代码可以简单一些。
不过这些轮子都是固定的,我直接用之前写好的轮子了,大家直接复制到set文件里使用即可。
1.获取资源文件夹地址:
def load_dir():
main_dir = os.path.split(os.path.abspath(__file__))[0]
pic_dir = os.path.join(main_dir,'pic')
font_dir = os.path.join(main_dir,'font')
sound_dir = os.path.join(main_dir,'sound')
#更新配置
CONFIG['main_dir'] = main_dir
CONFIG['pic_dir'] = pic_dir
CONFIG['font_dir'] = font_dir
CONFIG['sound_dir'] = sound_dir
2.加载其他资源:
函数怎么用可以看看注释,至于怎么实现的也不难理解,不过直接用就好了,反正使用的时候又不关心细节。
#加载图片的通用函数,第一个为图片名字,第二个是否更改图片大小,第三个为是否透明,第四个为图片更改的大小
def load_pic(name,set_size=None,colorkey=None,size=(100,100)):
fullname = os.path.join(CONFIG['pic_dir'],name)
try:
image = pygame.image.load(fullname)
except pygame.error:
print('Cannot load image:',fullname)
raise SystemExit
if set_size:
image = pygame.transform.scale(image,size)
image = image.convert_alpha()#修改了这里
if colorkey is not None:
if colorkey == -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey,RLEACCEL)
return image,image.get_rect()
#加载音效
def load_sound(name):
class NoneSound:
def play(self): pass
if not pygame.mixer:
return NoneSound()
fullname = os.path.join(CONFIG['sound_dir'],name)
try:
sound = pygame.mixer.Sound(fullname)
except pygame.error:
print("Cannot load sound:",fullname)
raise SystemExit
return sound
#加载字体
def load_font(name,font_size):
fullname = os.path.join(CONFIG['font_dir'],name)
try:
font = pygame.font.Font(fullname,font_size)
except pygame.error:
print("Cannot load font:",fullname)
raise SystemExit
return font
#绘制文字的函数,第一个是文本,第二哥是字体,第二个是字体大小,第三个是颜色,第四个是绘制的surface,第五个是x坐标,第六个是y坐标
def draw_text(text,name,font_size,color,surface,x,y):
font = load_font(name,font_size)
text_set = font.render(text,1,color)
surface.blit(text_set,(x,y))
五,设置主菜单
不管怎么样,先把主菜单这种可以更换场景的功能做出来,而且到此刻的内容不管什么游戏都是通用的,没有涉及到游戏逻辑,换其他的游戏也可以这样做,这也是我设计成这样结构的原因。
更换主菜单其实可以if语句来实现,具体来讲就是给Game类加一个 游戏状态的属性,然后通过这个属性来判断现在处于哪个界面
那如何更改这个属性呢?我采取的方法是按键,点击开始游戏按钮,游戏就运行,点击退出游戏按钮,游戏就退出
所以我要创建个按钮类,那按键该怎么管理呢,我创建了个菜单类,菜单上有按钮来处理事件,下面是类代码:
因为加载图片的函数的功能过多,所以初始化的参数很长。
类函数
import pygame,sys
import set
from pygame.locals import *
from config_set import CONFIG
class Button(pygame.sprite.Sprite):
#按钮类,初始化参数包括,原始的图片,鼠标放上去显示的图片,图片的位置,被点击后要处理的函数,是否要设置图片的大小,以及图片的尺寸和颜色键
def __init__(self,normal_image,hover_image,position,callback,set_size=None,size=(100,100),colorkey=True):
pygame.sprite.Sprite.__init__(self)
self.normal_image,self.rect=set.load_pic(normal_image,set_size,colorkey,size)
self.hover_image,_=set.load_pic(hover_image,set_size,colorkey,size)
self.image=self.normal_image
self.rect.center=position
self.callback= callback
self.is_hover=False
#这个是判断鼠标的状态,鼠标放上去,显示鼠标放上去的图片,鼠标点击,处理被点击后的函数
def handle_event(self,event):
if event.type==MOUSEMOTION:
self.is_hover=self.rect.collidepoint(event.pos)
elif event.type==MOUSEBUTTONDOWN and self.is_hover:
if self.is_hover and event.button==1:
self.callback()
self.image = self.hover_image if self.is_hover else self.normal_image
#菜单类
class Menu:
#初始化参数是将菜单放到哪里
def __init__(self,screen):
self.screen=screen
self.buttons = pygame.sprite.Group()
#添加按键
def add_button(self,button):
self.buttons.add(button)
#绘制按钮
def draw(self):
self.buttons.draw(self.screen)
#处理每一个按钮的事件。
def handle_event(self,events):
for event in events:
if event.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN):
for button in self.buttons:
button.handle_event(event)
主函数:
又优化了一下,改变的部分我都用注释标注出来了
import pygame,sys
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.main_menu = self.creat_main_menu()#添加了这里
self.run = True
self.game_case = 'start'#还有这里
#设置主菜单
def creat_main_menu(self):#添加了这个方法
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):#这里
self.game_case = 'game'
#让状态改为结束
def exit_game(self):#这里
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)#修改了这里
#游戏运行
def run_game(self):
while self.run:
events = pygame.event.get()
self.handle_event(events)
self.screen.blit(self.background,(0,0))
if self.game_case == 'start':#修改了这里
self.main_menu.handle_event(events)
self.main_menu.draw()
pygame.display.flip()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
因为要在循环里对鼠标事件进行处理,为了节约性能,就对事件进行统一的获取了
这是修改后的handle_exit函数:
def handle_exit(run,events):
for event in events:
if event.type == QUIT:
run = False
elif event.type == KEYDOWN and event.key == K_ESCAPE:
run = False
return run
效果:
现在都只是测试,后面再添加背景
一进来就是主菜单
这个按钮有背景的问题已经解决了,你直接复制前面的代码就行,我已经修改了

鼠标放上去可以更换图片

到此,菜单的功能实现了,可以根据需求来自行添加东西。
六,设置背景:
没有背景的游戏很丑,选取了错误的背景也很丑,不过这篇文章的图片都是测试,这不是我的审美!!
首先,我们要有加载背景的函数,其实之前已经有了,不过为了适应我的配置文件不得不新加一个
我目前的配置文件:
CONFIG={
'capation':'漫宿骄盛的例子',
'screen_size':(800,600),
'background_color':(110,110,110),
'background_pic':{
'playing':'playing.png',
'main_menu':'main_menu.png',
},
'start':'start.png',
'exit':'exit.png',
'confirm':'confirm.png',
'main_dir':None,
'font_dir':None,
'pic_dir':None,
'sound_dir':None,
}
加载背景的函数:
def load_background(state,size=None):
if size is None:
size = CONFIG['screen_size']
name = CONFIG['background_pic'][state]
background,_ = load_pic(name,set_size=True,size=size)
return background
主函数:
又优化了一下,改变的部分我都用注释标注出来了
import pygame,sys
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.background_list()#更改:这里加载背景图片
self.main_menu = self.creat_main_menu()
self.run = True
self.game_case = 'main_menu'
#更改:将所有的背景图片都预先加载出来
def background_list(self):
self.backgrounds={
'main_menu':set.load_background('main_menu'),
'playing':set.load_background('playing'),
}
#设置主菜单
def creat_main_menu(self):
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):
self.game_case = 'playing'
#让状态改为结束
def exit_game(self):
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)
#更改:将背景图片的更改放到了一个方法里
def update_screen(self):
self.screen.blit(self.backgrounds[self.game_case],(0,0))
#将切换页面的功能放到了一个方法里
def uptate_case(self,events):
if self.game_case == 'main_menu':
self.main_menu.handle_event(events)
self.main_menu.draw()
#游戏运行
def run_game(self):
while self.run:
events = pygame.event.get()
self.handle_event(events)
self.update_screen()#更改:使用方法
self.uptate_case(events)#更改:使用方法
pygame.display.flip()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
效果:
图片比例不太对,就这样子吧。

点击开始游戏:

完成了,接下来就是游戏功能的实现了
七,游戏逻辑:
终于到游戏部分了,前面的是通用部分,什么游戏都可以用,接下来的就只有这个游戏能用了
先制作玩家的飞机
#玩家类
class Player(pygame.sprite.Sprite):
#变量为位置,图片,是否设置大小,图片大小
#里面包含速度
def __init__(self,position,image,set_size=None,size=(100,100)):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.pos = pygame.Vector2(self.rect.center)
#按键移动
def move(self):
self.limit()
keys = pygame.key.get_pressed()
dx = (keys[K_d] -keys[K_a])*self.speed
dy = (keys[K_s] -keys[K_w])*self.speed
self.pos.x += dx
self.pos.y += dy
self.rect.center = self.pos
#限制,不能超出屏幕
def limit(self):
self.pos.x = max(0,min(self.pos.x,CONFIG['screen_size'][0]))
self.pos.y = max(0,min(self.pos.y,CONFIG['screen_size'][1]))
主函数:
老样子,修改的地方我会标出来
import pygame,sys
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.background_list()
self.sprite_init()
self.main_menu = self.creat_main_menu()
self.run = True
self.game_case = 'main_menu'
#初始化所有对象
def sprite_init(self):#这里新添
self.player = class_set.Player((400,300),CONFIG['player'],set_size=True,size=(50,50))
self.all_sprites = pygame.sprite.Group()
self.all_sprites.add(self.player)
#加载背景列表
def background_list(self):
self.backgrounds={
'main_menu':set.load_background('main_menu'),
'playing':set.load_background('playing'),
}
#设置主菜单
def creat_main_menu(self):
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):
self.game_case = 'playing'
#让状态改为结束
def exit_game(self):
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)
#处理游戏中的事件
def playing_handle(self):#这里新添
self.all_sprites.update()
self.all_sprites.draw(self.screen)
#更新屏幕
def update_screen(self):
self.screen.blit(self.backgrounds[self.game_case],(0,0))
#更新页面
def update_case(self,events):
if self.game_case == 'main_menu':
self.main_menu.handle_event(events)
self.main_menu.draw()
elif self.game_case == 'playing':#这里新添
self.playing_handle()
#游戏运行
def run_game(self):
clock = pygame.time.Clock()#这里新添
while self.run:
clock.tick(60)
events = pygame.event.get()
self.handle_event(events)
self.update_screen()
self.update_case(events)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
效果:
选项有背景的问题我解决了!!:

飞机可以按WASD移动:

敌机与计时器:
#敌人,老样子参数
class Enemy(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(100,100)):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.pos = pygame.Vector2(self.rect.center)
def move(self):
self.limit()
self.rect.y += self.speed
def limit(self):
if self.rect.y > CONFIG['screen_size'][1]:
self.kill()
def update(self):
self.move()
#计时器,每隔time毫秒输出一次True
class Timer():
def __init__(self,time):
self.time = time
self.last_time = pygame.time.get_ticks()
def is_seted(self):
now = pygame.time.get_ticks()
if now - self.last_time > self.time:
self.last_time = now
return True
return False
主函数:
import pygame,sys,random
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.background_list()
self.sprite_init()
self.main_menu = self.creat_main_menu()
self.enemy_spawn_timer = class_set.Timer(100)#敌人生成定时器,添加了这个
self.run = True
self.game_case = 'main_menu'
#初始化所有对象
def sprite_init(self):
self.player = class_set.Player((400,300),CONFIG['player'],set_size=True,size=(50,50))
self.all_sprites = pygame.sprite.Group()
self.enemies = pygame.sprite.Group()#添加了这个
self.all_sprites.add(self.player)
self.all_sprites.add(self.enemies)#添加了这个
#加载背景列表
def background_list(self):
self.backgrounds={
'main_menu':set.load_background('main_menu'),
'playing':set.load_background('playing'),
}
#设置主菜单
def creat_main_menu(self):
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):
self.game_case = 'playing'
#让状态改为结束
def exit_game(self):
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)
#处理游戏中的事件
def playing_handle(self):
if self.enemy_spawn_timer.is_seted():#添加了这个
self.spawn_enemy()#添加了这个
self.all_sprites.update()
self.all_sprites.draw(self.screen)
#敌人绘制
def spawn_enemy(self):#添加了这个
x = random.randint(0,CONFIG['screen_size'][0])
y = -50
enemy = class_set.Enemy((x,y),CONFIG['enemy'],set_size=True,size=(50,50))
self.enemies.add(enemy)
self.all_sprites.add(enemy)
#更新屏幕
def update_screen(self):
self.screen.blit(self.backgrounds[self.game_case],(0,0))
#更新页面
def update_case(self,events):
if self.game_case == 'main_menu':
self.main_menu.handle_event(events)
self.main_menu.draw()
elif self.game_case == 'playing':
self.playing_handle()
#游戏运行
def run_game(self):
clock = pygame.time.Clock()
while self.run:
clock.tick(60)
events = pygame.event.get()
self.handle_event(events)
self.update_screen()
self.update_case(events)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
效果:

子弹:
这个子弹弄得我好惨,半天都看不出问题来,问AI,AI也不知道问题在哪里
不过最终还是找到了,问题就在敌人的组别里,不过现在先不说,后面再说
实现的发射不需要去动主函数,只用动类就可以了
下面是一个一个类的改变:
弄了一个子弹父类,其下有两个子类,一个是玩家子弹另一个是敌人子弹,毕竟敌人和玩家的方向不同
#子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(10,30),speed=5,damage=1):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect = set.load_pic(image,set_size,size=size)
self.rect.center = position
self.speed = speed
self.damage = damage
#玩家子弹
class Bullet_player(Bullet):
def __init__(self,position,image,set_size=None,size=(10,30),speed=5,damage=1):
super().__init__(position,image,set_size,size,speed,damage)
def move(self):
self.rect.y -= self.speed
if self.rect.y<0:
self.kill()
def update(self):
self.move()
#敌人子弹
class Bullet_enemy(Bullet):
def __init__(self,position,image,set_size=None,size=(10,30),speed=5,damage=1):
super().__init__(position,image,set_size,size,speed,damage)
def move(self):
self.rect.y += self.speed
if self.rect.y>CONFIG['screen_size'][1]:
self.kill()
def update(self):
self.move()
玩家子弹的发射的实现:
类函数:
#玩家类
class Player(pygame.sprite.Sprite):
#变量为位置,图片,是否设置大小,图片大小,子弹所在的精灵组,以及所有精灵组
#里面包含速度,血量
def __init__(self,position,image,set_size=None,size=(100,100),bullet_group=None,all_sprites=None):
pygame.sprite.Sprite.__init__(self)
self.bullet_group = bullet_group
self.all_sprites = all_sprites
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.health = 100
self.shoot_timer = 20
self.last_shoot = 0
self.pos = pygame.Vector2(self.rect.center)
#加载子弹
def spawn_shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shoot > self.shoot_timer:
bullet = Bullet_player(self.rect.center,CONFIG['player_bullet'],set_size=True,size=(10,20),speed=10,damage=1)
bullet.add(self.bullet_group,self.all_sprites)
self.last_shoot = now
#发射子弹
def shoot(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.spawn_shoot()
#按键移动
def move(self):
self.limit()
keys = pygame.key.get_pressed()
dx = (keys[K_d] -keys[K_a])*self.speed
dy = (keys[K_s] -keys[K_w])*self.speed
self.pos.x += dx
self.pos.y += dy
self.rect.center = self.pos
#限制,不能超出屏幕
def limit(self):
self.pos.x = max(0,min(self.pos.x,CONFIG['screen_size'][0]))
self.pos.y = max(0,min(self.pos.y,CONFIG['screen_size'][1]))
def update(self):
self.move()
self.shoot()
主函数的修改:
事实上就变了以下顺序,给 player的类多加了两个参数
#初始化所有对象
def sprite_init(self):
self.all_sprites = pygame.sprite.Group()
self.enemies = pygame.sprite.Group()
self.player_bullets = pygame.sprite.Group()
self.enemy_bullets = pygame.sprite.Group()
self.player = class_set.Player((400,300),CONFIG['player'],set_size=True,size=(50,50),bullet_group=self.player_bullets,all_sprites=self.all_sprites)
self.all_sprites.add(self.player)
self.all_sprites.add(self.enemies)
效果:

敌人子弹的实现:
类函数
#敌人类
class Enemy(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(100,100),bullet_group=None,all_sprites=None):
pygame.sprite.Sprite.__init__(self)
self.bullet_group = bullet_group
self.all_sprites = all_sprites
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.shoot_timer =Timer(100)#计时器
self.pos = pygame.Vector2(self.rect.center)
self.health = 5
#发射子弹
def shoot(self):
if self.shoot_timer.is_seted():
if self.groups():
bullet = Bullet_enemy(self.rect.center,CONFIG['enemy_bullet'],set_size=True,size=(10,20),speed=5,damage=1)
bullet.add(self.bullet_group,self.all_sprites)
#向下移动
def move(self):
self.limit()
self.rect.y += self.speed
#移动限制
def limit(self):
if self.rect.y > CONFIG['screen_size'][1]:
self.kill()
def update(self):
self.move()
self.shoot()
主函数
这个也很简单,就多传了两个参数
enemy = class_set.Enemy((x,y),CONFIG['enemy'],set_size=True,size=(50,50),bullet_group= self.enemy_bullets,all_sprites=self.all_sprites)
效果:

子弹碰撞,让敌人掉血:
在完成上面那两个之后,下面这个也很简单
类函数:
给玩家和敌人类都加上了个这个方法。
#处理伤害
def take_damage(self,damage):
self.health -= damage
if self.health <= 0:
self.kill()
主函数:
给Game类加上check_collision方法,并且在playing_handle中调用。
#处理游戏中的事件
def playing_handle(self):
self.check_collision()
if self.enemy_spawn_timer.is_seted():
self.spawn_enemy()
self.all_sprites.update()
self.all_sprites.draw(self.screen)
#检查碰撞
def check_collision(self):
collisions = pygame.sprite.groupcollide(self.enemies,self.player_bullets,False,True)
for enemy,bullets in collisions.items():
for bullet in bullets:
enemy.take_damage(bullet.damage
效果:

被敌人子弹碰撞,被敌人碰到,自己掉血:
类函数;
给玩家类添加了一个展示血量的功能
#展示血量
def show_hp(self,surface):
set.draw_text(f'HP:{self.hp}',CONFIG['font1'],60,(255,255,255),surface,60,60)
主函数:
检测碰撞,碰到就掉血
#检查碰撞
def check_collision(self):
#玩家子弹和敌人
player_bullet_collisions = pygame.sprite.groupcollide(self.enemies,self.player_bullets,False,True)
for enemy,bullets in player_bullet_collisions.items():
for bullet in bullets:
enemy.take_damage(bullet.damage)
#敌人子弹和玩家
enemy_bullet_collisions = pygame.sprite.groupcollide(self.player_group,self.enemy_bullets,False,True)
for player,bullets in enemy_bullet_collisions.items():
for bullet in bullets:
player.take_damage(bullet.damage)
#敌人和玩家
enemy_collisions = pygame.sprite.groupcollide(self.enemies,self.player_group,True,False)
for enemy,players in enemy_collisions.items():
for player in players:
player.take_damage(enemy.damage)
效果:

到此为止的所有代码:
主函数
import pygame,sys,random
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.background_list()
self.sprite_init()
self.main_menu = self.creat_main_menu()
self.enemy_spawn_timer = class_set.Timer(100)#敌人生成定时器
self.run = True
self.game_case = 'main_menu'
#初始化所有对象
def sprite_init(self):
self.all_sprites = pygame.sprite.Group()
self.enemies = pygame.sprite.Group()
self.player_group = pygame.sprite.Group()
self.player_bullets = pygame.sprite.Group()
self.enemy_bullets = pygame.sprite.Group()
self.player = class_set.Player((400,300),CONFIG['player'],set_size=True,size=(50,50),bullet_group=self.player_bullets,all_sprites=self.all_sprites)
self.player_group.add(self.player)
self.all_sprites.add(self.player)
self.all_sprites.add(self.enemies)
#加载背景列表
def background_list(self):
self.backgrounds={
'main_menu':set.load_background('main_menu'),
'playing':set.load_background('playing'),
}
#设置主菜单
def creat_main_menu(self):
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):
self.game_case = 'playing'
#让状态改为结束
def exit_game(self):
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)
#处理游戏中的事件
def playing_handle(self):
self.player.show_hp(self.screen)
self.check_collision()
if self.enemy_spawn_timer.is_seted():
self.spawn_enemy()
self.all_sprites.update()
self.all_sprites.draw(self.screen)
#敌人绘制
def spawn_enemy(self):
x = random.randint(0,CONFIG['screen_size'][0])
y = -50
enemy = class_set.Enemy((x,y),CONFIG['enemy'],set_size=True,size=(50,50),bullet_group= self.enemy_bullets,all_sprites=self.all_sprites)
self.enemies.add(enemy)
self.all_sprites.add(enemy)
#检查碰撞
def check_collision(self):
#玩家子弹和敌人
player_bullet_collisions = pygame.sprite.groupcollide(self.enemies,self.player_bullets,False,True)
for enemy,bullets in player_bullet_collisions.items():
for bullet in bullets:
enemy.take_damage(bullet.damage)
#敌人子弹和玩家
enemy_bullet_collisions = pygame.sprite.groupcollide(self.player_group,self.enemy_bullets,False,True)
for player,bullets in enemy_bullet_collisions.items():
for bullet in bullets:
player.take_damage(bullet.damage)
#敌人和玩家
enemy_collisions = pygame.sprite.groupcollide(self.enemies,self.player_group,True,False)
for enemy,players in enemy_collisions.items():
for player in players:
player.take_damage(enemy.damage)
#更新屏幕
def update_screen(self):
self.screen.blit(self.backgrounds[self.game_case],(0,0))
#更新页面
def update_case(self,events):
if self.game_case == 'main_menu':
self.main_menu.handle_event(events)
self.main_menu.draw()
elif self.game_case == 'playing':
self.playing_handle()
#游戏运行
def run_game(self):
clock = pygame.time.Clock()
while self.run:
clock.tick(60)
events = pygame.event.get()
self.handle_event(events)
self.update_screen()
self.update_case(events)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
类函数:
import pygame,sys
import set
from pygame.locals import *
from config_set import CONFIG
class Button(pygame.sprite.Sprite):
#按钮类,初始化参数包括,原始的图片,鼠标放上去显示的图片,图片的位置,被点击后要处理的函数,是否要设置图片的大小,以及图片的尺寸和颜色键
def __init__(self,normal_image,hover_image,position,callback,set_size=None,size=(100,100),colorkey=True):
pygame.sprite.Sprite.__init__(self)
self.normal_image,self.rect=set.load_pic(normal_image,set_size,colorkey,size)
self.hover_image,_=set.load_pic(hover_image,set_size,colorkey,size)
self.image=self.normal_image
self.rect.center=position
self.callback= callback
self.is_hover=False
#这个是判断鼠标的状态,鼠标放上去,显示鼠标放上去的图片,鼠标点击,处理被点击后的函数
def handle_event(self,event):
if event.type==MOUSEMOTION:
self.is_hover=self.rect.collidepoint(event.pos)
elif event.type==MOUSEBUTTONDOWN and self.is_hover:
if self.is_hover and event.button==1:
self.callback()
self.image = self.hover_image if self.is_hover else self.normal_image
#菜单类
class Menu:
#初始化参数是将菜单放到哪里
def __init__(self,screen):
self.screen=screen
self.buttons = pygame.sprite.Group()
#添加按键
def add_button(self,button):
self.buttons.add(button)
#绘制按钮
def draw(self):
self.buttons.draw(self.screen)
#处理每一个按钮的事件。
def handle_event(self,events):
for event in events:
if event.type in (pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN):
for button in self.buttons:
button.handle_event(event)
#计时器,每隔time毫秒输出一次True
class Timer():
def __init__(self,time):
self.time = time
self.last_time = pygame.time.get_ticks()
def is_seted(self):
now = pygame.time.get_ticks()
if now - self.last_time > self.time:
self.last_time = now
return True
return False
#玩家类
class Player(pygame.sprite.Sprite):
#变量为位置,图片,是否设置大小,图片大小,子弹所在的精灵组,以及所有精灵组
#里面包含速度,血量
def __init__(self,position,image,set_size=None,size=(100,100),bullet_group=None,all_sprites=None):
pygame.sprite.Sprite.__init__(self)
self.bullet_group = bullet_group
self.all_sprites = all_sprites
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.hp = 100
self.shoot_timer = 20
self.last_shoot = 0
self.pos = pygame.Vector2(self.rect.center)
#加载子弹
def spawn_shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shoot > self.shoot_timer:
bullet = Bullet_player(self.rect.center,CONFIG['player_bullet'],set_size=True,size=(10,20),speed=10,damage=1)
bullet.add(self.bullet_group,self.all_sprites)
bullet.add(self.bullet_group,self.all_sprites)
self.last_shoot = now
#发射子弹
def shoot(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.spawn_shoot()
#按键移动
def move(self):
self.limit()
keys = pygame.key.get_pressed()
dx = (keys[K_d] -keys[K_a])*self.speed
dy = (keys[K_s] -keys[K_w])*self.speed
self.pos.x += dx
self.pos.y += dy
self.rect.center = self.pos
#限制,不能超出屏幕
def limit(self):
self.pos.x = max(0,min(self.pos.x,CONFIG['screen_size'][0]))
self.pos.y = max(0,min(self.pos.y,CONFIG['screen_size'][1]))
#处理伤害
def take_damage(self,damage):
self.hp -= damage
if self.hp <= 0:
self.kill()
#展示血量
def show_hp(self,surface):
set.draw_text(f'HP:{self.hp}',CONFIG['font1'],60,(255,255,255),surface,60,60)
#检查是否存活
def is_alive(self):
if self.hp <= 0:
return False
def update(self):
self.move()
self.shoot()
#敌人类
class Enemy(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(100,100),bullet_group=None,all_sprites=None):
pygame.sprite.Sprite.__init__(self)
self.bullet_group = bullet_group
self.all_sprites = all_sprites
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=2
self.shoot_timer =Timer(1000)#计时器
self.pos = pygame.Vector2(self.rect.center)
self.hp = 1
self.damage= 5
#发射子弹
def shoot(self):
if self.shoot_timer.is_seted():
if self.groups():
bullet = Bullet_enemy(self.rect.center,CONFIG['enemy_bullet'],set_size=True,size=(10,20),speed=5,damage=1)
bullet.add(self.bullet_group,self.all_sprites)
#向下移动
def move(self):
self.limit()
self.rect.y += self.speed
#移动限制
def limit(self):
if self.rect.y > CONFIG['screen_size'][1]:
self.kill()
#处理伤害
def take_damage(self,damage):
self.hp -= damage
if self.hp <= 0:
self.kill()
def update(self):
self.move()
self.shoot()
#子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(10,30)):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect = set.load_pic(image,set_size,size=size)
self.rect.center = position
self.speed = 12
self.damage = 1
#玩家子弹
class Bullet_player(Bullet):
def move(self):
self.rect.y -= self.speed
if self.rect.y<0:
self.kill()
def update(self):
self.move()
#敌人子弹
class Bullet_enemy(Bullet):
def move(self):
self.rect.y += self.speed
if self.rect.y>CONFIG['screen_size'][1]:
self.kill()
def update(self):
self.move()
set文件:
import pygame,os
from pygame import *
from config_seting import CONFIG # type: ignore
#加载资源地址的函数
def load_dir():
main_dir = os.path.split(os.path.abspath(__file__))[0]
pic_dir = os.path.join(main_dir,'pic')
font_dir = os.path.join(main_dir,'font')
sound_dir = os.path.join(main_dir,'sound')
#更新配置
CONFIG['main_dir'] = main_dir
CONFIG['pic_dir'] = pic_dir
CONFIG['font_dir'] = font_dir
CONFIG['sound_dir'] = sound_dir
#加载图片的通用函数,第一个为图片名字,第二个是否更改图片大小,第三个为是否透明,第四个为图片更改的大小
def load_pic(name,set_size=None,colorkey=None,size=(100,100)):
fullname = os.path.join(CONFIG['pic_dir'],name)
try:
image = pygame.image.load(fullname)
except pygame.error:
print('Cannot load image:',fullname)
raise SystemExit
if set_size:
image = pygame.transform.scale(image,size)
image = image.convert()
if colorkey is not None:
if colorkey == -1:
colorkey = image.get_at((0,0))
image.set_colorkey(colorkey,RLEACCEL)
return image,image.get_rect()
#加载音效
def load_sound(name):
class NoneSound:
def play(self): pass
if not pygame.mixer:
return NoneSound()
fullname = os.path.join(CONFIG['sound_dir'],name)
try:
sound = pygame.mixer.Sound(fullname)
except pygame.error:
print("Cannot load sound:",fullname)
raise SystemExit
return sound
#加载字体的函数
def load_font(name,font_size):
fullname = os.path.join(CONFIG['font_dir'],name)
try:
font = pygame.font.Font(fullname,font_size)
except pygame.error:
print("Cannot load font:",fullname)
raise SystemExit
return font
#绘制文字的函数
def draw_text(text,name,font_size,color,surface,x,y):
font = load_font(name,font_size)
text_set = font.render(text,1,color)
surface.blit(text_set,(x,y))
#初始化的通用函数,输入参数0,隐藏鼠标,不输入,显示鼠标
def game_init(pos=False):
pygame.init()
window = pygame.display.set_mode(CONFIG['screen_size'])
pygame.display.set_caption(CONFIG['capation'])
background = pygame.Surface(window.get_size())
background = background.convert()
background.fill(CONFIG['background_color'])
clock = pygame.time.Clock()
if not pos:
pygame.mouse.set_visible(pos)
return window,background,clock
#设置退出事件
def handel_event(run):
for event in pygame.event.get():
if event.type == QUIT:
run = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
run = False
return run
配置文件
CONFIG={
'capation':'漫宿骄盛的例子',
'screen_width':800,
'screen_height':600,
'screen_size':(800,600),
'background_color':(110,110,110),
'background_pic':{
'playing':'playing.png',
'main_menu':'main_menu.png',
},
'start':'start.png',
'exit':'exit.png',
'confirm':'confirm.png',
'player':'player.png',
'player_bullet':'player_bullet.png',
'enemy':'enemies.png',
'enemy_bullet':'enemy_bullet.png',
'font1':'msyh.ttc',
'main_dir':None,
'font_dir':None,
'pic_dir':None,
'sound_dir':None,
}
以下是错误示范:
为什么错误?
我当初实现的时候可能偷懒了,直接将子弹的精灵组绑死了,后面想实现子弹的碰撞发现实现不了,各种其他功能也实现不了,虽然代码简单,但已经几乎没法再添加和子弹相关的功能了,这种情况被称为耦合度过高,代码与代码直接的联系过于紧密,使得牵一发而动全身,幸好我发现的早,需要改的代码没几多少。错误呢,我也就保留了,当个示范吧,其中错误的部分是shoot方法的实现
玩家子弹的发射的实现:
实现的逻辑是按一次按键,一直按着也行,生成一个玩家子弹对象,并将这个对象放到玩家所在精灵组里,也就是全体精灵组,一起刷新绘制
#玩家类
class Player(pygame.sprite.Sprite):
#变量为位置,图片,是否设置大小,图片大小
#里面包含速度
def __init__(self,position,image,set_size=None,size=(100,100)):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.health = 100
self.shoot_timer = 20#添加这里,每冷却20毫秒
self.last_shoot = 0
self.pos = pygame.Vector2(self.rect.center)
def spawn_shoot(self):#这里,加载子弹,如果冷却期一过就可以发生子弹,但必须触发才行
now = pygame.time.get_ticks()
if now - self.last_shoot > self.shoot_timer:
bullet = Bullet_player(self.rect.center,CONFIG['player_bullet'],set_size=True,size=(10,20))
self.groups()[0].add(bullet)#######错在这里!!!!
self.last_shoot = now
def shoot(self):#触发射击子弹
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:#按空格发生
self.spawn_shoot()
#按键移动
def move(self):
self.limit()
keys = pygame.key.get_pressed()
dx = (keys[K_d] -keys[K_a])*self.speed
dy = (keys[K_s] -keys[K_w])*self.speed
self.pos.x += dx
self.pos.y += dy
self.rect.center = self.pos
#限制,不能超出屏幕
def limit(self):
self.pos.x = max(0,min(self.pos.x,CONFIG['screen_size'][0]))
self.pos.y = max(0,min(self.pos.y,CONFIG['screen_size'][1]))
def update(self):
self.move()
self.shoot()
敌人子弹的实现:
实现的逻辑是每隔一个设定的时间生成一个敌人子弹对象,并将这个对象放在全体的精灵组里在我的代码里,敌人先被放在敌人组,再被放到全体精灵组,所以 全体精灵组对敌人是第二个组,其实不放在全体精灵组也行,只要在主函数加上敌人精灵组绘制和更新的函数就行
#敌人类
class Enemy(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(100,100)):
pygame.sprite.Sprite.__init__(self)
self.image,self.rect=set.load_pic(image,set_size=set_size,size=size)
self.rect.center=position
self.speed=5
self.shoot_timer =Timer(100)#计时器
self.pos = pygame.Vector2(self.rect.center)
#发射子弹
def shoot(self):
if self.shoot_timer.is_seted():
if self.groups():
bullet = Bullet_enemy(self.rect.center,CONFIG['enemy_bullet'],set_size=True,size=(10,20))
self.groups()[1].add(bullet)
def move(self):
self.limit()
self.rect.y += self.speed
def limit(self):
if self.rect.y > CONFIG['screen_size'][1]:
self.kill()
def update(self):
self.move()
self.shoot()
多么汹涌的弹幕!
八,其他各种小功能
1.积分功能:
类函数修改
#敌人类
class Enemy(pygame.sprite.Sprite):
def __init__(self,position,image,set_size=None,size=(100,100),bullet_group=None,all_sprites=None,on_death=None):
pygame.sprite.Sprite.__init__(self)
self.bullet_group = bullet_group
self.all_sprites = all_sprites
self.on_death = on_death#增加了一个死亡时调用的函数
#处理伤害
def take_damage(self,damage):
self.hp -= damage
if self.hp <= 0:
if self.on_death:
self.on_death()#触发此函数
self.kill()
主函数修改:
增添了一个加分数的方法,并在传给敌人
#增加积分
def add_score(self,score):
self.score += score
enemy = class_set.Enemy((x,y),CONFIG['enemy'],set_size=True,size=(50,50),bullet_group= self.enemy_bullets,all_sprites=self.all_sprites,on_death= lambda:self.add_score(500))
#
效果:

2.血条功能:
主函数:
#展示血条:
def show_hp(self,surface):
#绘制玩家血条
player_hp_width = self.player.rect.width * self.player.hp / self.player.max_hp
player_hp_bar_y = self.player.rect.y - 10
pygame.draw.rect(surface,(255,0,0),(self.player.rect.x,player_hp_bar_y,self.player.rect.width,10))
pygame.draw.rect(surface,(0,255,0),(self.player.rect.x,player_hp_bar_y,player_hp_width,10))
#绘制敌人血条
for enemy in self.enemies:
hp_width = enemy.rect.width * enemy.hp / enemy.max_hp
hp_bar_y = enemy.rect.y - 10
pygame.draw.rect(surface,(255,0,0),(enemy.rect.x,hp_bar_y,enemy.rect.width,enemy.hp_bar))
pygame.draw.rect(surface,(0,255,0),(enemy.rect.x,hp_bar_y,hp_width,enemy.hp_bar))
类函数的话就给敌人类玩家类加上max_hp,hp_bar的属性就行
效果:

3.游戏失败及重新游戏
实现方法:创建一个失败界面,加上重新游戏按钮。然后在游戏事件处理中加上对玩家生命值的判断,如果死亡,那游戏状态就变为game_over,跳转到失败界面。
不过这样,我就得对游戏开始函数进行修改,游戏开始要清除之前所有精灵组里的内容,并且要创建玩家
我还顺手加了一个记录最高分数的功能
主函数代码:
import pygame,sys,random
import set,class_set
from pygame.locals import *
from config_set import CONFIG
class Game():
#初始化
def __init__(self):
set.load_dir()
self.screen,self.background= set.game_init()
self.background_list()
self.sprite_init()
self.main_menu = self.creat_main_menu()
self.game_over_menu = self.creat_game_over_menu()#添加这里
self.enemy_spawn_timer = class_set.Timer(1000)#敌人生成定时器
self.score_timer = class_set.Timer(10)
self.run = True
self.game_case = 'main_menu'
self.score_list=[0]#添加这里
self.score=0
#初始化所有对象
def sprite_init(self):#修改了
self.all_sprites = pygame.sprite.Group()
self.enemies = pygame.sprite.Group()
self.player_group = pygame.sprite.Group()
self.player_bullets = pygame.sprite.Group()
self.enemy_bullets = pygame.sprite.Group()
self.all_sprites.add(self.enemies)
#创建玩家
def create_player(self):#添加这里
self.player = class_set.Player((400,300),CONFIG['player'],set_size=True,size=(50,50),bullet_group=self.player_bullets,all_sprites=self.all_sprites)
self.player_group.add(self.player)
self.all_sprites.add(self.player)
#加载背景列表
def background_list(self):
self.backgrounds={
'main_menu':set.load_background('main_menu'),
'playing':set.load_background('playing'),
'game_over':set.load_background('game_over'),#添加这里
}
#设置主菜单
def creat_main_menu(self):
menu = class_set.Menu(self.screen)
start_btn = class_set.Button(CONFIG['start'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(start_btn)
menu.add_button(exit_btn)
return menu
#创建结束界面:
def creat_game_over_menu(self):#添加这里
menu = class_set.Menu(self.screen)
#创建按钮
reset_btn = class_set.Button(CONFIG['reset'],
CONFIG['confirm'],
(400,100),
self.begin_game,
True,
(100,100),)
exit_btn = class_set.Button(CONFIG['exit'],
CONFIG['confirm'],
(400,300),
self.exit_game,
True,
(100,100),)
menu.add_button(reset_btn)
menu.add_button(exit_btn)
return menu
#让状态改为开始
def begin_game(self):#修改这里
#清空所有精灵
self.all_sprites.empty()
self.enemies.empty()
self.player_bullets.empty()
self.enemy_bullets.empty()
self.player_group.empty()
#创建玩家
self.create_player()
#记录最高分数
self.max_score = max(self.score_list)
self.game_case = 'playing'
#让状态改为玩家死亡
def player_death(self):#添加这里
self.game_case = 'game_over'
#让状态改为结束
def exit_game(self):
self.run = False
# 处理事件
def handle_event(self,events):
self.run =set.handle_exit(self.run,events)
#处理游戏中的事件
def playing_handle(self):
if self.player.hp <= 0:#添加这里
self.player_death()
self.handle_score()
self.check_collision()
self.show_hp(self.screen)
if self.enemy_spawn_timer.is_seted():
self.spawn_enemy()
self.all_sprites.update()
self.all_sprites.draw(self.screen)
#敌人绘制
def spawn_enemy(self):
x = random.randint(0,CONFIG['screen_size'][0])
y = -50
enemy = class_set.Enemy((x,y),CONFIG['enemy'],set_size=True,size=(50,50),bullet_group= self.enemy_bullets,all_sprites=self.all_sprites,on_death= lambda:self.add_score(500))
self.enemies.add(enemy)
self.all_sprites.add(enemy)
#展示血条:
def show_hp(self,surface):
#绘制玩家血条
player_hp_width = self.player.rect.width * self.player.hp / self.player.max_hp
player_hp_bar_y = self.player.rect.y - 10
pygame.draw.rect(surface,(255,0,0),(self.player.rect.x,player_hp_bar_y,self.player.rect.width,10))
pygame.draw.rect(surface,(0,255,0),(self.player.rect.x,player_hp_bar_y,player_hp_width,10))
#绘制敌人血条
for enemy in self.enemies:
hp_width = enemy.rect.width * enemy.hp / enemy.max_hp
hp_bar_y = enemy.rect.y - 10
pygame.draw.rect(surface,(255,0,0),(enemy.rect.x,hp_bar_y,enemy.rect.width,enemy.hp_bar))
pygame.draw.rect(surface,(0,255,0),(enemy.rect.x,hp_bar_y,hp_width,enemy.hp_bar))
#检查碰撞
def check_collision(self):
#玩家子弹和敌人
player_bullet_collisions = pygame.sprite.groupcollide(self.enemies,self.player_bullets,False,True)
for enemy,bullets in player_bullet_collisions.items():
for bullet in bullets:
enemy.take_damage(bullet.damage)
self.add_score(100)
#敌人子弹和玩家
enemy_bullet_collisions = pygame.sprite.groupcollide(self.player_group,self.enemy_bullets,False,True)
for player,bullets in enemy_bullet_collisions.items():
for bullet in bullets:
player.take_damage(bullet.damage)
#敌人和玩家
enemy_collisions = pygame.sprite.groupcollide(self.enemies,self.player_group,True,False)
for enemy,players in enemy_collisions.items():
for player in players:
player.take_damage(enemy.damage)
#更新屏幕
def update_screen(self):
self.screen.blit(self.backgrounds[self.game_case],(0,0))
#更新页面
def update_case(self,events):
if self.game_case == 'main_menu':
self.main_menu.handle_event(events)
self.main_menu.draw()
elif self.game_case == 'playing':
self.playing_handle()
elif self.game_case == 'game_over':
self.game_over_menu.handle_event(events)
self.game_over_menu.draw()
#增加积分
def add_score(self,score):
self.score += score
#处理分数
def handle_score(self):#修改这里
if self.game_case == 'playing':
set.draw_text(f'分数:{self.score}',CONFIG['font1'],55,(255,255,255),self.screen,370,20)
set.draw_text(f'历史最高分数:{self.max_score}',CONFIG['font1'],55,(255,255,255),self.screen,330,80)
elif self.game_case == 'game_over'or'main_menu':
if self.score != 0:
self.score_list.append(self.score)
self.score = 0
#游戏运行
def run_game(self):
clock = pygame.time.Clock()
while self.run:
clock.tick(60)
events = pygame.event.get()
self.handle_event(events)
self.update_screen()
self.update_case(events)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == '__main__':
game = Game()
game.run_game()
效果:



后记:
虽然后面还能添加别的功能,什么暂停啊,多种子弹啊,多种敌人,动态难度,但是乏了,就不再弄了,学逆向去了。
本文来自博客园,作者:漫宿骄盛,转载请注明原文链接:https://www.cnblogs.com/msjs/p/18823030
都是顺手发的,写的时候可能有错误,如果发现了,望各位指出。

浙公网安备 33010602011771号