python 外星人入侵游戏 学习总结 以及完整代码

  • 从三月开始看《python:从入门到实践》自学python,踏入编程的世界,已经过去两个多月了,学完基础知识之后开始学习游戏项目《外星人入侵》。
    外星人入侵:在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星
    人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有
    外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。
    通过学习这样一个项目,我学到了pygame的运用,对之前学的类、函数、方法有了更深的认识,开始思考项目背后运行的原理。
    项目分为10个文件,9个py文件和1个json文件。
    py文件分别储存了游戏的各个要素,具体作用我会在代码的注释中详述。json文件会被 scorebroad.py 中的代码自动创建,用于储存游戏中出现的最高分。
    作为新手,免不了要出错,在写这个项目的过程我也遇到了不少问题,也向很多大神求助过,在这里感谢帮助过我的前辈。我把几个遇到的问题都写在了之前在出错的地方。
    总结一下教训:
    1.写代码的时候一定要专注,不然很多时候怎么出错都不知道。
    2.调用方法函数的时候记得加上括号,不然会报错,这是个细节问题。
    3.一定要注意缩进问题,有时候不应该的缩进会导致一些莫名其妙的问题,找代码出错的时候记得看一看缩进是否正确。
    游戏界面如图:
    python 外星人入侵游戏 学习总结 以及完整代码_第1张图片

python 外星人入侵游戏 学习总结 以及完整代码_第2张图片

项目代码如下:

settings.py

#创建一个游戏设置的类
class Settings:
    def __init__(self):
        #储存游戏初始化数据
        #初始化游戏的静态设置
        self.screen_width = 1400
        self.screen_height = 750
        self.bg_color = (200,200,200)
        #设置飞船数量
        self.ship_limit = 3
        #创建关于子弹的属性
        self.bullet_width = 15
        self.bullet_height = 30
        self.bullet_bg_color = (60,0,0)
        #设置限制子弹数量
        self.bullets_allowed = 3
        #设置外星人下降值
        self.alien_drop_factor = 2
        #设置速度提升倍数
        self.speedup_scale = 1.1
        #设置外星人点数的提高速度
        self.score_scale = 1.5
        #初始化速度值
        self.initialize_dynamic_settings()

    def initialize_dynamic_settings(self):
        #初始化动态设置
        # 设置外星人移动速度
        self.alien_speed_factor = 0.5
        # 设置外星人群移动值,向右为1,向左为-1
        self.fleet_direction = 1
        #设置外星人分数
        self.alien_points = 50
        #设置子弹移动速度
        self.bullet_speed_factor = 5
        # 设置飞船移动速度
        self.ship_speed_factor = 3

    def increase_speed(self):
        #提升速度值
        self.ship_speed_factor *= self.speedup_scale
        self.bullet_speed_factor *= self.speedup_scale
        self.alien_speed_factor*= self.speedup_scale
        #提升分数
        self.alien_points = int(self.alien_points * self.score_scale)

alien.py

#创建一个关于外星人的模块
import pygame
from pygame.sprite import Sprite
class Alien(Sprite):

    '''表示单个外星人的类'''
    def __init__(self,ai_settings,screen):
        #继承精灵类
        super().__init__()
        self.ai_settings = ai_settings
        self.screen = screen
        #返回外星人图像并矩形化
        self.image = pygame.image.load('image/humor.png')
        self.rect = self.image.get_rect()
        #每个外星人最初都在屏幕左上角附近
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height
        #储存外星人的准确位置
        self.x = float(self.rect.x)


    def blitme(self):
        '''在指定位置绘制外星人'''
        self.screen.blit(self.image,self.rect)

    def update(self):
        #外星人坐标为速度乘以方向值,当方向值为负时方向改变
        self.x += (self.ai_settings.alien_speed_factor *
                    self.ai_settings.fleet_direction)
        self.rect.x = self.x
        '''当方向值为1时,x 坐标增加,向右移动。方向值为-1时,x 坐标减少,向左移动'''
    def check_edges(self):
        #检查外星人是否碰到了边缘
        #矩形化屏幕
        screen_rect = self.screen.get_rect()
        #当外星人贴图右边缘大于或等于屏幕矩形右边缘,返回正确
        if self.rect.right >= screen_rect.right:
            return True
        #当外星人贴图左边缘小于或等于屏幕矩形左边缘(小于等于0),返回正确
        elif self.rect.left <= screen_rect.left:
            return True

