Bootstrap

《python编程:从入门到实践》游戏一:外星人射击,代码详解及效果

效果展示

在这里插入图片描述
在这里插入图片描述

代码部分

alien.py

"""
在游戏《外星人入侵》中,玩家控制着一艘最初出现在屏幕底部中央的飞船。玩家可以使用箭头键左右移动飞船,还可使用空格键进行射击。游戏开始时,一群外星
人出现在天空中,他们在屏幕中向下移动。玩家的任务是射杀这些外星人。玩家将所有外星人都消灭干净后,将出现一群新的外星人,他们移动的速度更快。只要有
外星人撞到了玩家的飞船或到达了屏幕底部,玩家就损失一艘飞船。玩家损失三艘飞船后,游戏结束。
"""
"""主程序,游戏开始的位置"""
from setting import *
from ship import *
import game_function as gf
from pygame.sprite import Group
from game_stats import GameStats
from button import Button


def run_game():
    pygame.init()  # 初始化背景
    ai_settings = Settings()  # 初始化设置实例
    screens = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))  # 设置背景窗口大小
    pygame.display.set_caption("Alien Invasion")  # 设置窗口标题

    # 实例
    play_button = Button(screens, "PLAY")  # PLAY按钮
    stats = GameStats(ai_settings)  # 游戏状态
    ship = Ship(ai_settings, screens)  # 飞船的实例
    bullets = Group()  # 创建子弹组
    aliens = Group()  # 创建外星人组
    gf.creat_fleet(ai_settings, screens, ship, aliens)  # 创建外星人大队

    while True:  # 游戏开始主循环
        gf.check_events(ai_settings, screens, stats, play_button, ship, aliens, bullets)  # 游戏的按键鼠标的监听事件
        if stats.game_active:  # 如果游戏的状态为真(即运行的情况下)的情况下
            ship.update()  # 刷新船只
            gf.update_bullets(ai_settings, screens, ship, aliens, bullets)  # 刷新子弹
            gf.update_aliens(ai_settings, stats, screens, ship, aliens, bullets)  # 刷新外星人
        gf.update_screen(ai_settings, screens, stats, ship, aliens, bullets, play_button)  # 屏幕刷新


run_game()

settings.py

"""游戏的初始化设置,各种初始数据存放处"""
class Settings(object):  # 存储《外星人入侵》的所有设置
    def __init__(self):  # 初始化游戏设置
        # 屏幕
        self.screen_width = 1000
        self.screen_height = 600
        self.bg_color = (230, 230, 230)

        # 飞船的数量初始值(即有三条命)
        self.ship_limit = 3

        # 子弹的样子
        self.bullet_width = 3
        self.bullet_height = 15
        self.bullet_color = 60, 60, 60
        self.bullet_allowed = 20#一个屏幕内允许的子弹数

        self.fleet_drop_speed = 10  # 外星人下坠的速度
        self.speedup_scale = 1.5  # 每经过一轮之后,速度都增加1.5倍
        self.initialize_dynamic_settings()  # 初始速度

    def initialize_dynamic_settings(self):
        self.ship_speed_factor = 1.5  # 飞船的速度
        self.bullet_speed_factor = 3  # 子弹的速度
        self.alien_speed_factor = 1  # 外星人的速度
        self.fleet_direction = 1  # 控制外星人的方向,1表示向右,-1表示向左

    def increase_speed(self):
        # 把子弹、外星人和飞船的移动速度提升到1.5倍
        self.ship_speed_factor *= self.speedup_scale
        self.bullet_speed_factor *= self.speedup_scale
        self.alien_speed_factor *= self.speedup_scale

bullet.py

"""存放子弹的基本信息"""
import pygame
from pygame.sprite import Sprite


