Python 带你穿越星际:打造太空侵略者2D游戏

开启 Python 游戏开发之旅

在游戏发展的漫长历史长河中,《太空侵略者》无疑是一座熠熠生辉的里程碑。1978 年,日本游戏公司太东(TAITO)推出了这款街机游戏,一经问世便迅速风靡全球 ,开启了街机视频游戏的黄金时代。它不仅在商业上取得了巨大成功,总收入高达数十亿美元,更是以独特的玩法 —— 玩家操控炮台击退一波又一波外星侵略者,为后续的射击类游戏奠定了基础范式,成为了游戏史上的经典之作。

如今,我们将踏上一段奇妙的旅程,用 Python 语言来开发属于自己的《太空侵略者》2D 游戏。Python 作为一种简洁而强大的编程语言,拥有丰富的库和工具,使得游戏开发变得更加容易上手且充满乐趣。通过开发这款经典游戏,我们能够深入理解游戏开发中的诸多核心概念,像是游戏循环、事件处理、精灵(Sprite)的运用、碰撞检测以及游戏物理等。这些知识和技能,无论是对于想要踏入游戏开发领域的新手,还是渴望提升编程能力的开发者来说,都无疑是宝贵的财富。

开发前的准备工作

搭建开发环境

在开始游戏开发之前,我们需要确保开发环境已经搭建完毕。Python 是我们的主要开发语言,建议使用 Python 3.x 版本,因为它在功能和性能上都有显著提升,并且对新特性和库的支持更好。如果你的电脑上还没有安装 Python,可以前往 Python 官方网站(
https://www.python.org/downloads/ )下载并安装最新版本。在安装过程中,记得勾选 “Add Python to PATH” 选项,这样就能在命令行中方便地使用 Python 了。

安装完成后,我们还需要安装 Pygame 库。Pygame 是一个专门用于开发 2D 游戏的 Python 库,它提供了丰富的功能,包括图形绘制、声音播放、事件处理等,能大大简化游戏开发的过程。安装 Pygame 非常简单,打开命令行工具,输入以下命令:

pip install pygame

如果你的系统中同时安装了 Python 2 和 Python 3,为了确保安装的是 Python 3 版本的 Pygame 库,你可能需要使用 pip3 命令:

pip3 install pygame

安装完成后,可以通过在 Python 解释器中输入import pygame来检查 Pygame 是否安装成功。如果没有出现错误信息,那就说明 Pygame 已经成功安装,可以开始我们的游戏开发之旅了!

不同操作系统下,除了上述通用的安装方式,还可以采用一些特定方法:

  • Windows 系统:除了使用 pip 安装,还能从 Pygame 官网(https://www.pygame.org/download.shtml )下载对应版本的安装包进行安装。下载完成后,双击安装包,按照提示完成安装即可。安装完成后,在命令提示符中输入python -m pygame.examples.aliens,如果能运行 Pygame 自带的示例游戏,就证明安装成功。
  • macOS 系统:在安装 Python 时,推荐使用 Homebrew 包管理器。安装 Homebrew 后,在终端中输入brew install python,就能轻松安装 Python。安装 Pygame 时,同样使用 pip 命令。安装完成后,在终端中输入python -c "import pygame; pygame.init(); print(pygame.get_sdl_version())",若能输出 SDL 版本信息,就表示 Pygame 安装成功。
  • Linux 系统:大多数 Linux 发行版的软件源中都包含 Python 和 Pygame。以 Ubuntu 为例,在终端中输入sudo apt-get install python3 python3-pygame,就能同时安装 Python 3 和 Pygame 库。安装完成后,在终端中输入python3 -c "import pygame; pygame.init(); print(pygame.get_sdl_version())",若能输出 SDL 版本信息,就说明安装成功。

了解游戏机制

在深入代码编写之前,深入理解《太空侵略者》的游戏机制是至关重要的,它是我们构建游戏逻辑的基石。

玩家在游戏中扮演的是保卫地球的勇士,操控着一艘位于屏幕底部的飞船。通过键盘上的左右方向键,玩家可以灵活地控制飞船在水平方向上移动,躲避敌人的攻击。当按下空格键时,飞船会发射出子弹,向屏幕上方飞去,旨在消灭来袭的外星敌人。这里的飞船移动和子弹发射,涉及到游戏中的输入处理和对象移动的逻辑。例如,在 Python 代码中,我们可以通过监听键盘事件来获取玩家的输入,然后根据输入来更新飞船的位置和发射子弹的操作。

敌人以整齐的队列出现在屏幕的上方,它们会以一定的速度整体左右移动。当敌人触碰到屏幕的边界时,便会向下移动一行,然后改变移动方向,继续向玩家逼近。随着游戏的进行,敌人的移动速度会逐渐加快,给玩家带来更大的挑战。这就需要我们在代码中实现敌人的移动逻辑,包括水平方向和垂直方向的移动,以及移动速度的变化。同时,还需要考虑敌人的生成方式,比如一次性生成若干行和列的敌人。

子弹从飞船发射后,会沿着垂直方向向上飞行,直到离开屏幕或者击中敌人。当子弹与敌人发生碰撞时,敌人会被消灭,同时子弹也会消失,并且玩家会获得相应的分数。这里的碰撞检测是游戏开发中的一个关键部分,我们可以使用矩形碰撞检测的方法,通过判断子弹和敌人所在矩形区域是否重叠,来确定是否发生了碰撞。在代码实现中,可以利用 Pygame 库提供的 Rect 类来进行矩形碰撞检测。

计分系统是激励玩家不断挑战的重要因素。每当玩家成功消灭一个敌人,就会获得一定的分数,分数会实时显示在屏幕上,让玩家清楚地了解自己的游戏进度。而游戏结束的条件有两个:一是敌人成功到达屏幕底部,意味着地球被入侵,游戏失败;二是玩家的飞船被敌人击中,此时玩家也宣告失败。在代码中,我们需要实时监测敌人的位置和玩家飞船的状态,一旦满足游戏结束的条件,就停止游戏循环,并显示游戏结束的画面。

Python 代码实现游戏功能

初始化 Pygame 和设置基础参数

在开始编写游戏的具体逻辑之前,我们首先要初始化 Pygame 库,这是使用 Pygame 进行游戏开发的第一步。通过pygame.init()这一简单的函数调用,Pygame 库中的所有模块就会被初始化,为后续的游戏开发工作做好准备,就如同搭建舞台一样,为精彩的游戏表演奠定基础。

接下来,我们需要设置游戏窗口的各项参数。以设定游戏窗口大小为WIDTH, HEIGHT = 800, 600为例,这两个变量分别定义了窗口的宽度和高度,单位是像素。这样大小的窗口既不会过大导致资源消耗过多,也不会过小影响游戏的视觉体验,是一个比较适中的尺寸,能够适应大多数玩家的屏幕分辨率。通过screen = pygame.display.set_mode((WIDTH, HEIGHT))这行代码,我们创建了一个指定大小的游戏窗口,这个窗口就是游戏的主要展示区域,玩家将在这里与游戏进行交互。

设置游戏窗口的标题也是必不可少的一步,使用
pygame.display.set_caption("太空侵略者"),我们将游戏窗口的标题设置为 “太空侵略者”,这不仅能让玩家一眼就了解游戏的主题,还能在众多窗口中轻松识别出我们的游戏窗口。

帧率的设置对于游戏的流畅运行至关重要。帧率指的是游戏每秒显示的帧数,通常用 FPS(Frames Per Second)来表示。我们将帧率设置为FPS = 60,这意味着游戏每秒会更新 60 次画面。较高的帧率可以使游戏画面更加流畅,减少卡顿现象,为玩家提供更好的游戏体验。在实际游戏中,我们可以使用clock = pygame.time.Clock()创建一个时钟对象,然后在游戏循环中通过clock.tick(FPS)来控制游戏的帧率,确保游戏以设定的帧率运行。

颜色定义在游戏开发中也扮演着重要的角色。在我们的游戏中,可能会用到各种颜色来绘制游戏元素,比如玩家的飞船、敌人、子弹等。通过定义颜色常量,我们可以方便地在代码中使用这些颜色。例如,定义黑色BLACK = (0, 0, 0)、白色WHITE = (255, 255, 255)、绿色GREEN = (0, 255, 0)、红色RED = (255, 0, 0)、黄色YELLOW = (255, 255, 0)等。这些颜色常量的定义遵循 RGB 颜色模式,每个颜色由三个值组成,分别表示红色、绿色和蓝色的分量,取值范围是 0 到 255。通过这种方式,我们可以精确地控制游戏中各种元素的颜色,使游戏画面更加丰富多彩。

创建游戏角色类

在游戏中,玩家、敌人和子弹是最基本的游戏角色,我们通过创建类来定义它们的属性和行为。

玩家类

class Player:
    def __init__(self):
        self.width = 50
        self.height = 50
        self.x = (WIDTH - self.width) // 2
        self.y = HEIGHT - 80
        self.speed = 5
        self.color = GREEN

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, (self.x, self.y, self.width, self.height))

    def move_left(self):
        self.x -= self.speed
        if self.x < 0:
            self.x = 0

    def move_right(self):
        self.x += self.speed
        if self.x + self.width > WIDTH:
            self.x = WIDTH - self.width

    def get_rect(self):
        return pygame.Rect(self.x, self.y, self.width, self.height)


玩家类代表游戏中的玩家角色,在__init__初始化方法中,定义了玩家的宽度、高度、初始位置、移动速度和颜色等属性。玩家的初始位置设置在屏幕底部的中央,通过(WIDTH - self.width) // 2计算得到水平方向的中心位置,HEIGHT - 80确定垂直方向的位置,这样能保证玩家在游戏开始时处于一个较为合适的位置,方便玩家操作。移动速度设置为 5,这个速度既不会太快让玩家难以控制,也不会太慢影响游戏节奏。颜色设置为绿色,使玩家的飞船在屏幕上更加醒目,易于区分。

draw方法用于将玩家绘制到游戏窗口上,它使用pygame.draw.rect函数,根据玩家的位置、大小和颜色绘制一个矩形,代表玩家的飞船。move_left和move_right方法实现了玩家的左右移动功能,在移动过程中,通过判断玩家的位置是否超出屏幕边界,来确保玩家始终在屏幕范围内移动,避免出现越界的情况。get_rect方法返回玩家的矩形区域,这个矩形区域在后续的碰撞检测中起着重要作用,通过比较不同矩形区域是否重叠,来判断是否发生碰撞。

敌人类

class Enemy:
    def __init__(self, x, y):
        self.width = 40
        self.height = 40
        self.x = x
        self.y = y
        self.speed_x = 2
        self.speed_y = 20
        self.color = RED

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, (self.x, self.y, self.width, self.height))

    def update(self, enemies):
        self.x += self.speed_x
        if self.x < 0 or self.x + self.width > WIDTH:
            for e in enemies:
                e.speed_x = -e.speed_x
                e.y += e.speed_y
            return

    def get_rect(self):
        return pygame.Rect(self.x, self.y, self.width, self.height)