bullet.py

#创建子弹类
import pygame
from pygame.sprite import Sprite
class Bullet(Sprite):
    '''一个对飞船发射的子弹进行管理的类'''
    def __init__(self,ai_settings,screen,ship):
        #通过函数super()来继承sprite
        super().__init__()
        self.screen = screen
        #在(0,0)处创建一个代表子弹的矩形,再设置正确的位置,参数为初识坐标,子弹的宽和高
        self.rect = pygame.Rect(0,0,ai_settings.bullet_width,ai_settings.bullet_height)
        #根据飞船位置来确定子弹位置
        self.rect.centerx = ship.rect.centerx
        self.rect.top = ship.rect.top
        #储存用小数表示的子弹位置
        '''由于子弹是直线向下移动,所以我们只考虑在y轴上坐标的变化,
        而x轴的坐标是固定的,这由发射子弹时飞船的x轴坐标决定,
        因为我们在前面定义了self.rect.centerx = ship.rect.centerx'''
        self.y = float(self.rect.y)
        #储存子弹颜色
        self.color = ai_settings.bullet_bg_color
        #储存速度
        self.speed_factor = ai_settings.bullet_speed_factor
    def update(self):
        #让子弹向上移动
        self.y -= self.speed_factor
        #更新子弹rect的位置
        self.rect.y = self.y
    def draw_bullet(self):
        #在屏幕上绘制子弹
        pygame.draw.rect(self.screen,self.color,self.rect)

ship.py

import pygame
from pygame.sprite import Sprite
class Ship(Sprite):
    def __init__(self,ai_settings,screen):
        '''初始化飞船并设置其初始位置'''
        #继承精灵
        super().__init__()
        self.screen = screen
        self.ai_settings = ai_settings
        #加载飞船图像,并获取其外接矩形
        #load返回贴图所在文件
        self.image = pygame.image.load('image/shot.PNG')
        #矩形属性rect为贴图的矩形对象
        #get_rect()是一个处理矩形图像的方法,返回值包含矩形的居中属性( center centerx centery )
        #get_rect须加上(),否则后续程序在运行时会出现无法引用centerx和bottom的错误
        self.rect = self.image.get_rect()
        #先将表示屏幕的矩形储存在属性screen—rect中
        self.screen_rect = screen.get_rect()
        '''将每艘新飞船放在屏幕底部中央'''
        #再将self.rect.centerx设置为表示屏幕的矩形属性centerx
        #再将self.rect.bottom设置为表示屏幕的矩形属性bottom
        #后面使用这个rect属性来放置飞船在屏幕底部中间
        self.rect.centerx = self.screen_rect.centerx
        self.rect.bottom = self.screen_rect.bottom
        #在飞船属性center中储存小数点
        self.center = float(self.rect.centerx)
        #添加属性,设置一个飞船向右移动的标志
        self.moving_right = False
        #添加一个飞船向左移动的标志
        self.moving_left = False
    def blitme(self):
        #方法blit 位块传输 将指定的像素内容传输到指定位置
        self.screen.blit(self.image,self.rect)
    def update(self):
        #根据标志来向右移动飞船
        #更新飞船的center值而不是rect(rect已被浮点化储存到center中)
        '''添加条件,防止飞船移动到屏幕外'''
        #以屏幕矩形的右边界为界,当按键事件(标志正确)以及飞船外接矩形坐标小于右边界时可向右移动
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        #根据标志向左移动飞船
        #以屏幕矩形的左边界为准,即x轴坐标的0,当按键事件(标志正确)以及飞船外接矩形坐标大于0时可向左移动
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
        '''此处使用两个if来应对左右方向按键,两个条件同时检测,当同时按下左右键时,飞船纹丝不动,
        这有利于精准控制,如使用if—elif格式的话,if优先,故右键优先,同时按两个键时无法同时抵消,则向右移动'''
        #根据center来更新rect
        self.rect.centerx = self.center

    def center_ship(self):
        #让飞船底部居中
        self.center = self.screen_rect.centerx

game_stats.py