class Bullet(Sprite):
    def __init__(self, ai_settings, screen, ship):
        super(Bullet, self).__init__()
        self.screen = screen

        self.rect = pygame.Rect(0, 0, ai_settings.bullet_width, ai_settings.bullet_height)  # 创建子弹的rect对象
        self.rect.centerx = ship.rect.centerx  # 子弹的横坐标的位置相对飞船居中
        self.rect.top = ship.rect.top  # 设置子弹从飞船头上发射,两者顶部重合
        self.y = float(self.rect.y)  # 纵坐标转为浮点数

        self.color = ai_settings.bullet_color  # 读取settings中设置了的颜色
        self.speed_factor = ai_settings.bullet_speed_factor  # 读取settings中设置的速度

    def update(self):
        self.y -= self.speed_factor  # 屏幕x轴在上面,所以子弹向上移动是用减的
        self.rect.y = self.y

    def draw_bullet(self):
        pygame.draw.rect(self.screen, self.color, self.rect)  # 绘画子弹

ship.py

"""存放飞船的相关数据"""
import pygame


class Ship(object):
    def __init__(self, ai_settings, screen):
        # 设置飞船的初始值
        self.screen = screen
        self.ai_settings = ai_settings

        self.image = pygame.image.load('images/ship.png')  # 加载飞船
        self.rect = self.image.get_rect()  # 获得的rect对象
        self.screen_rect = screen.get_rect()  # 获得屏幕的rect对象
        self.rect.centerx = self.screen_rect.centerx  # 飞船横坐标初始位置居中
        self.rect.bottom = self.screen_rect.bottom  # 飞船底部与飞机底部重合
        self.center = float(self.rect.centerx)  # 转成浮点数

        # 左右移动的标志,true为可以移动
        self.moving_right = False
        self.moving_left = False

    def update(self):
        # 向右移动的标志为true且实际位置不超过右边缘时,就向右移动,speed为速度,settings中定义了
        if self.moving_right and self.rect.right < self.screen_rect.right:
            self.center += self.ai_settings.ship_speed_factor
        # 向左移动的标志为true且实际位置不超过左边缘时,就向左移动,speed为速度,settings中定义了
        if self.moving_left and self.rect.left > 0:
            self.center -= self.ai_settings.ship_speed_factor
        self.rect.centerx = self.center

    def blitme(self):
        self.screen.blit(self.image, self.rect)  # 将图像移动到屏幕上

    def center_ship(self):
        self.center = self.screen_rect.centerx  # 把飞船放回屏幕中间

alien.py

"""外星人的相关数据"""
import pygame
from pygame.sprite import Sprite


class Alien(Sprite):
    def __init__(self, ai_settings, screen):
        super(Alien, self).__init__()  # 继承pygame中的Sprite
        self.screen = screen
        self.ai_settings = ai_settings

        self.image = pygame.image.load('images/alien.png')  # 加载外星人的图像
        self.rect = self.image.get_rect()  # 获取该图像的rect对象
        # 设置图像的位置(即x,y值)
        self.rect.x = self.rect.width  # 设置图像的横坐标为图像的宽
        self.rect.y = self.rect.height  # 设置图像的纵坐标为图像的高(即将图像放在屏幕左上角(0,0)点)
        self.x = float(self.rect.x)  # rect对应值只能为整数,这里将其转为字符串

    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)  # 图像的横坐标等于速度乘以方向,方向1为右,-1为左
        self.rect.x = self.x

    def check_edges(self):
        # 判断是否碰到屏幕边缘,碰到就改变方向
        screen_rect = self.screen.get_rect()  # 获取屏幕的rect对象
        if self.rect.right >= screen_rect.right:
            return True  # 图像的右边缘超出了屏幕右边缘返回True
        elif self.rect.left <= 0:
            return True  # 图像的左边缘小于0返回True,因为屏幕远点在左上角,左边缘为0

button.py

"""PLAY按钮的相关数据"""
import pygame.font  # font可以将文本渲染到屏幕上


