使用Pygame制作“太空侵略者”游戏

1. 前言

在 2D 游戏开发中,“太空侵略者”是一款入门难度适中、却能覆盖多种常见游戏机制的项目:

  • 玩家控制飞船(Player)左右移动,发射子弹。
  • 敌人(Enemy)排列成一行或多行,从屏幕顶端缓慢移动或左右移动逼近玩家。
  • 当敌人被击中时,会被销毁并加分。若敌人到达玩家所在行或玩家被碰撞,则游戏结束。

在本篇中,我们将使用 Python + Pygame 实现一个简化版本的 Space Invaders,涵盖:玩家移动、子弹发射、敌人生成与移动、碰撞检测、计分与游戏结束逻辑等。


2. 开发环境

  1. Python 3.x
  2. Pygame
    如果尚未安装,可在命令行执行:
    pip install pygame
    
  3. 桌面系统:Windows、macOS 或大多数 Linux 系统都可正常运行。

确认 import pygame 无报错即可开始。


3. 游戏思路与关键点

  1. 玩家(Player)

    • 以飞船形象出现在屏幕下方,可左右移动(受屏幕边界限制)。
    • 按空格键或其他按键发射子弹。子弹往上移动,离开屏幕后可回收。
  2. 敌人(Enemy)

    • 可一次性生成若干敌人(如 3 行 × 6 列),排列在屏幕上方。
    • 敌人整体移动:
      • 可以先左右移动,到达边界后整体往下一行移动;
      • 或者在一定频率向玩家方向缓慢下降。
    • 当敌人到达玩家所在行(或接近底部),游戏结束。
  3. 子弹(Bullet)

    • 玩家发射的子弹以固定速度向上移动;
    • 敌人若与子弹发生碰撞,则敌人被销毁,玩家得分,子弹也随之消失。
  4. 碰撞检测

    • Rect 或基于距离的判断:当子弹矩形与敌人矩形重叠时,判定击中。
    • 当敌人矩形与玩家矩形重叠,或敌人到达屏幕下方,则游戏结束。
  5. 游戏循环

    • 每帧:处理输入事件(玩家移动、发射)、更新玩家位置、子弹与敌人位置,检测碰撞、绘制画面。
    • 当所有敌人消灭或玩家阵亡,即可结束游戏并显示结果。

4. 完整示例代码

以下示例保存为 space_invaders.py 并运行即可。请根据自己的需求调节画面尺寸、敌人数量、速度等参数。

import pygame
import sys
import random

# 初始化 Pygame
pygame.init()

# ---------------------
# 全局配置
# ---------------------
WIDTH, HEIGHT = 600, 600
FPS = 60

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

# 创建窗口
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Space Invaders - Pygame 示例")
clock = pygame.time.Clock()

# 字体
font = pygame.font.SysFont("arial", 24)

# ---------------------
# 玩家类
# ---------------------
class Player:
    def __init__(self):
        self.width = 40
        self.height = 40
        self.x = (WIDTH - self.width) // 2
        self.y = HEIGHT - 60
        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)

# ---------------------
# 敌人类
# ---------------------
class Enemy:
    def __init__(self, x, y):
        self.width = 30
        self.height = 30
        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:
            # 改变方向,并让所有敌人往下移动 speed_y
            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)

# ---------------------
# 子弹类
# ---------------------
class Bullet:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.radius = 5
        self.speed = 7
        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)

# ---------------------
# 工具函数
# ---------------------
def create_enemies(rows=3, cols=6):
    """
    生成敌人列表,分布在屏幕上方
    """
    enemies = []
    gap_x = 60
    gap_y = 50
    start_x = 50
    start_y = 50
    for row in range(rows):
        for col in range(cols):
            x = start_x + col * gap_x
            y = start_y + row * gap_y
            enemy = Enemy(x, y)
            enemies.append(enemy)
    return enemies

def main():
    player = Player()
    enemies = create_enemies(rows=3, cols=6)
    bullets = []

    score = 0
    running = True
    while running:
        clock.tick(FPS)

        # 1) 处理事件
        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()

        # 2) 更新子弹位置并检测越界
        for b in bullets[:]:
            b.update()
            if b.y + b.radius < 0:
                bullets.remove(b)

        # 3) 更新敌人群位置
        for e in enemies:
            e.update(enemies)

        # 4) 碰撞检测:子弹与敌人
        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  # 该子弹已被移除,无需再检测其它敌人

        # 5) 检测敌人是否到达玩家或超越底部 -> 游戏结束
        for e in enemies:
            # 若敌人越过玩家所在 y,或者直接与玩家重叠
            if e.y + e.height > player.y:
                running = False

        # 6) 如果敌人全部消灭 -> 获胜 or 提示
        if not enemies:
            # 可以自定义下一波敌人或结束游戏
            running = False

        # 7) 绘制
        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()

    game_over(score)

