pygame贪吃蛇

预习内容

项目分析:
       对于贪吃蛇,需要设计页面布局,在此基础上添加监听器,实现按键响应,然后根据键盘控制改变蛇的位置,设计随机出现的食物,在蛇吃掉食物的情况下改变蛇身长度,在蛇吃到自身或者撞墙的情况下死亡,重新开启游戏。
       

实验目标:
       实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点,得分增加,“蛇”碰到边界或,蛇头与蛇身相撞,蛇死亡,游戏结束。为游戏设计初始欢迎界面,游戏界面,游戏结束界面。

运算符优先级认知

1、环境准备

开始之前,先来安装Pygame。可使用pip模块来帮助下载安装python包。

anaconda用户可按以下操作

pygame贪吃蛇_第1张图片

输入conda list,然后回车

conda list

pygame贪吃蛇_第2张图片

再输入python -m pip install --upgrade pip

然后输入pip install pygame

python -m pip install --upgrade pip
pip install pygame

pygame贪吃蛇_第3张图片

安装成功!

可以输入python -m pygame.examples.aliens来检验是否成功安装

python -m pygame.examples.aliens

pygame贪吃蛇_第4张图片

若正常运行,说明安装成功

2、游戏规则

2.1开始和结束

1.贪吃蛇一开始出生在左上角,只有一节身体一个头

2.蛇如果碰到了自己的身体或者碰到了游戏边界,那么就直接死亡

3.如果死亡或者想要暂停可以按下空格键

2.2运动和控制

1.我们使用监听键盘上的方向键(↑、↓、←、→)来控制蛇的运动轨迹

2.3得分

1.得分设为吃到一个豆子得5分,初始为0分

2.食物必须满足是在游戏窗口随机生成的,如果蛇头跟食物碰到了,那就代表蛇吃到了食物,然后食物再次刷新随机位置

3.食物出现30s内,贪吃蛇没吃到,那么食物就刷新

4.游戏会随着你蛇的增长,也会变快

关键代码分析

(1)蛇身增加与行动

用列表来储存身体,当头部和食物坐标重合时,在列表第0项添加一些身体,通过在蛇头添加一些身体并删除蛇尾来实现移动

(2)随机食物

在game_item.py模块的顶部,导入random模块,以方便使用随机数代码如下import random

在Food类中定义random_rect方法,随机确定游戏窗口的任意格子设置食物出现的位置,每30秒刷新一次,吃到食物时改变移动速度。

(3)蛇死亡判定思路

既要判断舍得死亡又要记录死亡时的状态

①在Snake类定义is_dead方法:如果碰到边界或者身体测返回True

②完善update方法:一旦发现移动身体之后贪吃蛇挂了,则回复之前的身体数据,并且返回Flase,表示无法移动

运行界面

pygame贪吃蛇_第5张图片pygame贪吃蛇_第6张图片

pygame贪吃蛇_第7张图片 pygame贪吃蛇_第8张图片

import random
import pygame

# 全局变量定义
SCREEN_RECT = pygame.Rect(0, 0, 640, 480)       # 游戏窗口矩形区域
CELL_SIZE = 20                                  # 小格子大小

BACKGROUND_COLOR = (255,255, 255)              # 主窗口背景颜色
SCORE_TEXT_COLOR = (0, 0, 0)                  # 分数文字颜色
TIP_TEXT_COLOR = (0, 0, 255)                   # 提示文字颜色

FOOD_UPDATE_EVENT = pygame.USEREVENT            # 食物更新事件
SNAKE_UPDATE_EVENT = pygame.USEREVENT + 1       # 贪吃蛇移动事件


class Label(object):
    """文字标签类"""
    def __init__(self, size=48, is_score=True):

        self.font = pygame.font.SysFont("simhei", size)  # 黑体字
        self.is_score = is_score

    """在窗口中绘制文本内容"""
    def draw(self, window, text):
        # 使用字体渲染文本内容
        color = SCORE_TEXT_COLOR if self.is_score else TIP_TEXT_COLOR
        text_surface = self.font.render(text, True, color)

        # 获得文本图像的矩形区域
        text_rect = text_surface.get_rect()
        # 获得主窗口的矩形区域
        window_rect = window.get_rect()

        # 设置位置
        if self.is_score:  # 分数文字显示在右下角
            text_rect.bottomright = window_rect.bottomright
        else:  # 其他文字显示在中间
            text_rect.center = window_rect.center

        # 在游戏窗口中绘制渲染结果
        window.blit(text_surface, text_rect)