class Button():
    def __init__(self, screen, msg):  # 读取屏幕信息和要显示的文本
        self.screen = screen
        self.screen_rect = screen.get_rect()  # 读取的屏幕rect()对象,以便设置按钮在屏幕中的位置
        self.width, self.height = 200, 50  # 设置按钮的大小
        self.button_color = (0, 255, 0)  # 设置按钮的颜色
        self.text_color = (255, 255, 255)  # 设置按钮中文本的颜色
        self.font = pygame.font.SysFont(None, 48)  # 从pygame的font字体库中寻找字体,类型为默认类型,字体大小为48
        self.rect = pygame.Rect(0, 0, self.width, self.height)  # 创建这个按钮的rect对象
        self.rect.center = self.screen_rect.center  # 将按钮元素的中心点和屏幕元素的中心点重合(即居中)
        self.prep_msg(msg)  # 写入文本(即PLAY)

    def prep_msg(self, msg):
        self.msg_image = self.font.render(msg, True, self.text_color,
                                          self.button_color)  # 创建一个新的表面,并写入msg文本(类似图层的概念,创建一个新的带文字的图层)
        self.msg_image_rect = self.msg_image.get_rect()  # 获得该‘图层’的rect对象
        self.msg_image_rect.center = self.rect.center  # 将该rect对象中心点和按钮的中心点重合(即居中)

    def draw_button(self):
        self.screen.fill(self.button_color, self.rect)  # 把整个按钮填充到屏幕上
        self.screen.blit(self.msg_image, self.msg_image_rect)  # 把刚才那个带文本的‘图层’移到屏幕上

game_stats.py

"""游戏状态检测"""
class GameStats():
    def __init__(self, ai_settings):
        self.ai_settings = ai_settings  # 读取游戏的相关设置
        self.reset_stats()  # 重置飞船的数量
        self.game_active = False  # 开始游戏前,游戏的状态设为False(即不运行)

    def reset_stats(self):
        self.ships_left = self.ai_settings.ship_limit  # 重置飞船的数量,让飞船的剩余量等于初始值

game_function.py

import sys
import pygame
from bullet import Bullet
from alien import Alien
from time import sleep


def check_events(ai_settings, screens, stats, play_button, ship, aliens, bullets):
    for event in pygame.event.get():  # 监听鼠标键盘事件
        if event.type == pygame.QUIT:  # 如果监听到QUIT信号
            sys.exit()  # sys来退出游戏
        # 监听到鼠标点击开始按钮
        elif event.type == pygame.MOUSEBUTTONDOWN:
            mouse_x, mouse_y = pygame.mouse.get_pos()
            check_play_button(ai_settings, screens, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y)
        # 监听到键盘按下
        elif event.type == pygame.KEYDOWN:
            check_keydown_events(event, ai_settings, screens, ship, bullets)
        # 监听到键盘释放
        elif event.type == pygame.KEYUP:
            check_keyup_events(event, ship)


def check_play_button(ai_settings, screen, stats, play_button, ship, aliens, bullets, mouse_x, mouse_y):
    # PYAY按钮相关操作
    button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)  # 检测是否有鼠标左键和右键的操作
    if button_clicked and not stats.game_active:  # (鼠标点击或游戏状态为False(即不运行的状态),就启动游戏)
        pygame.mouse.set_visible(False)  # 隐藏鼠标
        ai_settings.initialize_dynamic_settings()  # 初始化各部门的速度
        stats.reset_stats()  # 重置飞船的数量
        stats.game_active = True  # 游戏状态设置为True(即运行中)
        aliens.empty()
        bullets.empty()  # 清空上轮游戏外星人和子弹在后台存的数据
        creat_fleet(ai_settings, screen, ship, aliens)  # 重新创建外星人大队
        ship.center_ship()  # 初始化飞船的位置