def game_over(score):
    # 游戏结束界面
    screen.fill(BLACK)
    msg = f"Game Over! Your Score: {score}"
    label = font.render(msg, True, WHITE)
    rect = label.get_rect(center=(WIDTH // 2, HEIGHT // 2))
    screen.blit(label, rect)
    pygame.display.flip()
    pygame.time.wait(3000)
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()

主要逻辑解析

  1. Player(玩家)

    • 只有左右移动逻辑,且在碰撞边界时保持在范围内。
    • 按空格键发射子弹(Bullet 对象),存入列表。
  2. Enemy(敌人)

    • 通过 create_enemies() 一次性生成多个敌人,按行列布局排列在上方。
    • 每帧都执行 update(),在水平方向移动;若检测到超出左右边界,就整体往下一行,并反转移动方向。
  3. Bullet(子弹)

    • 每帧向上移动,若飞出屏幕则被移除;
    • 碰撞到敌人后,敌人与子弹都被移除并加分。
  4. 碰撞检测

    • 使用 Rectcolliderect 方法判断子弹与敌人是否重叠。
    • 当任意敌人到达玩家所在行或越过玩家,则游戏结束。
  5. 游戏结束

    • 当敌人列表为空时,玩家成功消灭全部敌人,也退出游戏循环;
    • 或者当敌人到达玩家区域,表示玩家失败。
    • 最后进入 game_over() 界面做简单提示后退出。

5. 运行效果

image.png
在这里插入图片描述


6. 总结

通过本篇文章,你已经学会了如何使用 Pygame 编写一个简易版的 Space Invaders 游戏:从玩家移动与射击,到敌人群体移动与下落,再到碰撞检测与游戏结束逻辑,都得到了体现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964594.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux find 命令 | grep 命令 | 查找 / 列出文件或目录路径 | 示例

注&#xff1a;本文为 “Linux find 命令 | grep 命令使用” 相关文章合辑。 未整理去重。 如何在 Linux 中查找文件 作者&#xff1a; Lewis Cowles 译者&#xff1a; LCTT geekpi | 2018-04-28 07:09 使用简单的命令在 Linux 下基于类型、内容等快速查找文件。 如果你是 W…

JVM 四虚拟机栈

虚拟机栈出现的背景 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多…

排序算法与查找算法

1.十大经典排序算法 我们希望数据以一种有序的形式组织起来&#xff0c;无序的数据我们要尽量将其变得有序 一般说来有10种比较经典的排序算法 简单记忆为Miss D----D小姐 时间复杂度 &#xff1a;红色<绿色<蓝色 空间复杂度&#xff1a;圆越大越占空间 稳定性&…

Spring理论知识(Ⅴ)——Spring Web模块

Spring的组成 Spring由20个核心依赖组成&#xff0c;这20个核心依赖可以分为6个核心模块 Spring Web模块简介 众所周知&#xff0c;Java目前最大的一个用途就是作为Web应用的服务端&#xff08;Java Web&#xff09; Spring又是JavaEE中使用最广泛的开发框架&#xff0…

(10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例

&#xff08;1&#xff09; 先介绍下后面的代码里要用到的基础函数&#xff1a; 以及&#xff1a; &#xff08;2&#xff09; 接着给出现代版的 读写 socket 参数的系统函数 &#xff1a; 以及&#xff1a; &#xff08;3&#xff09; 给出 一言的 范例代码&#xff0c;获取…

车载软件架构 --- 软件定义汽车面向服务架构的应用迁移

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 简单&#xff0c;单纯&#xff0c;喜欢独处&#xff0c;独来独往&#xff0c;不易合同频过着接地气的生活…

【高级篇 / IPv6】(7.2) ❀ 04. 在60E上配置ADSL拨号宽带上网(IPv4) ❀ FortiGate 防火墙

【简介】除了单位用户以外&#xff0c;大部分个人用户目前使用的仍然是30E、50E、60E系列防火墙&#xff0c;固件无法达到目前最高版本7.6&#xff0c;这里以最常用的60E为例&#xff0c;演示固件版本7.2下实现ADSL拨号宽带的IPv6上网。由于内容比较多&#xff0c;文章分上、下…

鼠标拖尾特效

文章目录 鼠标拖尾特效一、引言二、实现原理1、监听鼠标移动事件2、生成拖尾元素3、控制元素生命周期 三、代码实现四、使用示例五、总结 鼠标拖尾特效 一、引言 鼠标拖尾特效是一种非常酷炫的前端交互效果&#xff0c;能够为网页增添独特的视觉体验。它通常通过JavaScript和C…

OpenEuler学习笔记(十六):搭建postgresql高可用数据库环境

以下是在OpenEuler系统上搭建PostgreSQL高可用数据环境的一般步骤&#xff0c;通常可以使用流复制&#xff08;Streaming Replication&#xff09;或基于Patroni等工具来实现高可用&#xff0c;以下以流复制为例&#xff1a; 安装PostgreSQL 配置软件源&#xff1a;可以使用O…

机器学习--2.多元线性回归

多元线性回归 1、基本概念 1.1、连续值 1.2、离散值 1.3、简单线性回归 1.4、最优解 1.5、多元线性回归 2、正规方程 2.1、最小二乘法 2.2、多元一次方程举例 2.3、矩阵转置公式与求导公式 2.4、推导正规方程0的解 2.5、凸函数判定 成年人最大的自律就是&#xff1a…

(done) MIT6.S081 2023 学习笔记 (Day7: LAB6 Multithreading)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/thread.html (任务1教会了你如何用 C 语言调用汇编&#xff0c;编译后链接即可) 任务1&#xff1a;Uthread: switching between threads (完成) 在这个练习中&#xff0c;你将设计一个用户级线程系统中的上下文切…

AWS配置邮件服务器给其他邮箱发邮件的方法

一、前言 公司系统用AWS平台的邮件服务器&#xff0c;如果想给新收件人发邮件&#xff0c;就需要特殊配置下才行&#xff1b;不然发不出去。 二、步骤 1.登录AWS平台&#xff0c;登录地址&#xff08;藏的很深&#xff0c;不太好搜到&#xff0c;参数也不能删&#xff09; …

kaggle视频行为分析1st and Future - Player Contact Detection

这次比赛的目标是检测美式橄榄球NFL比赛中球员经历的外部接触。您将使用视频和球员追踪数据来识别发生接触的时刻&#xff0c;以帮助提高球员的安全。两种接触&#xff0c;一种是人与人的&#xff0c;另一种是人与地面&#xff0c;不包括脚底和地面的&#xff0c;跟我之前做的这…

42【文件名的编码规则】

我们在学习的过程中&#xff0c;写出数据或读取数据时需要考虑编码类型 火山采用&#xff1a;UTF-16 易语言采用&#xff1a;GBK php采用&#xff1a;UTF-8 那么我们写出的文件名应该是何种编码的&#xff1f;比如火山程序向本地写出一个“测试.txt”&#xff0c;理论上这个“测…

全栈开发:使用.NET Core WebAPI构建前后端分离的核心技巧(一)

目录 cors解决跨域 依赖注入使用 分层服务注册 缓存方法使用 内存缓存使用 缓存过期清理 缓存存在问题 分布式的缓存 cors解决跨域 前后端分离已经成为一种越来越流行的架构模式&#xff0c;由于跨域资源共享(cors)是浏览器的一种安全机制&#xff0c;它会阻止前端应用…

JVM执行流程与架构(对应不同版本JDK)

直接上图&#xff08;对应JDK8以及以后的HotSpot&#xff09; 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭&#xff1a; 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间&#xff0c;堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…

【玩转 Postman 接口测试与开发2_014】第11章:测试现成的 API 接口(下)——自动化接口测试脚本实战演练 + 测试集合共享

《API Testing and Development with Postman》最新第二版封面 文章目录 3 接口自动化测试实战3.1 测试环境的改造3.2 对列表查询接口的测试3.3 对查询单个实例的测试3.4 对新增接口的测试3.5 对修改接口的测试3.6 对删除接口的测试 4 测试集合的共享操作4.1 分享 Postman 集合…

登录认证(5):过滤器:Filter

统一拦截 上文我们提到&#xff08;登录认证&#xff08;4&#xff09;&#xff1a;令牌技术&#xff09;&#xff0c;现在大部分项目都使用JWT令牌来进行会话跟踪&#xff0c;来完成登录功能。有了JWT令牌可以标识用户的登录状态&#xff0c;但是完整的登录逻辑如图所示&…

基于Spring Security 6的OAuth2 系列之七 - 授权服务器--自定义数据库客户端信息

之所以想写这一系列&#xff0c;是因为之前工作过程中使用Spring Security OAuth2搭建了网关和授权服务器&#xff0c;但当时基于spring-boot 2.3.x&#xff0c;其默认的Spring Security是5.3.x。之后新项目升级到了spring-boot 3.3.0&#xff0c;结果一看Spring Security也升级…

git基础使用--1--版本控制的基本概念

文章目录 git基础使用--1--版本控制的基本概念1.版本控制的需求背景&#xff0c;即为啥需要版本控制2. 集中式版本控制SVN3. 分布式版本控制 Git4. SVN和Git的比较 git基础使用–1–版本控制的基本概念 1.版本控制的需求背景&#xff0c;即为啥需要版本控制 先说啥叫版本&…