敌人类表示游戏中的敌人,在初始化时,除了定义敌人的基本属性如宽度、高度、颜色外,还设置了敌人的水平移动速度speed_x和垂直移动速度speed_y。敌人的初始位置由传入的x和y坐标决定,这样可以方便地在不同位置生成敌人。

draw方法与玩家类中的类似,用于将敌人绘制到屏幕上。update方法是敌人类的核心方法之一,它实现了敌人的移动逻辑。敌人会以speed_x的速度在水平方向上移动,当敌人触碰到屏幕的左边界(self.x < 0)或右边界(self.x + self.width > WIDTH)时,所有敌人的水平移动方向会发生改变(e.speed_x = -e.speed_x),并且整体向下移动speed_y的距离(e.y += e.speed_y),通过这种方式模拟敌人的移动行为,增加游戏的挑战性。get_rect方法同样返回敌人的矩形区域,用于碰撞检测。

子弹类

class Bullet:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = 3
        self.speed = 10
        self.color = YELLOW

    def update(self):
        self.y -= self.speed

    def draw(self, surface):
        pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), self.radius)

    def get_rect(self):
        return pygame.Rect(self.x - self.radius, self.y - self.radius, self.radius * 2, self.radius * 2)


子弹类用于表示玩家发射的子弹,初始化时定义了子弹的位置、半径、速度和颜色。子弹的初始位置由玩家发射时的位置决定,速度设置为 10,这个速度相对较快,能让玩家感受到射击的及时性和流畅性。颜色设置为黄色,与其他游戏元素形成鲜明对比,便于玩家观察。