def check_keydown_events(event, ai_settings, screens, ship, bullets):
    if event.key == pygame.K_RIGHT:  # 键盘右键按下时右移
        ship.moving_right = True
    elif event.key == pygame.K_LEFT:  # 键盘左键按下时左移
        ship.moving_left = True
    elif event.key == pygame.K_SPACE:  # 空格键按下时
        fire_bullet(ai_settings, screens, ship, bullets)  # 开火(即发射子弹)
    elif event.key == pygame.K_q:  # 键盘q建按下时退出
        sys.exit()


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 update_screen(ai_settings, screens, stats, ship, aliens, bullets, play_button):  # 更新屏幕
    screens.fill(ai_settings.bg_color)  # 每次循环都颜色填充
    for bullet in bullets.sprites():  # 在子弹组中循环每一颗子弹
        bullet.draw_bullet()  # 绘制每一颗子弹
    ship.blitme()  # 更新飞船
    aliens.draw(screens)  # 屏幕上绘制外星人
    if not stats.game_active:
        play_button.draw_button()  # 如果游戏没有运行,就绘制开始按钮
    pygame.display.flip()  # 让最近绘制的屏幕可见


def fire_bullet(ai_settings, screens, ship, bullets):
    if len(bullets) < ai_settings.bullet_allowed:
        new_bullet = Bullet(ai_settings, screens, ship)
        bullets.add(new_bullet)  # 子弹在允许的数量范围内,子弹群组添加新子弹


def update_bullets(ai_settings, screen, ship, aliens, bullets):
    bullets.update()
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)  # 如果子弹超出屏幕范围,删除子弹,否则就一直运行下去,程序运行量持续增加
    check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets)


def check_bullet_alien_collisions(ai_settings, screen, ship, aliens, bullets):
    collisions = pygame.sprite.groupcollide(bullets, aliens, True, True)  # 子弹和外星人碰撞就一起消失
    if len(aliens) == 0:
        bullets.empty()
        ai_settings.increase_speed()
        creat_fleet(ai_settings, screen, ship, aliens)  # 如果外星人打完了就重新绘制外星人群组,并加速


def creat_fleet(ai_settings, screen, ship, aliens):  # 创造外星人群组
    alien = Alien(ai_settings, screen)
    number_alien_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_alien_x):
            create_alien(ai_settings, screen, aliens, alien_number, row_number)  # 循环创建每一个外星人


def get_number_aliens_x(ai_settings, alien_width):  # 计算一行有几个外星人
    available_space_x = ai_settings.screen_width - 2 * alien_width
    number_alien_x = int(available_space_x / (2 * alien_width))
    return number_alien_x


def create_alien(ai_settings, screen, aliens, alien_number, row_number):
    # 创造外星人
    alien = Alien(ai_settings, screen)
    alien_width = alien.rect.width
    alien.x = alien_width + 2 * alien_width * alien_number
    alien.rect.x = alien.x
    alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
    aliens.add(alien)


def get_number_rows(ai_settings, ship_height, alien_height):  # 计算总共放几行外星人
    avaliable_space_y = (ai_settings.screen_height - (3 * alien_height) - ship_height)
    number_rows = int(avaliable_space_y / (2 * alien_height))
    return number_rows


def update_aliens(ai_settings, stats, screens, ship, aliens, bullets):
    check_fleet_edges(ai_settings, aliens)  # 检查是否碰到屏幕边缘
    check_aliens_bottom(ai_settings, stats, screens, ship, aliens, bullets)  # 检查是否运动到底部
    aliens.update()

    if pygame.sprite.spritecollideany(ship, aliens):
        ship_hit(ai_settings, stats, screens, ship, aliens, bullets)  # 如果碰到飞船就运行撞击后的函数


def check_fleet_edges(ai_settings, aliens):
    for alien in aliens.sprites():
        if alien.check_edges():
            change_fleet_direction(ai_settings, aliens)  # 如果碰到屏幕边缘就检查方向
            break


def change_fleet_direction(ai_settings, aliens):
    for alien in aliens.sprites():
        alien.rect.y += ai_settings.fleet_drop_speed
    ai_settings.fleet_direction *= -1  # 向下移动并改变方向


