用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()

效果:

后记:

虽然后面还能添加别的功能,什么暂停啊,多种子弹啊,多种敌人,动态难度,但是乏了,就不再弄了,学逆向去了。

posted @ 2025-04-13 10:56  漫宿骄盛  阅读(49)  评论(0)    收藏  举报