#创建游戏统计类
class GameStats():
    #跟踪游戏的统计信息
    def __init__(self,ai_settings):
        #初始化统计信息
        self.ai_setting = ai_settings
        #设置结束游戏的标志
        #修改标志,让游戏一开始处于非活动状态
        self.game_active = False
        #设置最高分,最高分不会被重置,所以在init初始化
        self.high_score = 0
        #在初始化init中调用方法reset_stats
        #在创建实例时能妥善地设置统计信息
        #在后续也可以调用
        self.reset_stats()
    def reset_stats(self):
        #重置统计
        #初始化游戏运行期间可能变化的统计信息
        #储存飞船数量
        self.ships_left = self.ai_setting.ship_limit
        #初始化得分
        self.score = 0
        #初始化等级
        self.level = 1

button.py

#创建按钮类
#导入模块ftfont(font),它能将文本渲染到屏幕上
import pygame.ftfont
class Button():
    def __init__(self,ai_settings,screen,msg):
        #初始化
        self.screen = screen
        self.screen_rect = screen.get_rect()
        #设置按钮尺寸和颜色
        self.wdith,self.height = 200,50
        self.button_color = (0,50,0)
        #指定文本颜色
        self.text_color = (255,255,255)
        #None 指定了使用默认字体渲染文本,48为字号
        self.ftfont = pygame.ftfont.SysFont(None,48)
        #创建一个rect对象
        self.rect = pygame.Rect(0,0,self.wdith,self.height)
        #rect对象位置居中
        self.rect.center = self.screen_rect.center
        #调用按钮标签(只需创建一次)
        self.prep_msg(msg)

    def prep_msg(self,msg):
        #创建渲染文本方法
        '''将msg渲染为图像,并使其在按钮上居中'''
        #将文本渲染为图像,第一个实参为文本,第二个实参为布尔值(用于指定图像边缘是否开启反锯齿功能,此处的Ture为开启)
        #第三个实参为文本颜色,第四个实参为按钮颜色(即图像的背景色,当没有指定时为透明)
        self.msg_image = self.ftfont.render(msg,True,self.text_color,self.button_color)
        #矩形化文本图像
        self.msg_image_rect = self.msg_image.get_rect()
        #文本图像位置居中,即在按钮中居中(按钮矩形的中央位置)
        self.msg_image_rect.center = self.rect.center

    def draw_button(self):
        #绘制一个用颜色填充的按钮,再绘制文本
        self.screen.fill(self.button_color,self.rect)
        self.screen.blit(self.msg_image,self.msg_image_rect)

scorebroad.py

import pygame.ftfont
from pygame.sprite import Group
from ship import Ship
#导入json用于写入最高分
import json
#创建计分板类
class Scoreboard():
    def __init__(self,screen,ai_settings,stats):
        #初始化得分涉及的属性
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.ai_settings = ai_settings
        self.stats = stats
        #设置计分板的字体
        self.text_color = (30,30,30)
        #默认字体,48字号
        self.ftfont = pygame.ftfont.SysFont(None,48)
        #准备初始得分图像和最高分图像、等级、飞船剩余图像
        self.prep_images()

    def prep_images(self):
        # 整合初始化图像内容
        self.prep_score()
        self.prep_high_score()
        self.prep_level()
        self.prep_ships()

    def prep_score(self):
        '''将得分转换为渲染的图像'''
        #  得分为得分为10的整数倍,并将逗号用作千分位分隔符
        rounded_score = int(round(self.stats.score, -1))
        score_str = "{:,}".format(rounded_score)
        self.image = self.ftfont.render(score_str,True,self.text_color,self.ai_settings.bg_color)
        #将得分放在屏幕右上角
        #将文字渲染成的图像矩形化,指定位置
        self.score_rect = self.image.get_rect()
        self.score_rect.right = self.screen_rect.right - 20
        self.score_rect.top = 20

    def prep_high_score(self):
        '''将最高得分渲染为图像'''
        #打开json文件读取最高分来圆整规范以及渲染为图像,当出现 FileNotFoundError 异常时创建json文件,并写入初始化最高分
        # (这一步仅需执行一次,也可应对文件丢失情况)

        try:
            with open('high_score.json') as f_obj:
                high_score_json = json.load(f_obj)
        except FileNotFoundError:
            with open('high_score.json','w') as f_obj:
                json.dump(self.stats.high_score,f_obj)

        # 最高得分为10的整数倍,并将逗号用作千位分隔符
        high_score = int(round(high_score_json,-1))
        high_score_str = "{:,}".format(high_score)


        self.high_score_image = self.ftfont.render(high_score_str,True,self.text_color,self.ai_settings.bg_color)
        #矩形化图像,并指定位置为屏幕顶端中央
        self.high_score_rect = self.high_score_image.get_rect()
        self.high_score_rect.centerx = self.screen_rect.centerx
        self.high_score_rect.top = self.score_rect.top

    def prep_level(self):
        '''将等级转化为渲染的图像'''
        self.level_image = self.ftfont.render(str(self.stats.level),True,self.text_color,self.ai_settings.bg_color)
        #将等级放在得分下方
        self.level_rect = self.level_image.get_rect()
        self.level_rect.right = self.score_rect.right
        self.level_rect.top = self.score_rect.bottom + 10

    def prep_ships(self):
        '''显示还剩余多少飞船'''
        self.ships = Group()
        #循环剩下的飞船数,并根据数量生成位置
        for ship_numbers in range(self.stats.ships_left):
            ship = Ship(self.ai_settings,self.screen)
            ship.rect.x = 10 + ship_numbers * (2 * ship.rect.width)
            ship.rect.y = -30
            self.ships.add(ship)




    def show_score(self):
        #在屏幕上显示得分和最高分
        self.screen.blit(self.image,self.score_rect)
        self.screen.blit(self.high_score_image,self.high_score_rect)
        self.screen.blit(self.level_image,self.level_rect)
        #绘制飞船
        self.ships.draw(self.screen)