def ship_hit(ai_settings, stats, screens, ship, aliens, bullets):
    if stats.ships_left > 0:
        stats.ships_left -= 1  # 外星人撞击飞船,就少了一条命
        aliens.empty()
        bullets.empty()  # 清空屏幕的外星人和子弹从新开始
        creat_fleet(ai_settings, screens, ship, aliens)  # 创建新的外星人群组
        ship.center_ship()  # 飞船从新居中
        sleep(0.5)  # 暂停0.5秒
    else:
        stats.game_active = False
        pygame.mouse.set_visible(True)  # 如果没有飞船了(即没有命了,闯关失败),重置游戏状态并重新显示鼠标


def check_aliens_bottom(ai_settings, stats, screens, ship, aliens, bullets):
    screen_rect = screens.get_rect()
    for alien in aliens.sprites():
        if alien.rect.bottom >= screen_rect.bottom:
            ship_hit(ai_settings, stats, screens, ship, aliens, bullets)  # 如果飞船到屏幕的底部也相当于游戏失败,和飞船撞击外星人一样处理
            break

相关模块的讲解

rect

pygame用rect来储存矩形坐标(rectangular coordinates)的对象
他也可以操作游戏元素,把游戏元素当成矩形对象来操作,选取rect对象相当于选中该元素然后对其进行操作,相关函数如下:

  1. 移动类型
    pygame.Rect.copy() — 拷贝 Rect 对象
    pygame.Rect.move() — 移动 Rect 对象
    pygame.Rect.move_ip() — 原地移动 Rect 对象
    pygame.Rect.inflate() — 放大和缩小 Rect 对象的尺寸
    pygame.Rect.inflate_ip() — 原地放大和缩小 Rect 对象的尺寸
    pygame.Rect.clamp() — 将一个 Rect 对象移动到另一个 Rect 对象的中心
    pygame.Rect.clamp_ip() — 原地将一个 Rect 对象移动到另一个 Rect 对象的中心
  2. 逻辑类型
    pygame.Rect.clip() — 获取两个 Rect 对象互相重叠的部分
    pygame.Rect.union() — 将两个 Rect 对象合并
    pygame.Rect.union_ip() — 原地将两个 Rect 对象合并
    pygame.Rect.unionall() — 将多个 Rect 对象合并
    pygame.Rect.unionall_ip() — 原地将多个 Rect 对象合并
    pygame.Rect.fit() — 按照一定的宽高比调整 Rect 对象
    pygame.Rect.normalize() — 翻转 Rect 对象(如果尺寸为负数)
  3. 检查类型
    pygame.Rect.contains() — 检测一个 Rect 对象是否完全包含在该 Rect 对象内
    pygame.Rect.collidepoint() — 检测一个点是否包含在该 Rect 对象内
    pygame.Rect.colliderect() — 检测两个 Rect 对象是否重叠
    pygame.Rect.collidelist() — 检测该 Rect 对象是否与列表中的任何一个矩形有交集
    pygame.Rect.collidelistall() — 检测该 Rect 对象与列表中的每个矩形是否有交集
    pygame.Rect.collidedict() — 检测该 Rect 对象是否与字典中的任何一个矩形有交集
    pygame.Rect.collidedictall() — 检测该 Rect 对象与字典中的每个矩形是否有交集

相关链接:
Python之Pygame.rect函数
rect模块详解

mouse模块

mouse就是用来监听鼠标事件用的
相关函数:
pygame.mouse.get_pressed() —— 获取鼠标按键的情况(是否被按下)
pygame.mouse.get_pos() —— 获取鼠标光标的位置
pygame.mouse.get_rel() —— 获取鼠标一系列的活动
pygame.mouse.set_pos() —— 设置鼠标光标的位置
pygame.mouse.set_visible() —— 隐藏或显示鼠标光标
pygame.mouse.get_focused() —— 检查程序界面是否获得鼠标焦点
pygame.mouse.set_cursor() —— 设置鼠标光标在程序内的显示图像
pygame.mouse.get_cursor() —— 获取鼠标光标在程序内的显示图像

相关链接:
mouse详解

;