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

news2025/2/2 16:07:30

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 游戏:从玩家移动与射击,到敌人群体移动与下落,再到碰撞检测与游戏结束逻辑,都得到了体现。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《逆向工程核心原理》第三~五章知识整理

查看上一章节内容《逆向工程核心原理》第一~二章知识整理 对应《逆向工程核心原理》第三章到第五章内容 小端序标记法 字节序 多字节数据在计算机内存中存放的字节顺序分为小端序和大端序两大类 大端序与小端序 BYTE b 0x12; WORD w 0x1234; DWORD dw 0x12345678; cha…

2025 AI行业变革:从DeepSeek V3到o3-mini的技术演进

【核心要点】 DeepSeek V3引领算力革命&#xff0c;成本降至1/20o3-mini以精准优化回应市场挑战AI技术迈向真正意义的民主化行业生态正在深刻重构 一、市场格局演变 发展脉络 2025年初&#xff0c;AI行业迎来重要转折。DeepSeek率先发布V3模型&#xff0c;通过革命性的架构创…

SAP SD学习笔记28 - 请求计划(开票计划)之2 - Milestone请求(里程碑开票)

上一章讲了请求计划&#xff08;开票计划&#xff09;中的 定期请求。 SAP SD学习笔记27 - 请求计划(开票计划)之1 - 定期请求-CSDN博客 本章继续来讲请求计划&#xff08;开票计划&#xff09;的其他内容&#xff1a; Milestone请求(里程碑请求)。 目录 1&#xff0c;Miles…

SpringBoot+Vue的理解(含axios/ajax)-前后端交互前端篇

文章目录 引言SpringBootThymeleafVueSpringBootSpringBootVue&#xff08;前端&#xff09;axios/ajaxVue作用响应式动态绑定单页面应用SPA前端路由 前端路由URL和后端API URL的区别前端路由的数据从哪里来的 Vue和只用三件套axios区别 关于地址栏url和axios请求不一致VueJSPS…

大白话讲清楚embedding原理

Embedding&#xff08;嵌入&#xff09;是一种将高维数据&#xff08;如单词、句子、图像等&#xff09;映射到低维连续向量的技术&#xff0c;其核心目的是通过向量表示捕捉数据之间的语义或特征关系。以下从原理、方法和应用三个方面详细解释Embedding的工作原理。 一、Embe…

2025年1月22日(网络编程 udp)

系统信息&#xff1a; ubuntu 16.04LTS Raspberry Pi Zero 2W 系统版本&#xff1a; 2024-10-22-raspios-bullseye-armhf Python 版本&#xff1a;Python 3.9.2 已安装 pip3 支持拍摄 1080p 30 (1092*1080), 720p 60 (1280*720), 60/90 (640*480) 已安装 vim 已安装 git 学习…

ios swift画中画技术尝试

继上篇&#xff1a;iOS swift 后台运行应用尝试失败-CSDN博客 为什么想到画中画&#xff0c;起初是看到后台模式里有一个picture in picture&#xff0c;去了解了后发现这个就是小窗口视频播放&#xff0c;方便用户执行多任务。看小窗口视频的同时&#xff0c;可以作其他的事情…

ArkTS高性能编程实践

文章目录 概述声明与表达式函数数组异常 概述 本文主要提供应用性能敏感场景下的高性能编程的相关建议&#xff0c;助力开发者开发出高性能的应用。高性能编程实践&#xff0c;是在开发过程中逐步总结出来的一些高性能的写法和建议&#xff0c;在业务功能实现过程中&#xff0…

阿里新发的大模型Qwen2.5-max如何?

阿里新发布的大模型Qwen2.5-Max是一款性能卓越、技术先进的大型语言模型&#xff0c;其在多个方面展现了突出的表现。以下是基于我搜索到的资料对Qwen2.5-Max的详细评价&#xff1a; 技术特点 超大规模预训练数据&#xff1a;Qwen2.5-Max采用了超过20万亿tokens的超大规模预训…

走向基于大语言模型的新一代推荐系统:综述与展望

