使用Pygame制作“贪吃蛇”游戏

news/2025/2/2 3:53:18 标签: pygame, python, 游戏程序

贪吃蛇 是一款经典的休闲小游戏:玩家通过操控一条会不断变长的“蛇”在屏幕中移动,去吃随机出现的食物,同时要避免撞到墙壁或自己身体的其他部分。由于其逻辑相对简单,但可玩性和扩展性都不错,非常适合作为新手练习游戏编程的项目。在本篇博客中,我们将使用 Python 语言 + Pygame 库来从零实现一款贪吃蛇。


1. 开发环境准备

  1. Python 3.x
  2. Pygame 库:如果尚未安装,请在命令行执行:
    pip install pygame
    
  3. 图形界面环境:在常见的桌面系统(Windows、macOS、Linux)上都可以正常运行 Pygame 程序。

2. 游戏实现思路

要实现一个简易的贪吃蛇,主要需要以下几个要点:

  1. 游戏界面

    • 一般的做法是将屏幕拆分成网格(如 20×20 的方格),蛇和食物都在这些方格坐标上移动。
    • 每一个方格的大小可定义为 20×20 或更适合你需求的像素。
    • 蛇的运动每次走一格,方向由玩家通过箭头键或 WASD 控制。
  2. 蛇的表示

    • 通常用一个列表来储存蛇身每个“方块”的坐标(从头到尾依次存放)。
    • 当蛇移动时,需要在头部插入一个新的坐标(根据方向计算),同时移除最后一个坐标(蛇尾)来保持“长度”不变;只有当蛇吃到食物时,才不移除尾部,从而实现“变长”。
  3. 食物的产生

    • 在网格上随机产生一个坐标点,作为食物。
    • 当蛇头和食物坐标重合时,表示蛇吃到了食物,此时增加蛇的长度并在其他空格处随机生成新的食物。
  4. 边界与身体碰撞检测

    • 边界检测:如果蛇头超出屏幕范围,则表示撞墙,游戏结束。
    • 身体碰撞检测:如果蛇头的坐标跟身体某个方块相同,表示咬到自己,游戏结束。
  5. 游戏循环

    • 使用 Pygame 的事件与时钟,循环更新游戏状态:接收玩家按键,移动蛇头,检测碰撞,更新得分等。

3. 完整示例代码

将以下代码保存为 snake_game.py 并运行,即可体验一个最基本版本的贪吃蛇。你可以根据自己的需求对其中的参数或逻辑进行修改和完善。

python">import pygame
import random
import sys

# 初始化 Pygame
pygame.init()

# ---------------------
#  全局配置
# ---------------------
BLOCK_SIZE = 20        # 蛇和食物的大小(方块尺寸)
GRID_WIDTH = 30        # 水平方向方块数
GRID_HEIGHT = 20       # 垂直方向方块数

SCREEN_WIDTH = GRID_WIDTH * BLOCK_SIZE
SCREEN_HEIGHT = GRID_HEIGHT * BLOCK_SIZE

FPS = 10               # 游戏刷新率(蛇移动速度)

# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED   = (255, 0, 0)
GREEN = (0, 200, 0)
GRAY  = (50, 50, 50)

# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("贪吃蛇 - Pygame")

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

# ---------------------
#  功能函数
# ---------------------
def draw_text(text, color, x, y):
    """在屏幕指定位置绘制文字"""
    surface = font.render(text, True, color)
    screen.blit(surface, (x, y))

def draw_block(color, x, y):
    """在(x, y)绘制一个BLOCK_SIZE大小的方块"""
    rect = pygame.Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)
    pygame.draw.rect(screen, color, rect)