game_functions.py

#将管理事件的代码部分转移到此模块中,以简化主程序逻辑
import sys
import pygame
#导入子弹类
from bullet import Bullet
from alien import Alien
#导入json用于记录最高分
import json
#导入sleep来实现游戏暂停
from time import sleep

def fire_bullet(ai_settings,screen,ship,bullets):
    # 创建一个子弹,并将其加入到编组中去
    # 设置条件,限制子弹数量
    #当符合条件时,按下空格发射子弹
    if len(bullets) < ai_settings.bullets_allowed:
        new_bullet = Bullet(ai_settings, screen, ship)
        bullets.add(new_bullet)

def check_keydown_events(event,ai_settings,screen,ship,bullets,stats,aliens,sb):
    #响应按键
    if event.key == pygame.K_RIGHT:
        # 当按键事件为按下右箭头时,向右移动标志为正确
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:
        # 当按键事件为按下左箭头时,向左移动标志为正确
        ship.moving_left = True
        #当按下的键为z时
    elif event.key == pygame.K_z:
        #简化keydown,当按下空格时调用函数fire_bullets检查条件发射子弹
        fire_bullet(ai_settings,screen,ship,bullets)
        #设置快捷键q用于退出游戏
    elif event.key == pygame.K_q:
        sys.exit()
        #设置快捷键p用于重置游戏
    elif event.key == pygame.K_p:
        start_game(stats,aliens,bullets,ai_settings,screen,ship,sb)

def check_keyup_events(event,ship):
    #响应松开
    if event.key == pygame.K_RIGHT:
        # 当按键事件为松开按右箭头时,向右移动标志为错误
        ship.moving_right = False
    elif event.key == pygame.K_LEFT:
        # 当按键事件为松开按左箭头时,向左移动标志为错误
        ship.moving_left = False


def check_events(ai_settinngs,ship,screen,bullets,stats,play_button,aliens,sb):
    '''响应按键和鼠标事件'''
    for event in pygame.event.get():
        '''当事件类型为QUIT时,退出游戏'''
        if event.type == pygame.QUIT:
            sys.exit()
        #检测鼠标事件
        elif event.type == pygame.MOUSEBUTTONDOWN:
            #将鼠标点击区域坐标储存到变量中
            mouse_x,mouse_y = pygame.mouse.get_pos()
            check_play_button(stats,play_button,mouse_x,mouse_y,aliens,bullets,ai_settinngs,screen,ship,sb)
       #更新后的check_event直接使用前面定义的函数
        elif event.type == pygame.KEYDOWN:
            #调用时增加形参
            check_keydown_events(event,ai_settinngs,screen,ship,bullets,stats,aliens,sb)
        elif event.type == pygame.KEYUP:
            check_keyup_events(event,ship)
            '''在 KEYDOWN 和 KEYUP 中使用 if-elif 结构处理两种按键标志,因为每个事件只与一个键关联,
            当同时按下左右两键时,即视为两个事件来处理'''