HightLight 论文题目&#xff1a;Towards Next-Generation LLM-based Recommender Systems: A Survey and Beyond作者机构&#xff1a;吉林大学、香港理工大学、悉尼科技大学、Meta AI论文地址&#xff1a; https://arxiv.org/abs/2410.1974 基于大语言模型的下一代推荐系统&…

第1章 量子暗网中的血色黎明

月球暗面的危机与阴谋 量子隧穿效应催生的幽蓝电弧&#xff0c;于环形山表面肆意跳跃&#xff0c;仿若无数奋力挣扎的机械蠕虫&#xff0c;将月球暗面的死寂打破&#xff0c;徒增几分诡异。艾丽伫立在被遗弃的“广寒宫”量子基站顶端&#xff0c;机械义眼之中&#xff0c;倒映着…

MCU内部ADC模块误差如何校准

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、ADC误差校准引言 MCU 片内 ADC 模块的误差总包括了 5 个静态参数 (静态失调&#xff0c;增益误差&#xff0c;微分非线性…

【Rust自学】15.4. Drop trait:告别手动清理,释放即安全

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 15.4.1. Drop trait的意义 类型如果实现了Drop trait&#xff0c;就可以让程序员自定义当值离开作用域时发生的操作。例如文件、网络资源…

【Block总结】CPCA,通道优先卷积注意力|即插即用

论文信息 标题: Channel Prior Convolutional Attention for Medical Image Segmentation 论文链接: arxiv.org 代码链接: GitHub 创新点 本文提出了一种新的通道优先卷积注意力&#xff08;CPCA&#xff09;机制&#xff0c;旨在解决医学图像分割中存在的低对比度和显著…

信息学奥赛一本通 1607:【 例 2】任务安排 2 | 洛谷 P10979 任务安排 2

【题目链接】 ybt 1607&#xff1a;【 例 2】任务安排 2 洛谷 P10979 任务安排 2 注&#xff1a;ybt1607中n最大达到 1 0 4 10^4 104&#xff0c;洛谷P10979中n最大达到 3 ∗ 1 0 5 3*10^5 3∗105&#xff0c;本题解统一认为n最大达到 3 ∗ 1 0 5 3*10^5 3∗105。 【题目考点…

OFDM系统仿真

1️⃣ OFDM的原理 1.1 介绍 OFDM是一种多载波调制技术&#xff0c;将输入数据分配到多个子载波上&#xff0c;每个子载波上可以独立使用 QAM、PSK 等传统调制技术进行调制。这些子载波之间互相正交&#xff0c;从而可以有效利用频谱并减少干扰。 1.2 OFDM的核心 多载波调制…

【Go语言圣经】第四节:复合数据类型

第四章&#xff1a;复合数据类型 本节主要讨论四种类型——数组、slice、map和结构体。 数组和结构体都是有固定内存大小的数据结构。相比之下&#xff0c;slice 和 map 则是动态的数据结构&#xff0c;它们可以根据需要动态增长。 4.1 数组 数组是一个定长的由特定类型元素…

完美还是完成?把握好度,辨证看待

完美还是完成&#xff1f; 如果说之前这个答案有争议&#xff0c;那么现在&#xff0c;答案毋庸置疑 ■为什么完美大于完成 ●时间成本&#xff1a; 做事不仅要考虑结果&#xff0c;还要考虑时间和精力&#xff0c;要说十年磨一剑的确质量更好&#xff0c;但是现实没有那么多…

Many Whelps! Handle It! (10 player) Many Whelps! Handle It! (25 player)

http://db.nfuwow.com/80/?achievement4403 http://db.nfuwow.com/80/?achievement4406 最少扣你50DKP! 第二阶段 当奥妮克希亚升空后&#xff0c;在10秒内引出50只奥妮克希亚雏龙&#xff0c;随后击败奥妮克希亚。 World of Warcraft [CLASSIC][80猎人][Grandel][最少扣你5…

【React】PureComponent 和 Component 的区别

前言 在 React 中&#xff0c;PureComponent 和 Component 都是用于创建组件的基类&#xff0c;但它们有一个主要的区别&#xff1a;PureComponent 会给类组件默认加一个shouldComponentUpdate周期函数。在此周期函数中&#xff0c;它对props 和 state (新老的属性/状态)会做一…