# ---------------------
#  蛇类
# ---------------------
class Snake:
    def __init__(self):
        # 蛇初始位置(列表: 记录蛇身每个方块的坐标)
        # 例如[(x1, y1), (x2, y2), ...] 从头到尾
        self.body = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]
        self.direction = 'UP'     # 初始方向:上
        self.grow = False         # 标识是否需要增长身体

    def move(self):
        # 计算新头部位置
        head_x, head_y = self.body[0]
        if self.direction == 'UP':
            head_y -= 1
        elif self.direction == 'DOWN':
            head_y += 1
        elif self.direction == 'LEFT':
            head_x -= 1
        elif self.direction == 'RIGHT':
            head_x += 1

        new_head = (head_x, head_y)
        self.body.insert(0, new_head)  # 头部插入到列表最前
        # 如果不需要生长,则移除尾部
        if not self.grow:
            self.body.pop()
        else:
            self.grow = False  # 本次已经完成增长

    def change_direction(self, new_dir):
        """
        根据玩家按键改变蛇的方向,
        但需要防止蛇直接掉头(如当前在往左跑,不可立即改成往右)
        """
        opposite = {'UP': 'DOWN', 'DOWN': 'UP', 'LEFT': 'RIGHT', 'RIGHT': 'LEFT'}
        if new_dir != opposite[self.direction]:
            self.direction = new_dir

    def check_collision(self):
        """
        检测是否撞墙或撞自己:
        - 撞墙: 头部超出网格范围
        - 撞自己: 头部坐标出现在身体其他部分
        """
        head_x, head_y = self.body[0]
        # 撞墙
        if head_x < 0 or head_x >= GRID_WIDTH or head_y < 0 or head_y >= GRID_HEIGHT:
            return True
        # 撞自己
        if self.body[0] in self.body[1:]:
            return True
        return False

    def draw(self):
        """绘制蛇"""
        for block in self.body:
            x_coord = block[0] * BLOCK_SIZE
            y_coord = block[1] * BLOCK_SIZE
            draw_block(GREEN, x_coord, y_coord)

# ---------------------
#  食物类
# ---------------------
class Food:
    def __init__(self):
        self.position = self.random_pos()

    def random_pos(self):
        """随机生成食物坐标(网格中)"""
        return (random.randint(0, GRID_WIDTH - 1),
                random.randint(0, GRID_HEIGHT - 1))

    def draw(self):
        """绘制食物"""
        x_coord = self.position[0] * BLOCK_SIZE
        y_coord = self.position[1] * BLOCK_SIZE
        draw_block(RED, x_coord, y_coord)

# ---------------------
#  游戏主函数
# ---------------------
def main():
    clock = pygame.time.Clock()

    snake = Snake()
    food = Food()

    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_UP:
                    snake.change_direction('UP')
                elif event.key == pygame.K_DOWN:
                    snake.change_direction('DOWN')
                elif event.key == pygame.K_LEFT:
                    snake.change_direction('LEFT')
                elif event.key == pygame.K_RIGHT:
                    snake.change_direction('RIGHT')

        # 2) 更新游戏状态 (蛇移动, 检测碰撞, 食物处理)
        snake.move()

        # 如果蛇头与食物位置相同 => 吃到食物
        if snake.body[0] == food.position:
            score += 1
            snake.grow = True
            food.position = food.random_pos()

        # 检测蛇是否撞墙或撞自己
        if snake.check_collision():
            running = False

        # 3) 绘制游戏界面
        screen.fill(BLACK)    # 清屏(背景黑色)

        snake.draw()
        food.draw()
        draw_text(f"Score: {score}", WHITE, 10, 10)

        pygame.display.flip()

    # 如果跳出主循环,说明游戏结束
    game_over(screen, score)


