Python贪吃蛇游戏开发教程

260 阅读7分钟

📚 前言

编程基础第一期《10-30》--贪吃蛇游戏教程,贪吃蛇是一款经典的休闲游戏,也是Python初学者进阶的绝佳练习项目。本教程将详细讲解如何使用Python的pygame库开发一个完整的贪吃蛇游戏,并解析其中涉及的编程知识点。

🛠️ 开发环境准备

  • Python 3.6+
  • Pygame库

安装pygame库:

pip install pygame

🧩 游戏核心概念

1. 游戏循环

贪吃蛇游戏基于一个主循环,包括:

  • 处理用户输入
  • 更新游戏状态
  • 渲染画面

2. 坐标系统

我们使用网格坐标系统,将游戏区域划分为若干个方格,蛇和食物都占据其中的格子。

3. 碰撞检测

需要检测:

  • 蛇头是否碰到墙壁
  • 蛇头是否碰到自己的身体
  • 蛇头是否碰到食物

💡 代码实现与知识点解析

1. 导入必要的库

import pygame
import sys
import random
import time

知识点

  • pygame:Python游戏开发库,提供图形、声音、输入等功能
  • sys:系统相关功能,用于退出程序
  • random:生成随机数,用于食物的随机位置
  • time:时间相关功能,用于控制游戏速度

2. 初始化游戏设置

# 初始化pygame
pygame.init()

# 颜色定义 (R, G, B)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# 游戏参数设置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 20
GRID_WIDTH = SCREEN_WIDTH // GRID_SIZE
GRID_HEIGHT = SCREEN_HEIGHT // GRID_SIZE
SNAKE_SPEED = 10  # 帧率控制

# 方向常量
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)

知识点

  • 常量定义:使用全大写变量名表示常量
  • RGB颜色模型:使用三元组表示颜色
  • 整除运算符(//):确保网格大小为整数
  • 元组表示方向:使用二维向量表示移动方向

3. 创建游戏窗口

# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Python贪吃蛇')
clock = pygame.time.Clock()

知识点

  • pygame.display.set_mode:创建游戏窗口
  • pygame.display.set_caption:设置窗口标题
  • pygame.time.Clock:创建时钟对象,用于控制游戏帧率

4. 定义蛇类

class Snake:
    def __init__(self):
        self.positions = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]  # 蛇身体位置列表,初始在屏幕中央
        self.direction = RIGHT  # 初始方向向右
        self.next_direction = RIGHT  # 下一步方向
        self.grow = False  # 是否增长

    def get_head_position(self):
        return self.positions[0]  # 获取蛇头位置

    def update(self):
        # 更新方向
        self.direction = self.next_direction
        
        # 获取蛇头位置
        head = self.get_head_position()
        
        # 计算新的蛇头位置
        new_x = (head[0] + self.direction[0]) % GRID_WIDTH
        new_y = (head[1] + self.direction[1]) % GRID_HEIGHT
        new_head = (new_x, new_y)
        
        # 检查是否碰到自己
        if new_head in self.positions[1:]:
            return False  # 游戏结束
        
        # 添加新的蛇头
        self.positions.insert(0, new_head)
        
        # 如果不需要增长,则移除蛇尾
        if not self.grow:
            self.positions.pop()
        else:
            self.grow = False
            
        return True  # 继续游戏

    def change_direction(self, direction):
        # 防止直接反向移动
        if (direction[0] * -1, direction[1] * -1) == self.direction:
            return
        self.next_direction = direction

    def grow_snake(self):
        self.grow = True

    def draw(self, surface):
        for i, pos in enumerate(self.positions):
            color = GREEN if i == 0 else BLUE  # 蛇头绿色,身体蓝色
            rect = pygame.Rect(pos[0] * GRID_SIZE, pos[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)
            pygame.draw.rect(surface, color, rect)
            pygame.draw.rect(surface, BLACK, rect, 1)  # 绘制网格边框

知识点

  • 类的定义与实例化:面向对象编程
  • 列表操作:insert添加元素,pop移除元素
  • 模运算(%):实现穿墙效果
  • 条件判断:检查碰撞和方向变化
  • 绘图函数:使用pygame.draw.rect绘制矩形

5. 定义食物类

class Food:
    def __init__(self):
        self.position = (0, 0)
        self.randomize_position()

    def randomize_position(self):
        self.position = (random.randint(0, GRID_WIDTH - 1), 
                         random.randint(0, GRID_HEIGHT - 1))

    def draw(self, surface):
        rect = pygame.Rect(self.position[0] * GRID_SIZE, 
                          self.position[1] * GRID_SIZE, 
                          GRID_SIZE, GRID_SIZE)
        pygame.draw.rect(surface, RED, rect)
        pygame.draw.rect(surface, BLACK, rect, 1)

知识点

  • 随机数生成:使用random.randint生成随机整数
  • 类的方法:定义类的行为

6. 游戏主循环

def main():
    snake = Snake()
    food = Food()
    score = 0
    game_over = False

    # 游戏主循环
    while True:
        # 处理事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if game_over:
                    if event.key == pygame.K_RETURN:
                        # 重新开始游戏
                        snake = Snake()
                        food = Food()
                        score = 0
                        game_over = False
                else:
                    # 方向控制
                    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)

        if not game_over:
            # 更新游戏状态
            if not snake.update():
                game_over = True

            # 检查是否吃到食物
            if snake.get_head_position() == food.position:
                snake.grow_snake()
                food.randomize_position()
                # 确保食物不出现在蛇身上
                while food.position in snake.positions:
                    food.randomize_position()
                score += 1

        # 绘制游戏画面
        screen.fill(BLACK)
        snake.draw(screen)
        food.draw(screen)
        
        # 显示分数
        font = pygame.font.SysFont('arial', 20)
        score_text = font.render(f'得分: {score}', True, WHITE)
        screen.blit(score_text, (10, 10))
        
        # 游戏结束显示
        if game_over:
            font = pygame.font.SysFont('arial', 50)
            game_over_text = font.render('游戏结束!', True, WHITE)
            restart_text = font.render('按回车键重新开始', True, WHITE)
            screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 50))
            screen.blit(restart_text, (SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2 + 10))

        # 更新屏幕
        pygame.display.update()
        clock.tick(SNAKE_SPEED)  # 控制游戏速度