# 食物类
class Food(object):

    def __init__(self):
        self.color = (0, 255, 0)  # 绿色食物
        self.score = 5  # 每颗食物得分 5 分
        self.rect = pygame.Rect(0, 0, CELL_SIZE, CELL_SIZE)  # 食物位置

        self.random_rect()  # 设置食物随机位置

    def random_rect(self):
        col = SCREEN_RECT.w / CELL_SIZE - 1  # 屏幕上小格子的列数
        row = SCREEN_RECT.h / CELL_SIZE - 1  # 屏幕上小格子的行数

        x = random.randint(0, col) * CELL_SIZE
        y = random.randint(0, row) * CELL_SIZE

        self.rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE)
        # 食物初始不可见
        self.rect.inflate_ip(-CELL_SIZE, -CELL_SIZE)

        pygame.time.set_timer(FOOD_UPDATE_EVENT, 30000)  # 设置更新食物事件

    def draw(self, window):

        # # 判断宽度是否达到小格子宽度
        if self.rect.w < CELL_SIZE:
            self.rect.inflate_ip(2, 2)  # 向四周各自放大 1 个像素

        pygame.draw.ellipse(window, self.color, self.rect)

# 贪吃蛇类
class Snake(object):
    def __init__(self):
        self.dir = pygame.K_RIGHT  # 初始向右运动
        self.score = 0  # 初始得分
        self.time_interval = 250 # 运动间隔时间
        self.color = (255, 144, 255)  # 身体颜色-紫粉

        self.body_list = []  # 身体列表

        self.reset_snake()

    """重置蛇属性"""
    def reset_snake(self):
        self.dir = pygame.K_RIGHT
        self.score = 0
        self.time_interval = 250

        self.body_list.clear()  # 清空身体列表

        for i in range(2):  # 添加一节身体
            self.add_node()

    """增加身体"""
    def add_node(self):
        # 判断是否有身体
        if self.body_list:
            head = self.body_list[0].copy()
        else:
            head = pygame.Rect(-CELL_SIZE, 0, CELL_SIZE, CELL_SIZE)

        # 根据运动方向,调整 head 的位置
        if self.dir == pygame.K_RIGHT:
            head.x += CELL_SIZE
        elif self.dir == pygame.K_LEFT:
            head.x -= CELL_SIZE
        elif self.dir == pygame.K_UP:
            head.y -= CELL_SIZE
        elif self.dir == pygame.K_DOWN:
            head.y += CELL_SIZE

        # 将蛇头插入到身体列表第 0 项
        self.body_list.insert(0, head)

        # 设置贪吃蛇移动定时器
        pygame.time.set_timer(SNAKE_UPDATE_EVENT, self.time_interval)

    def draw(self, window):
        # 遍历绘制每一节身体
        for idx, rect in enumerate(self.body_list):
            pygame.draw.rect(window,
                             self.color,
                             rect.inflate(-2, -2),  # 缩小矩形区域
                             idx == 0)              # 蛇头绘制边框不填充

    def update(self):
        """移动贪吃蛇的整个身体
        一旦发现贪吃蛇移动后会死亡,则恢复整个身体数据
        移动成功返回 True,贪吃蛇死亡表示不能移动,返回 False
        """
        # 备份身体列表
        body_list_copy = self.body_list.copy()

        # 移动身体
        self.add_node()         # 沿着运动方向在蛇头位置增加一节身体
        self.body_list.pop()    # 删除蛇尾

        # 判断是否死亡,如果是,恢复备份的身体
        if self.is_dead():
            self.body_list = body_list_copy

            return False

        return True

    """行动"""
    def change_dir(self, to_dir):
        """改变贪吃蛇的运动方向
        :param to_dir: 要变化的方向
        """
        hor_dirs = (pygame.K_RIGHT, pygame.K_LEFT)      # 水平方向
        ver_dirs = (pygame.K_UP, pygame.K_DOWN)         # 垂直方向

        # 判断当前运动方向及要修改的方向
        if ((self.dir in hor_dirs and to_dir not in hor_dirs) or
                (self.dir in ver_dirs and to_dir not in ver_dirs)):

            self.dir = to_dir

    def has_eat(self, food):
        """判断蛇头是否与食物相遇 - 吃到食物

        :param food: 食物对象
        :return: 是否吃到食物
        """

        if self.body_list[0].contains(food.rect):
            self.score += food.score    # 增加分数

            # 修改运动时间间隔(速度)
            if self.time_interval > 100:
                # self.time_interval -= 0.03 * (food.score // 100)
                self.time_interval -= 5

            self.add_node()             # 增加一节身体

            return True

        return False
    """死亡"""
    def is_dead(self):
        """判断贪吃蛇是否死亡

        :return: 死亡返回 True,否则返回 False
        """
        # 记录蛇头的矩形区域
        head = self.body_list[0]

        # 判断蛇头是否移出屏幕
        if not SCREEN_RECT.contains(head):
            return True

        # 判断是否与身体其他部分重叠
        for body in self.body_list[1:]:
            if head.contains(body):
                return True

        return False