def check_play_button(stats,play_button,mouse_x,mouse_y,aliens,bullets,ai_settings,screen,ship,sb):
    '''在玩家单击play按钮时开始新游戏'''
    #将按钮点击检测储存到变量中
    button_clicked = play_button.rect.collidepoint(mouse_x,mouse_y)
    #当按钮被点击且游戏非运行状态时,按钮点击生效
    if button_clicked:
        #调用重置游戏方法
        start_game(stats,aliens,bullets,ai_settings,screen,ship,sb)

def start_game(stats,aliens,bullets,ai_settings,screen,ship,sb):
    '''创建重置游戏的方法'''
    if not stats.game_active :
        # 隐藏光标
        # 向set_visible()传递参数False来隐藏光标
        pygame.mouse.set_visible(False)
        #重置速度
        ai_settings.initialize_dynamic_settings()
        # 重置统计信息
        stats.reset_stats()
        # 游戏运行标志正确
        stats.game_active = True
        #重置记分牌中的图像
        sb.prep_images()
        # 清空子弹和外星人
        aliens.empty()
        bullets.empty()
        # 创建新外星人群
        create_fleet(ai_settings, screen, ship, aliens)
        # 飞船居中
        ship.center_ship()

def update_bullets(bullets,aliens,ai_settings,screen,ship,stats,sb):
    # 对编组中的每一个子弹调用update
    #更新子弹位置
    bullets.update()
    # 删除已消失的子弹
    # 在for循环中不应从列表或编组删除条目,所以使用方法copy()遍历编组的副本,然后修改编组
    # 遍历副本的原因:由于编组内的子弹是动态变化的,遍历副本后根据副本元素来删除编组中对应的元素,做到精准删除
    # 注意:这里删除的是原编组里的子弹!
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
    #调用检测外星人子弹碰撞函数
    check_bullet_alien_collisions(bullets,aliens,ai_settings,screen,ship,stats,sb)

def check_bullet_alien_collisions(bullets,aliens,ai_settings,screen,ship,stats,sb):
    #检查是否有子弹击中了外星人
    #如果子弹击中外星人,则删除该子弹和外星人
    collisions = pygame.sprite.groupcollide(bullets,aliens,True,True)
    #当发生碰撞时加分
    if collisions :
        for aliens in collisions.values():
            stats.score += ai_settings.alien_points * len(aliens)
            sb.prep_score()
            #检测是否出现新的最高分
            check_high_score(stats,sb)

    #当外星人团灭时,调用提升等级函数做出相应变化
    if len(aliens) == 0:
        start_new_level(bullets,ai_settings,stats,sb,screen,ship,aliens)

def start_new_level(bullets,ai_settings,stats,sb,screen,ship,aliens):
    # 清空子弹
    bullets.empty()
    # 提升外星人群速度
    ai_settings.increase_speed()
    # 提升等级
    stats.level += 1
    sb.prep_level()
    # 创建外星人
    create_fleet(ai_settings, screen, ship, aliens)

def check_high_score(stats,sb):
    '''检测是否诞生了新的最高分'''
    #读取json中的最高分,将最高分储存在变量,将当前分数与最高分比较
    #当当前分数高于最高分时,将当前最高分储存到新变量,并将新变量写入json文件
    #调用更新最高分函数,显示新的最高分
    with open('high_score.json') as f_obj:
        high_score = json.load(f_obj)
    if stats.score > high_score :
        new_high_score = stats.score
        with open('high_score.json','w') as f_obj :
            json.dump(new_high_score,f_obj)
    sb.prep_high_score()

def get_number_aliens_x(ai_settings,alien_width):
    '''整理函数,增加条理性'''
    '''此函数返回每行可容下的外星人数量'''
    # 按照公式来计算行的有效屏幕空间,需要与边界保持间距,间距为一个外星人矩形的宽,故行有效屏幕空间为屏宽减去两个外星人宽
    # 求一行容纳外星人数量:两个外星人之间需要保留一个外星人宽的间距,即一个外星人需要的宽为两倍外星人矩形宽
    # 那么行容纳外星人数量 = 行有效屏幕空间/两倍外星人矩形宽
    available_space_x = ai_settings.screen_width - 2 * alien_width  # 行外星人可用屏幕容量
    number_aliens_x = int(available_space_x / (2 * alien_width))  # 行容纳外星人数量
    '''这里使用了int来求得整数数量的外星人,舍弃了小部分,防止出现不完整外星人,同时也出于生成的目的,
       函数range()需要一个整数值'''
    return number_aliens_x