update方法实现了子弹的向上移动,通过不断减少y坐标的值,使子弹在屏幕上向上飞行。draw方法使用pygame.draw.circle函数绘制一个圆形的子弹,根据子弹的位置、半径和颜色进行绘制。get_rect方法返回子弹的矩形区域,虽然子弹在绘制时是圆形,但在碰撞检测中,使用矩形区域可以简化计算,提高检测效率。

实现游戏核心逻辑

游戏的核心逻辑主要包含在游戏循环中,这个循环就像是游戏的 “心脏”,不断地跳动,驱动着游戏的运行。在每一次循环中,游戏都会处理各种事件、更新游戏元素的状态、进行碰撞检测,并将最新的游戏画面绘制到屏幕上。

处理游戏事件

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        running = False
    elif event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            bullet_x = player.x + player.width // 2
            bullet_y = player.y
            bullets.append(Bullet(bullet_x, bullet_y))


keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
    player.move_left()
if keys[pygame.K_RIGHT]:
    player.move_right()


在游戏循环中,首先要处理玩家的输入事件。通过pygame.event.get()获取所有的事件,然后对不同类型的事件进行处理。当检测到pygame.QUIT事件时,意味着玩家点击了关闭窗口按钮,此时将running变量设置为False,从而退出游戏循环,结束游戏。