def game_over(surface, score):
    """
    游戏结束界面,可在此添加重来或退出选项
    """
    surface.fill(GRAY)
    draw_text("Game Over!", WHITE, SCREEN_WIDTH // 2 - 60, SCREEN_HEIGHT // 2 - 30)
    draw_text(f"Your Score: {score}", WHITE, SCREEN_WIDTH // 2 - 70, SCREEN_HEIGHT // 2 + 10)
    pygame.display.flip()

    # 等待若干秒后退出
    pygame.time.wait(3000)
    pygame.quit()
    sys.exit()


if __name__ == "__main__":
    main()

核心逻辑解析

  1. Snake 类

    • 用一个列表 body 存储从头到尾的网格坐标。
    • move() 函数在前面插入新的头部坐标,如不需要增长则移除尾部。
    • check_collision() 判断是否超出网格边界或头部与身体其他部分重合。
  2. Food 类

    • 只需保存一个 position 表示食物在网格的坐标。
    • 当被吃到时,用 random_pos() 重新生成新的位置。
  3. 主循环

    • 通过 clock.tick(FPS) 控制蛇每秒移动多少次。
    • 监听方向键来改变蛇的方向。
    • 判断蛇头和食物是否重合,若重合则得分、食物重生、蛇变长。
    • 若蛇撞墙或撞自己,则游戏结束并跳转到 game_over 界面。

4. 实现效果

在这里插入图片描述

在这里插入图片描述


5. 总结

通过这篇文章,我们用 Python + Pygame 从零实现了一款简单的贪吃蛇游戏。该示例涵盖了网格移动、随机食物生成、碰撞检测、方向控制等基本概念,为你打下初步的游戏开发基础。


http://www.niftyadmin.cn/n/5839729.html

相关文章

FIDL:Flutter与原生通讯的新姿势,不局限于基础数据类型

void initUser(User user); } 2、执行命令./gradlew assembleDebug&#xff0c;生成IUserServiceStub类和fidl.json文件 3、打开通道&#xff0c;向Flutter公开方法 FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { Override void…

人物传记之新月篇

相关故事链接&#xff08;及时更新&#xff09;&#xff1a;Python的那些事第四篇&#xff1a;编程中的智慧之光控制结构-CSDN博客 Python的那些事第五篇&#xff1a;数据结构的艺术与应用-CSDN博客 Python的那些事第六篇&#xff1a;从定义到应用&#xff0c;Python函数的奥秘…

洛谷P3372 【模板】线段树 1以及分块

【模板】线段树 1 题目描述 如题&#xff0c;已知一个数列&#xff0c;你需要进行下面两种操作&#xff1a; 将某区间每一个数加上 k k k。求出某区间每一个数的和。 输入格式 第一行包含两个整数 n , m n, m n,m&#xff0c;分别表示该数列数字的个数和操作的总个数。 …

分享| RL-GPT 框架通过慢agent和快agent结合提高AI解决复杂任务的能力-Arxiv

结论 “RL-GPT: Integrating Reinforcement Learning and Code-as-policy” RL-GPT 框架为解决大语言模型在复杂任务处理中的难题提供了创新有效的途径&#xff0c; 旨在将强化学习&#xff08;RL&#xff09;和代码即策略相结合&#xff0c; 以解决大语言模型&#xff08…

C#,入门教程(08)——基本数据类型及使用的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(07)——软件项目的源文件与目录结构https://blog.csdn.net/beijinghorn/article/details/124139947 数据类型用于指定数据体&#xff08;DataEntity&#xff0c;包括但不限于类或结构体的属性、变量、常量、函数返回值&#xff09;…

KineStop:手机上的智能防晕车助手

KineStop是一款专为晕车用户设计的智能防晕车应用&#xff0c;通过手机传感器精准识别车辆运动状态&#xff0c;并在屏幕上实时提示用户&#xff0c;帮助缓解晕车不适。它无需复杂设置&#xff0c;仅需Android 7.0及以上系统&#xff0c;即可实现“即开即用”&#xff0c;随时随…

[原创](Modern C++)现代C++的关键性概念: 正则表达式

常用网名: 猪头三 出生日期: 1981.XX.XX 企鹅交流: 643439947 个人网站: 80x86汇编小站 编程生涯: 2001年~至今[共24年] 职业生涯: 22年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi、XCode、Eclipse、C Bui…

CentOs9新手教程

CentOS 9是基于RHEL的CentOS Stream版本&#xff0c;主要用于开发和测试环境&#xff0c;不适合作为生产环境的稳定系统。它提供了最新的软件和功能&#xff0c;但可能存在不稳定性和兼容性问题。如果你需要一个稳定的生产环境&#xff0c;建议使用CentOS Linux版本。 安装环境…