def get_number_rows(ai_settings, ship_height, alien_height):
    """计算屏幕可容纳多少行外星人"""
    # 屏幕垂直可用空间为屏幕高度减去第一行外星人高度和飞船高度,以及减去两倍外星人高度作为最底下一行外星人与飞船的距离
    available_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
    # 可容纳行数为屏幕垂直可用空间除以二倍外星人高度(外星人行与行之间保持一个外星人高度的间距)
    #公式中注意计算时的括号问题
    number_rows = int(available_space_y / (2 * alien_height))
    # 返回可容纳行数
    return number_rows

def create_alien(ai_settings, screen, aliens, alien_number, row_number):
    '''创建单个外星人并将其放在行中'''
    # 先创建单个外星人
    # 外星人间距为外星人宽度
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    # 外星人位置从左算起,空掉一个外星人矩形作为间隔,每个外星人占据两个外星人矩形大小,
    # x 轴排布位置由for循环的外星人次序决定,故 * number_aliens_x
    alien.x = alien_width + 2 * alien_width * alien_number
    # 储存外星人坐标
    alien.rect.x = alien.x
    # 外星人行的位置需空出一行作为与顶的边界,同时行与行之间空出一个外星人高度作为间距
    # y 轴排布位置由for循环的行数次序决定,故 * row_number
    alien.rect.y = 30 + alien.rect.height + 2 * alien.rect.height * row_number
    # 将外星人添加到编组
    aliens.add(alien)

def create_fleet(ai_settings, screen, ship,aliens):
    '''创建外星人群'''
    # 创建一个外星人,计算每行外星人容量
    alien = Alien(ai_settings, screen)
    # 调用前面计算每行容量的函数,储存在变量中
    number_aliens_x = get_number_aliens_x(ai_settings, alien.rect.width)
    number_rows = get_number_rows(ai_settings, ship.rect.height,alien.rect.height)
    # 创建外星人群
    #遍历可用行空间和行数量,创造出相应数量的一群外星人
    for row_number in range(number_rows):
        for alien_number in range(number_aliens_x):
            create_alien(ai_settings, screen, aliens, alien_number,row_number)


#增加了子弹和外星人形参
def update_screen(ai_settings,ship,screen,aliens,bullets,stats,play_button,sb):
    '''设置、飞船、屏幕为三个实参,在方法中使用'''
    #屏幕颜色填充为设置中设置的颜色
    screen.fill(ai_settings.bg_color)
    #在飞船和外星人后面重绘所有子弹
    #在背景设置完毕后将飞船绘制到指定位置
    ship.blitme()
    #绘制外星人编组中的每一个
    aliens.draw(screen)
    '''遍历编组中的每一个精灵(子弹),并对每一个精灵(子弹)调用draw_bullte'''
    # 这里使用了方法bullets.sprites  它返回子弹编组  注意sprites是复数
    for bullet in bullets.sprites():
        bullet.draw_bullet()
    #显示得分
    sb.show_score()
    #让最近绘制的屏幕可见
    #当游戏处于非活动状态,绘制按钮
    if not stats.game_active :
        play_button.draw_button()
    #让最近绘制的屏幕可见
    pygame.display.flip()

def check_fleet_edges(ai_settings,aliens):
    #遍历每一个外星人,当触碰边缘时调用改变方向函数
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings,aliens)
            '''由于之前这里忘记break退出循环,外星人群有时会直接下落导致游戏结束'''
            break

def change_fleet_direction(ai_settings,aliens):
    #遍历每一个外星人,调整每一个外星人的 y 坐标,即下降
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.alien_drop_factor
    #改变方向值
    ai_settings.fleet_direction *= -1

def check_alien_bottom(ai_settings,ship,aliens,stats,bullets,screen,sb):
    #检查外星人是否触底
    #注意!!!
    #get_rect()要记得加上括号,不然会出现如下错误:
    # AttributeError: 'builtin_function_or_method' object has no attribute 'bottom'
    screen_rect = screen.get_rect()
    #遍历外星人编组
    for alien in aliens.sprites():
        #当有外星人触底时,调用ship_hit函数更新外星人和飞船
        if alien.rect.bottom >= screen_rect.bottom:
            ship_hit(stats,aliens,bullets,ai_settings,screen,ship,sb)
            break