当检测到pygame.KEYDOWN事件,即有按键被按下时,判断按下的键是否为空格键(event.key == pygame.K_SPACE)。如果是空格键,就获取玩家当前的位置,计算出子弹的发射位置(bullet_x = player.x + player.width // 2,bullet_y = player.y),然后创建一个新的子弹对象并添加到bullets列表中,实现玩家发射子弹的功能。

此外,还通过pygame.key.get_pressed()获取当前所有被按下的键的状态,判断左右方向键是否被按下。如果左方向键(pygame.K_LEFT)被按下,调用玩家对象的move_left方法,使玩家向左移动;如果右方向键(pygame.K_RIGHT)被按下,调用move_right方法,使玩家向右移动,以此实现玩家对飞船的控制。

更新游戏元素状态

for b in bullets[:]:
    b.update()
    if b.y - b.radius < 0:
        bullets.remove(b)


for e in enemies:
    e.update(enemies)


在每一次游戏循环中,都需要更新子弹和敌人的位置,以反映它们在游戏世界中的动态变化。

对于子弹,遍历bullets列表,调用每个子弹对象的update方法,使子弹向上移动。同时,检查子弹是否超出了屏幕的上边界(b.y - b.radius < 0),如果超出,则将其从bullets列表中移除,避免无效的计算和绘制,优化游戏性能。

对于敌人,遍历enemies列表,调用每个敌人对象的update方法,根据敌人的移动逻辑更新它们的位置。敌人会在水平方向上移动,当到达屏幕边界时,会改变移动方向并向下移动,通过这种方式不断向玩家逼近,增加游戏的紧张感和挑战性。

碰撞检测与计分

score = 0
for b in bullets[:]:
    b_rect = b.get_rect()
    for e in enemies[:]:
        e_rect = e.get_rect()
        if b_rect.colliderect(e_rect):
            enemies.remove(e)
            bullets.remove(b)
            score += 10
            break


for e in enemies:
    e_rect = e.get_rect()
    p_rect = player.get_rect()
    if e_rect.colliderect(p_rect) or e.y + e.height > HEIGHT:
        running = False

碰撞检测是游戏中非常关键的一部分,它决定了游戏中各种元素之间的交互结果。在我们的游戏中,主要涉及子弹与敌人的碰撞以及敌人与玩家的碰撞。

对于子弹与敌人的碰撞检测,遍历bullets列表和enemies列表,获取每个子弹和敌人的矩形区域(b_rect和e_rect)。然后使用colliderect方法判断两个矩形区域是否重叠,如果重叠,说明子弹击中了敌人。此时,将被击中的敌人从enemies列表中移除,将子弹从bullets列表中移除,并增加玩家的得分(score += 10),这里得分为 10 分只是一个示例,可以根据游戏的难度和设计需求进行调整。为了提高效率,当检测到一次碰撞后,使用break语句跳出内层循环,因为一颗子弹只会击中一个敌人。

对于敌人与玩家的碰撞检测,遍历enemies列表,获取每个敌人的矩形区域(e_rect)和玩家的矩形区域(p_rect)。判断敌人与玩家的矩形区域是否重叠(e_rect.colliderect(p_rect)),或者敌人是否到达了屏幕底部(e.y + e.height > HEIGHT)。如果满足这两个条件中的任何一个,说明游戏结束,将running变量设置为False,结束游戏循环。

绘制游戏画面

screen.fill(BLACK)
player.draw(screen)
for e in enemies:
    e.draw(screen)
for b in bullets:
    b.draw(screen)


score_surface = font.render(f"Score: {score}", True, WHITE)
screen.blit(score_surface, (10, 10))


pygame.display.flip()

在每一次游戏循环的最后,需要将更新后的游戏画面绘制到屏幕上,让玩家能够看到游戏的实时状态。

首先,使用screen.fill(BLACK)将屏幕背景填充为黑色,清除上一帧的画面,为绘制新的画面做好准备。然后,依次调用玩家、敌人和子弹的draw方法,将它们绘制到屏幕上。绘制的顺序很重要,先绘制背景,再绘制玩家,接着绘制敌人,最后绘制子弹,这样可以确保画面的显示效果符合游戏的逻辑,避免出现元素遮挡错误的情况。

绘制完游戏元素后,还需要显示玩家的得分。使用pygame.font.SysFont创建一个字体对象,然后通过font.render方法将得分信息渲染成一个图像表面(score_surface),其中f"Score: {score}"是格式化字符串,将当前的得分值插入到字符串中。最后,使用screen.blit方法将得分图像绘制到屏幕的左上角((10, 10))位置,让玩家能够清楚地看到自己的得分情况。

最后,调用pygame.display.flip()方法更新整个屏幕的显示,将绘制好的画面呈现给玩家。如果不调用这个方法,玩家将无法看到游戏画面的更新。

调试与优化

在游戏开发过程中,调试与优化是至关重要的环节,它能够确保游戏的稳定性、流畅性和用户体验。通过有效的调试手段,我们可以及时发现并解决游戏中出现的各种问题,而优化则能让游戏在性能上得到提升,运行更加高效。

常见问题与解决方法

在开发过程中,我们可能会遇到各种各样的问题,这些问题就像是游戏开发道路上的绊脚石,需要我们逐一克服。

游戏卡顿是一个常见的问题,它会严重影响玩家的游戏体验。导致游戏卡顿的原因可能有很多,其中最常见的是性能瓶颈。当游戏中的计算量过大,超过了计算机的处理能力时,就会出现卡顿现象。例如,在碰撞检测部分,如果使用了过于复杂的算法,每次检测都需要进行大量的计算,就可能导致帧率下降,游戏出现卡顿。此外,内存管理不善也可能导致卡顿。如果在游戏中频繁地创建和销毁对象,而没有及时释放内存,就会导致内存占用过高,进而影响游戏的性能。为了解决游戏卡顿问题,我们可以采用一些优化策略,如减少不必要的计算、合理使用缓存、优化绘制操作等,后面会详细介绍这些优化技巧。

碰撞检测不准确也是一个比较棘手的问题。在我们的游戏中,碰撞检测用于判断子弹是否击中敌人、敌人是否与玩家发生碰撞等。如果碰撞检测不准确,就会出现子弹明明击中了敌人但敌人却没有被消灭,或者敌人与玩家已经接触但游戏却没有检测到碰撞的情况。这通常是由于碰撞检测算法的精度不够或者碰撞区域的定义不准确导致的。在使用矩形碰撞检测时,如果矩形区域的大小和位置与游戏元素的实际大小和位置不匹配,就会出现检测不准确的问题。为了解决这个问题,我们需要仔细检查碰撞检测的算法和参数设置,确保碰撞区域的定义准确无误。可以通过调试工具,可视化地查看碰撞区域,以便及时发现并调整问题。

图像加载失败也是开发中可能遇到的问题之一。在游戏中,我们可能会使用各种图像资源来丰富游戏的视觉效果,如玩家飞船的图片、敌人的图片、背景图片等。如果图像加载失败,游戏画面就会出现缺失或错误的情况,影响游戏的美观和可玩性。图像加载失败的原因可能有很多,比如图像文件路径错误、文件格式不支持、文件损坏等。在加载图像时,如果指定的文件路径不正确,程序就无法找到对应的图像文件,从而导致加载失败。

Pygame 虽然支持多种图像格式,但如果使用了不支持的格式,也会出现加载失败的问题。为了解决图像加载失败的问题,我们首先要确保图像文件的路径正确,可以使用绝对路径或相对路径,但要注意路径的分隔符在不同操作系统下的差异。其次,要检查图像文件的格式是否为 Pygame 支持的格式,如 PNG、JPEG、BMP 等。最后,还可以通过捕获异常的方式,在图像加载失败时给出相应的提示信息,方便我们进行调试。

性能优化技巧

为了让游戏运行得更加流畅,我们可以采用一些性能优化技巧,这些技巧就像是给游戏注入了一剂强心针,让它能够在各种设备上都能稳定运行。

减少不必要的计算是优化游戏性能的关键。在游戏循环中,每一次循环都会执行各种计算操作,如果这些计算中有一些是不必要的,就会浪费计算机的资源,降低游戏的帧率。在碰撞检测部分,我们可以采用一些优化算法,减少检测的次数。可以使用空间分区算法,将游戏场景划分为多个小区域,只对位于同一区域内的游戏元素进行碰撞检测,这样可以大大减少检测的范围,提高检测的效率。此外,还可以缓存一些计算结果,避免重复计算。在计算敌人的移动位置时,如果敌人的移动速度和方向在一段时间内没有变化,就可以直接使用之前计算好的结果,而不需要重新计算。

合理使用缓存也是提高游戏性能的有效方法。缓存就像是一个临时存储区,它可以存储一些经常使用的数据或计算结果,当需要时可以直接从缓存中获取,而不需要重新计算或加载。在游戏中,我们可以缓存一些图像资源、声音资源等。当需要显示某个图像时,如果该图像已经在缓存中,就可以直接从缓存中取出并显示,而不需要再次从文件中加载,这样可以减少文件读取的次数,提高游戏的加载速度。此外,还可以缓存一些游戏状态信息,如玩家的得分、生命值等,避免在每次更新时都重新计算。

优化绘制操作也能显著提升游戏的性能。在游戏循环中,绘制操作是比较耗时的,因为它需要将游戏中的各种元素绘制到屏幕上。为了减少绘制的时间,我们可以采用一些优化策略,如批处理绘制。批处理绘制是将多个需要绘制的对象组合成一个批次,一次性进行绘制,而不是逐个绘制。这样可以减少绘制函数的调用次数,提高绘制的效率。在绘制敌人时,如果有多个敌人,我们可以将它们的绘制操作合并成一个批次,一次性绘制所有敌人,而不是分别调用绘制函数来绘制每个敌人。此外,还可以使用双缓冲技术,即在后台缓冲区中进行绘制操作,绘制完成后再将缓冲区的内容一次性显示到屏幕上,这样可以避免在绘制过程中出现闪烁的现象,提高游戏的画面质量。

总结与展望

通过这次使用 Python 开发太空侵略者 2D 游戏的实践,相信大家已经对 Python 编程和游戏开发有了更深入的理解。我们从搭建开发环境开始,逐步了解游戏机制,使用 Python 代码实现了游戏的各个功能,包括初始化 Pygame、设置基础参数、创建游戏角色类、实现游戏核心逻辑等。在开发过程中,我们还学习了如何调试游戏,解决常见问题,并运用性能优化技巧让游戏运行得更加流畅。

然而,我们的游戏目前还只是一个基础版本,还有很大的扩展和创新空间。比如,添加音效可以极大地增强游戏的沉浸感。当玩家发射子弹时,添加清脆的射击音效;当敌人被击中时,播放爆炸音效;再配上紧张刺激的背景音乐,让玩家仿佛置身于真实的太空战斗场景中。在 Pygame 中,可以使用pygame.mixer模块来实现音效和音乐的播放,通过加载音效文件和音乐文件,并在合适的时机调用播放函数,就能轻松为游戏添加丰富的声音效果。

增加更多关卡也是一个不错的扩展方向。随着关卡的推进,敌人的种类可以更加多样化,它们可能具有不同的移动方式、攻击模式和防御能力。比如,有些敌人可以发射子弹攻击玩家,有些敌人具有护盾,需要玩家多次攻击才能消灭。关卡的背景也可以发生变化,从神秘的太空星云到危险的小行星带,为玩家带来全新的视觉体验。同时,关卡的难度可以逐步提升,敌人的数量增多、移动速度加快,给玩家带来更大的挑战。

道具系统的添加能让游戏更加有趣。道具可以随机出现在游戏场景中,玩家收集后可以获得各种特殊能力。收集加速道具后,玩家的飞船移动速度会大幅提升;获得火力增强道具,子弹的威力会增加,甚至可以一次发射多发子弹;护盾道具则可以为玩家提供额外的防御,抵挡敌人的一次攻击。通过合理设计道具的出现概率和效果,可以为游戏增添更多的策略性和趣味性。

希望大家能够在现有代码的基础上,发挥自己的创造力,对游戏进行改进和完善。无论是优化游戏性能、丰富游戏玩法,还是提升游戏的视觉和听觉效果,每一次的尝试都是一次宝贵的学习和成长机会。相信在大家的努力下,这款经典的太空侵略者游戏将焕发出新的活力,成为一款独具特色的游戏作品 。

# -*- coding: utf-8 -*-
import math
import random
import sys

import pygame
from pydub import AudioSegment

# 初始化Pygame
pygame.init()
pygame.mixer.init()


# 生成音效文件
def generate_sounds():
    try:
        # 射击音效
        sample_rate = 44100
        duration = 0.1
        frequency = 2000
        samples = [int(32767 * math.sin(2 * math.pi * frequency * t / sample_rate))
                   for t in range(int(sample_rate * duration))]
        AudioSegment(
            data=bytes(samples),
            sample_width=2,
            frame_rate=sample_rate,
            channels=1
        ).export("shoot.wav", format="wav")

        # 爆炸音效
        noise = [random.randint(-32768, 32767) for _ in range(2000)]
        AudioSegment(
            data=bytes(noise),
            sample_width=2,
            frame_rate=44100,
            channels=1
        ).export("explosion.wav", format="wav")
    except Exception as e:
        print(f"音效生成失败: {e}")


generate_sounds()

# 游戏配置
WIDTH, HEIGHT = 800, 600
FPS = 60
PLAYER_SPEED = 8
BULLET_SPEED = 12
ENEMY_ROWS = 5
ENEMY_COLS = 11

# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)

# 加载资源
try:
    shoot_sound = pygame.mixer.Sound("shoot.wav")
    explosion_sound = pygame.mixer.Sound("explosion.wav")
    pygame.mixer.music.load("background_music.mp3")
except pygame.error:
    print("警告:音效文件加载失败")


class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((50, 30))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect(center=(WIDTH // 2, HEIGHT - 50))
        self.speed = PLAYER_SPEED
        self.last_shot = 0

    def update(self, keys):
        if keys[pygame.K_LEFT]:
            self.rect.x = max(0, self.rect.x - self.speed)
        if keys[pygame.K_RIGHT]:
            self.rect.x = min(WIDTH - self.rect.width, self.rect.x + self.speed)


class Bullet(pygame.sprite.Sprite):
    def __init__(self, pos):
        super().__init__()
        self.image = pygame.Surface((4, 15))
        self.image.fill(YELLOW)
        self.rect = self.image.get_rect(center=pos)
        self.speed = BULLET_SPEED

    def update(self):
        self.rect.y -= self.speed
        if self.rect.bottom < 0:
            self.kill()


class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y, level):
        super().__init__()
        self.image = pygame.Surface((40, 30))
        color = [(255, 100, 100), (255, 150, 150), (200, 200, 255)][level % 3]
        self.image.fill(color)
        self.rect = self.image.get_rect(topleft=(x, y))
        self.speed = 1 + level * 0.3
        self.direction = 1

    def update(self, group):
        self.rect.x += self.speed * self.direction
        if any(e.rect.right >= WIDTH - 20 for e in group):
            self.direction = -1
            self.rect.y += 30
        elif any(e.rect.left <= 20 for e in group):
            self.direction = 1
            self.rect.y += 30


class Game:
    def __init__(self):
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("太空侵略者")
        self.clock = pygame.time.Clock()
        # 修改字体加载方式
        try:
            # Windows系统使用微软雅黑,其他系统需要替换对应字体路径
            self.font = pygame.font.Font("msyh.ttc", 28)  # 字体文件需要放在游戏目录
        except:
            # 备选字体方案
            try:
                self.font = pygame.font.SysFont("simhei", 28)  # 黑体
            except:
                self.font = pygame.font.Font(None, 28)  # 最后回退到系统默认

        self.reset()

    def reset(self):
        self.player = Player()
        self.bullets = pygame.sprite.Group()
        self.enemies = pygame.sprite.Group()
        self.score = 0
        self.level = 1
        self._create_enemies()
        pygame.mixer.music.play(-1)

    def _create_enemies(self):
        spacing_x = 60
        spacing_y = 50
        for row in range(ENEMY_ROWS):
            for col in range(ENEMY_COLS):
                x = 100 + col * spacing_x
                y = 50 + row * spacing_y
                self.enemies.add(Enemy(x, y, self.level))

    def run(self):
        running = True
        while running:
            self.clock.tick(FPS)
            keys = pygame.key.get_pressed()

            # 事件处理
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        if len(self.bullets) < 3:
                            self.bullets.add(Bullet(self.player.rect.midtop))
                            shoot_sound.play()

            # 更新状态
            self.player.update(keys)
            self.bullets.update()
            self.enemies.update(self.enemies)

            # 碰撞检测
            for bullet in pygame.sprite.groupcollide(self.bullets, self.enemies, True, True):
                self.score += 100
                explosion_sound.play()

            if pygame.sprite.spritecollide(self.player, self.enemies, False) \
                    or any(e.rect.bottom > HEIGHT - 100 for e in self.enemies):
                self.game_over()
                return

            if not self.enemies:
                self.level += 1
                self._create_enemies()

            # 渲染画面
            self.screen.fill(BLACK)
            self.screen.blit(self.player.image, self.player.rect)
            self.bullets.draw(self.screen)
            self.enemies.draw(self.screen)

            # 显示信息

            score_text = self.font.render(f"得分: {self.score} 关卡: {self.level}", True, WHITE)
            self.screen.blit(score_text, (10, 10))

            pygame.display.flip()

    def game_over(self):
        pygame.mixer.music.stop()
        text = self.font.render("游戏结束! 按R重试,Q退出", True, RED)
        self.screen.blit(text, (WIDTH // 2 - 180, HEIGHT // 2))
        pygame.display.flip()

        while True:
            event = pygame.event.wait()
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r:
                    self.reset()
                    self.run()
                elif event.key == pygame.K_q:
                    pygame.quit()
                    sys.exit()


if __name__ == "__main__":
    game = Game()
    game.run()

相关文章

怎么安装python的pygame库文件

怎么安装python的pygame库文件?点击“开始菜单”,搜索程序“cmd”,鼠标右键,选择“以管理员身份运行”。推荐:《Python教程》输入代码“pip install pygame”,选择“剪...

python安装 pygame

通过安装的PyCharm,在里面启动安装在终端里面输入pip install pygame,安装成功...

零基础学习编程:用Python和Pygame实现2048游戏

预计阅读时间:30分钟简介编程是一门强大的工具,可以帮助我们解决问题、创造创新,并提升我们的思维能力。对于那些零基础的人来说,学习编程可能会感到有些困惑。本教程将带领您逐步学习编程的基础知识,并通过使...

「Python系列」python几个重要模块的安装(二)

一、 python的pygame的安装:安装地址:https://www.cnblogs.com/charliedaifu/p/9938542.htmlpyagme包下载地址:https://down...

Python3+pygame实现的坦克大战

一、显示效果二、代码1. 说明几乎所有pygame游戏,基本都遵循一定的开发流程,大体如下:初始化pygame创建窗口while循环检测以及处理事件(鼠标点击、按键等)更新UI界面2. 代码创建一个m...

pymunk,一个超酷的 Python 库!

大家好,今天为大家分享一个超酷的 Python 库 - pymunk。Github地址:https://github.com/viblo/pymunkPymunk是一个基于Chipmunk物理引擎的P...