# 初始化
pygame.mixer.init()
pygame.init()
window = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Greedy Snake")
clock = pygame.time.Clock()
start_window = pygame.Surface(window.get_size())  # 充当开始界面的画布
start_window2 = pygame.Surface(window.get_size())  # 充当第一关的画布界面暂时占位(可以理解为游戏开始了)
start_window = start_window.convert()
start_window2 = start_window2.convert()
start_window.fill((255, 255, 255))  # 白色画布
start_window2.fill((0, 0, 0))
# 加载各个素材图片 并且赋予变量名

i11 = pygame.image.load("ChosenStart.png")
i11.convert()

#  以下为选择开始界面鼠标检测结构。

n1 = True
while n1:
    clock.tick(30)
    start_window.blit(i11, (200, 240))
    buttons = pygame.mouse.get_pressed()
    # x1, y1 = pygame.mouse.get_pos()
    # if x1 >= 227 and x1 <= 555 and y1 >= 261 and y1 <= 327:
    if buttons[0]:
        n1 = False

    window.blit(start_window, (0, 0))
    pygame.display.update()

    # 下面是监听退出动作

    # 监听事件
    for event in pygame.event.get():

        # 判断事件类型是否是退出事件
        if event.type == pygame.QUIT:
            print("游戏退出...")

            # quit 卸载所有的模块
            pygame.quit()

            # exit() 直接终止当前正在执行的程序
            exit()

#  贪吃蛇代码

n2 = True
while n2:
    clock.tick(30)


    class Game(object):
        """游戏类"""

        def __init__(self):
            self.main_window = pygame.display.set_mode((640, 480))
            pygame.display.set_caption("Greedy Snake")

            self.score_label = Label()  # 得分文本标签
            self.tip_label = Label(24, False)  # 提示标签

            self.is_game_over = False  # 游戏结束标记
            self.is_pause = False  # 游戏暂停标记

            self.food = Food()  # 食物
            self.snake = Snake()  # 贪吃蛇

            print(self.snake.body_list)

        def reset_game(self):
            """游戏复位"""
            self.is_game_over = False  # 游戏结束标记
            self.is_pause = False  # 游戏暂停标记

            self.food.random_rect()  # 重新设置食物位置
            self.snake.reset_snake()  # 设置蛇属性

        def start(self):
            """开始贪吃蛇游戏"""
            clock = pygame.time.Clock()  # 游戏时钟

            while True:

                # 事件监听
                for event in pygame.event.get():  # 遍历同一时刻发生的事件列表
                    if event.type == pygame.QUIT:  # 判断退出事件
                        return
                    elif event.type == pygame.KEYDOWN:  # 判断按键事件
                        if event.key == pygame.K_ESCAPE:
                            return
                        elif event.key == pygame.K_SPACE:
                            if self.is_game_over:
                                self.reset_game()
                            else:
                                self.is_pause = not self.is_pause

                    # 仅在游戏状态才会更新食物、移动蛇的位置以及改变蛇的运动方向
                    if not self.is_pause and not self.is_game_over:
                        if event.type == FOOD_UPDATE_EVENT:  # 判断更新食物事件
                            self.food.random_rect()
                        if event.type == SNAKE_UPDATE_EVENT:  # 判断贪吃蛇移动事件
                            self.is_game_over = not self.snake.update()
                        if event.type == pygame.KEYDOWN:  # 判断按键事件
                            # 判断是否方向键
                            if event.key in (pygame.K_LEFT, pygame.K_RIGHT,
                                             pygame.K_UP, pygame.K_DOWN):
                                self.snake.change_dir(event.key)

                # 依次绘制游戏元素
                self.main_window.fill(BACKGROUND_COLOR)

                # 判断游戏状态
                if self.is_game_over:

                    self.tip_label.draw(self.main_window, "游戏结束,按空格键开启新游戏...")

                elif self.is_pause:

                    self.tip_label.draw(self.main_window, "游戏暂停,按空格键继续...")

                else:
                    # 判断是否吃到食物
                    if self.snake.has_eat(self.food):
                        self.food.random_rect()

                self.score_label.draw(self.main_window, "得分:%d" % self.snake.score)
                self.food.draw(self.main_window)
                self.snake.draw(self.main_window)

                # 更新显示
                pygame.display.update()

                clock.tick(60)  # 刷新帧率


    if __name__ == '__main__':
        pygame.init()  # 初始化所有模块

        # 游戏代码
        Game().start()  # 创建游戏对象并且启动游戏

        pygame.quit()  # 取消初始化所有模块
    pygame.display.update()
    for event in pygame.event.get():

        # 判断事件类型是否是退出事件
        if event.type == pygame.QUIT:
            print("游戏退出...")

            # quit 卸载所有的模块
            pygame.quit()

            # exit() 直接终止当前正在执行的程序
            exit()

你可能感兴趣的:(pygame,python,开发语言)