if __name__ == "__main__":
    main()

==总代码==

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Python贪吃蛇游戏
作者: Python游戏开发者
功能: 一个完整的贪吃蛇游戏,使用Pygame库开发
"""

import pygame
import sys
import random
import time

# 初始化pygame
pygame.init()

# 颜色定义 (R, G, B)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# 游戏参数设置
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
GRID_SIZE = 20
GRID_WIDTH = SCREEN_WIDTH // GRID_SIZE
GRID_HEIGHT = SCREEN_HEIGHT // GRID_SIZE
SNAKE_SPEED = 10  # 帧率控制

# 方向常量
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)

# 创建游戏窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('Python贪吃蛇')
clock = pygame.time.Clock()


class Snake:
    def __init__(self):
        self.positions = [(GRID_WIDTH // 2, GRID_HEIGHT // 2)]  # 蛇身体位置列表,初始在屏幕中央
        self.direction = RIGHT  # 初始方向向右
        self.next_direction = RIGHT  # 下一步方向
        self.grow = False  # 是否增长

    def get_head_position(self):
        return self.positions[0]  # 获取蛇头位置

    def update(self):
        # 更新方向
        self.direction = self.next_direction
        
        # 获取蛇头位置
        head = self.get_head_position()
        
        # 计算新的蛇头位置
        new_x = (head[0] + self.direction[0]) % GRID_WIDTH
        new_y = (head[1] + self.direction[1]) % GRID_HEIGHT
        new_head = (new_x, new_y)
        
        # 检查是否碰到自己
        if new_head in self.positions[1:]:
            return False  # 游戏结束
        
        # 添加新的蛇头
        self.positions.insert(0, new_head)
        
        # 如果不需要增长,则移除蛇尾
        if not self.grow:
            self.positions.pop()
        else:
            self.grow = False
            
        return True  # 继续游戏

    def change_direction(self, direction):
        # 防止直接反向移动
        if (direction[0] * -1, direction[1] * -1) == self.direction:
            return
        self.next_direction = direction

    def grow_snake(self):
        self.grow = True

    def draw(self, surface):
        for i, pos in enumerate(self.positions):
            color = GREEN if i == 0 else BLUE  # 蛇头绿色,身体蓝色
            rect = pygame.Rect(pos[0] * GRID_SIZE, pos[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)
            pygame.draw.rect(surface, color, rect)
            pygame.draw.rect(surface, BLACK, rect, 1)  # 绘制网格边框


class Food:
    def __init__(self):
        self.position = (0, 0)
        self.randomize_position()

    def randomize_position(self):
        self.position = (random.randint(0, GRID_WIDTH - 1), 
                         random.randint(0, GRID_HEIGHT - 1))

    def draw(self, surface):
        rect = pygame.Rect(self.position[0] * GRID_SIZE, 
                          self.position[1] * GRID_SIZE, 
                          GRID_SIZE, GRID_SIZE)
        pygame.draw.rect(surface, RED, rect)
        pygame.draw.rect(surface, BLACK, rect, 1)


def main():
    snake = Snake()
    food = Food()
    score = 0
    game_over = False

    # 游戏主循环
    while True:
        # 处理事件
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if game_over:
                    if event.key == pygame.K_RETURN:
                        # 重新开始游戏
                        snake = Snake()
                        food = Food()
                        score = 0
                        game_over = False
                else:
                    # 方向控制
                    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)

        if not game_over:
            # 更新游戏状态
            if not snake.update():
                game_over = True

            # 检查是否吃到食物
            if snake.get_head_position() == food.position:
                snake.grow_snake()
                food.randomize_position()
                # 确保食物不出现在蛇身上
                while food.position in snake.positions:
                    food.randomize_position()
                score += 1

        # 绘制游戏画面
        screen.fill(BLACK)
        snake.draw(screen)
        food.draw(screen)
        
        # 显示分数
        font = pygame.font.SysFont('arial', 20)
        score_text = font.render(f'得分: {score}', True, WHITE)
        screen.blit(score_text, (10, 10))
        
        # 游戏结束显示
        if game_over:
            font = pygame.font.SysFont('arial', 50)
            game_over_text = font.render('游戏结束!', True, WHITE)
            restart_text = font.render('按回车键重新开始', True, WHITE)
            screen.blit(game_over_text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 50))
            screen.blit(restart_text, (SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2 + 10))

        # 更新屏幕
        pygame.display.update()
        clock.tick(SNAKE_SPEED)  # 控制游戏速度


if __name__ == "__main__":
    main() 

知识点

  • 事件处理:捕获键盘输入和窗口关闭事件
  • 游戏状态管理:跟踪游戏是否结束
  • 碰撞检测:检查蛇头是否碰到食物
  • 文本渲染:显示分数和游戏结束信息
  • 主循环控制:使用时钟控制游戏帧率
  • if __name__ == "__main__" 结构:确保代码作为主程序运行时才执行

🎮 游戏功能

  1. 基础玩法

    • 使用方向键控制蛇的移动
    • 吃到食物后蛇身增长,得分增加
    • 碰到自己身体游戏结束
    • 支持穿墙功能
  2. 游戏界面

    • 显示当前得分
    • 游戏结束提示
    • 支持重新开始

🔍 进阶知识点

  1. 游戏循环模式

    • 输入处理 → 状态更新 → 渲染 → 循环
  2. 面向对象编程

    • 封装:将蛇和食物的属性和行为封装在类中
    • 继承:可以扩展为更复杂的游戏对象
    • 多态:不同对象可以有自己的绘制方法
  3. 碰撞检测算法

    • 基于网格的简单碰撞检测
  4. 状态管理

    • 游戏状态(进行中/结束)的管理
    • 重置游戏状态

📝 总结

通过这个贪吃蛇游戏项目,我们学习了以下Python编程知识:

  1. Pygame库的基本使用
  2. 面向对象编程思想
  3. 游戏循环和状态管理
  4. 事件处理机制
  5. 碰撞检测算法
  6. 随机数生成
  7. 图形绘制和文本渲染
  8. 键盘输入处理

这个项目是Python游戏开发的良好起点,掌握了这些基础知识后,你可以尝试开发更复杂的游戏或者对现有游戏进行扩展。


💡 编程小贴士:游戏开发是学习编程的绝佳方式,它结合了算法、数据结构、图形、音频等多方面知识,同时能给你带来成就感和乐趣。