def update_aliens(ai_settings,ship,aliens,stats,bullets,screen,sb):
    '''检查外星人是否触碰边缘,做出调整'''
    check_fleet_edges(ai_settings,aliens)
    # 对每个外星人的位置更新
    aliens.update()
    #检查外星人与飞船的碰撞,当碰撞时发出信号(已删去)
    #在碰撞时调用ship_hit,做出相应变化
    if pygame.sprite.spritecollideany(ship,aliens):
        ship_hit(stats,aliens,bullets,ai_settings,screen,ship,sb)
    #调用函数检查是否有外星人触底
    check_alien_bottom(ai_settings,ship,aliens,stats,bullets,screen,sb)


def ship_hit(stats,aliens,bullets,ai_settings,screen,ship,sb):
    #检查飞船数量是否用完,若未用完,暂停后更新飞船和外星人;若用完,标志False,没有更新,游戏结束
    if stats.ships_left > 0 :
        #响应被外星人撞到的飞船
        #飞船数量相应减一
        stats.ships_left -= 1
        #更新飞船剩余
        sb.prep_ships()
        #清空外星人和子弹列表
        aliens.empty()
        bullets.empty()
        #创建一群新的外星人
        create_fleet(ai_settings,screen,ship,aliens)
        #让飞船底部居中
        ship.center_ship
        #暂停0.5秒
        sleep(0.5)
    elif stats.ships_left == 0:
        #当飞船数量用完,标志错误,不执行更新
        stats.game_active = False
        #在游戏处于非运行状态时,光标出现,可以点击按钮继续游戏
        pygame.mouse.set_visible(True)

alien_invasion.py

#游戏主程序
#模块sys可用于退出游戏,因为模块game_functions中已经导入sys,主程序中不需要再导入
import pygame
from settings import Settings
from ship import Ship
#导入信息统计类
from game_stats import GameStats
from scoreboard import Scoreboard
#导入编组
from pygame.sprite import Group
#由于不在主程序创建外星人实例,此处不必导入
#导入game_functions,后面的事件管理部分从模块导入
import game_functions as gf
#导入按钮类
from button import Button
def run_game():
    #初始化游戏并创建一个屏幕
    pygame.init()
    ai_settings = Settings()
    screen = pygame.display.set_mode((ai_settings.screen_width,ai_settings.screen_height))
    #设置屏幕对象
    pygame.display.set_caption("alien_invision")
    #创建按钮类 形参msg对应的实参为“play”
    play_button = Button(ai_settings,screen,"Play")
    #创建一个用于储存游戏信息统计的实例
    stats = GameStats(ai_settings)
    #创建记分牌实例
    sb = Scoreboard(screen,ai_settings,stats)
    #创建一艘飞船
    ship = Ship(ai_settings,screen)
    #创建一个子弹编组
    bullets = Group()
    #创建一个外星人编组
    aliens = Group()
    # 使用gf模块中的函数绘制外星人群
    gf.create_fleet(ai_settings,screen,ship,aliens)
    #开始游戏主循环
    while True:
        #使用模块game_funcations中的方法
        #check_events检测按键和鼠标点击事件
        gf.check_events(ai_settings,ship,screen,bullets,stats,play_button,aliens,sb)
        #当标志正确时运行游戏:
        if stats.game_active :
            #方法update()用于确定事件标志以及移动距离和方向
            ship.update()
            #使用更新后的gf的函数用于更新和删除子弹
            #检测外星人与子弹的碰撞,删除外星人与子弹  并创建外星人群
            gf.update_bullets(bullets,aliens,ai_settings,screen,ship,stats,sb )
            #由于外星人会被子弹击中,所以在绘制子弹之后绘制外星人
            gf.update_aliens(ai_settings,ship,aliens,stats,bullets,screen,sb)
        #方法update_screen用于更新绘制屏幕各元素
        gf.update_screen(ai_settings,ship,screen,aliens,bullets,stats,play_button,sb)
#运行游戏
run_game()

你可能感兴趣的:(笔记,心得,新手)