pygame贪吃蛇

这周的pygame程序我是一个都没有写出来,在这里就借一位大神同学的代码贴在这好了,希望自己以后也能够单独写出这样的程序。

import pygame
from random import randint
from abc import ABCMeta, abstractmethod

BLACK_COLOR = (0, 0, 0)  # 全局常量,黑色
RED_COLOR = (255, 0, 0)  # 全局常量,红色
FOOD_COLOR = (230, 185, 185)  # 全局常量,食物的颜色
GREEN_COLOR = (0, 255, 0)  # 绿色

UP = 0  # 向上
RIGHT = 1  # 向右
DOWN = 2  # 向下
LEFT = 3  # 向左


class GameObject(object, metaclass=ABCMeta):
    """游戏物品抽象父类"""

    def __init__(self, pos=(0, 0), color=BLACK_COLOR):
        """初始化方法"""
        self._pos = pos  # 在界面中的位置
        self._color = color  # 在界面中的颜色

    @abstractmethod
    def draw(self, screen):
        """
        抽象方法,绘制对象

        :param screen: 在哪个图像表面层绘画
        :return: None
        """
        pass

    @property
    def pos(self):
        """
        装饰器修饰,直接获取对象在界面中的位置

        :return: 元组,(x,y)
        """
        return self._pos


class Wall(GameObject):
    """四周墙的类"""

    def __init__(self, pos, width, height, tick, color=BLACK_COLOR):
        """
        初始化、构造方法

        :param pos: 墙开始画的坐标点
        :param width: 墙的宽度
        :param height: 墙的高度
        :param tick: 墙的厚度
        :param color: 墙的颜色
        """
        super().__init__(pos, color)
        self._width = width
        self._height = height
        self._tick = tick

    @property
    def width(self):
        return self._width

    @property
    def height(self):
        return self._height

    def draw(self, screen):
        pygame.draw.rect(screen, self._color,
                         (self._pos[0], self._pos[1], self._width, self._height), self._tick)


class Food(GameObject):
    """食物类"""

    def __init__(self, pos, size=10, color=RED_COLOR):
        """
        初始化方法

        :param pos: 外切圆坐标
        :param color:
        :param size:
        """
        super().__init__(pos, color)
        self._is_hide = 0  # 是否隐藏的计数器,为了食物点不频繁的闪烁
        self._size = size  # 食物点的大小,直径

    def draw(self, screen):
        """
        绘制食物的方法

        :param screen: 在哪个表面绘制
        :return: None
        """
        # 如果不是5的倍数,就消失一次,减少Boolean值的频繁闪烁,数值越大闪烁越快
        if self._is_hide % 5 != 0:
            # 开始在界面进行绘制食物
            pygame.draw.circle(screen, self._color,
                               (self._pos[0] + self._size // 2, self._pos[1] + self._size // 2),
                               self._size // 2, 0)
        self._is_hide += 1  # 计数点自加


class SnakeNode(GameObject):
    """蛇的结点类"""

    def __init__(self, pos, size, color=GREEN_COLOR):
        """
        初始化构造方法

        :param pos: 结点的坐标
        :param size: 结点的大小
        :param color: 结点的颜色
        """
        super().__init__(pos, color)
        self._size = size

    def draw(self, screen):
        """
        绘制结点

        :param screen: 在哪个表面进行绘制
        :return: None
        """
        # 绘制内部绿色的结点
        pygame.draw.rect(screen, self._color,
                         (self._pos[0], self._pos[1], self._size, self._size), 0)
        # 绘制结点的外部边框
        pygame.draw.rect(screen, BLACK_COLOR,
                         (self._pos[0], self._pos[1], self._size, self._size), 1)

    @property
    def size(self):
        """
        装饰器,修饰get_size方法

        :return: 结点的大小
        """
        return self._size


class Snake(GameObject):
    """蛇的类"""

    def __init__(self):
        """
        初始化构造方法

        """
        super().__init__()
        self._snake_dir = LEFT  # 默认的蛇爬行的方向为左
        self._nodes = []  # 蛇的结点容器
        self._aline = True  # 蛇是否存活
        self._eat_food = False  # 是否吃到食物
        for index in range(5):  # 初始化5个结点在列表容器中
            node = SnakeNode((290 + index * 20, 250), 20)
            self._nodes.append(node)

    def is_in_nodes(self, x, y):
        """
        判断刷新出来的苹果是否在蛇的结点上

        :param x:  食物的x坐标
        :param y:  食物的y坐标
        :return: boolean 值,在为True,不在为False
        """
        for node in self._nodes:
            if node.pos[0] == x and node.pos[1] == y:
                return True
        return False

    @property
    def aline(self):
        """
        装饰器,获取对象属性

        :return: aline属性
        """
        return self._aline

    @property
    def snake_dir(self):
        """
        装饰器,获取蛇移动的方向

        :return: snake_dir 属性
        """
        return self._snake_dir

    @property
    def head(self):
        """
        装饰器,获取容器中的第一个结点

        :return: nodes[0] 第一个元素
        """
        return self._nodes[0]

    def change_dir(self, new_dir):
        """
        改变蛇前进的方向

        :param new_dir:  前进的新方向
        :return: None
        """
        # 如果方向和原来方向不同,而且不相反,就可以改变,蛇的前进方向
        if new_dir != self._snake_dir and (self._snake_dir + new_dir) % 2 != 0:
            self._snake_dir = new_dir

    def draw(self, screen):
        """
        画出蛇

        :param screen: 在哪个表面画蛇
        :return: None
        """
        # 遍历结点,同时画出结点
        for node in self._nodes:
            node.draw(screen)

    def move(self):
        """
        蛇移动的方法

        :return: None
        """
        # if self._aline:
        head = self.head  # 获取蛇头
        snake_dir = self._snake_dir  # 获取蛇前进的方向
        # 获取蛇头想x,y坐标和头的大小
        x, y, size = head.pos[0], head.pos[1], head.size
        if snake_dir == UP:  # 向上移动
            y -= size  # y坐标减少
        elif snake_dir == RIGHT:  # 向右移动
            x += size  # x坐标增加
        elif snake_dir == DOWN:  # 向下移动
            y += size  # y坐标增加
        else:  # 向左移动
            x -= size  # x坐标减少
        new_head = SnakeNode((x, y), size)  # 新建一个结点对象
        self._nodes.insert(0, new_head)  # 添加到蛇头
        if self._eat_food:  # 如果吃到了食物
            self._eat_food = False  # 重设参数为没吃到,同时不移除最后的一个结点
        else:  # 没吃到食物
            self._nodes.pop()  # 移除最后的一个结点元素

    def collide(self, wall):
        """
        判断是否撞墙

        :param wall: 墙的对象
        :return: None
        """
        x, y = wall.pos  # 获取墙的坐标
        width = wall.width  # 获取墙的宽
        height = wall.height  # 获取墙的高
        node_one = self._nodes[0]  # 获取蛇头对象
        # 如果蛇头的x坐标小于了墙的最左边,或加上自身宽度大于了墙的初始位置加宽度,
        # 或蛇头的y坐标小于了墙最上面,或加上自身宽度大于了墙初始xy位置加高度
        if x > node_one.pos[0] or node_one.pos[0] + node_one.size > x + width \
                or y > node_one.pos[1] or node_one.pos[1] + node_one.size > y + height:
            self._aline = False  # 蛇撞到墙,死了
        else:
            self._aline = True  # 蛇没撞到墙,还或者

    def eat_food(self, food):
        """
        蛇吃食物的行为

        :param food: food对象
        :return: Boolean 是否吃到
        """
        head = self.head  # 获取蛇头结点
        # 如果蛇头的x,y坐标与食物的x,y坐标重合,那么就吃到了食物
        if head.pos[0] == food.pos[0] and head.pos[1] == food.pos[1]:
            self._eat_food = True  # 吃到食物的状态为True
            return True  # 返回True
        return False  # 没吃到食物返回False

    def eat_me(self):
        """
        吃自己判断

        :return: None
        """
        head = self.head  # 获取蛇头结点对象
        # 从蛇的第4个结点开始遍历,因为,蛇只能吃到自己的第四个结点
        for index in range(3, len(self._nodes)):
            # 如果蛇头坐标和蛇身坐标重合,就吃到了自己
            if head.pos[0] == self._nodes[index].pos[0] and \
                    head.pos[1] == self._nodes[index].pos[1]:
                self._aline = False  # 修改当前蛇的存活状态

    @property
    def snake_len(self):
        """
        获取蛇的结点长度

        :return: 蛇的长度
        """
        return len(self._nodes)


def main():
    """游戏主函数"""

    def refresh():
        """刷新游戏窗口"""
        screen.fill((242, 242, 242))
        snake.draw(screen)
        food.draw(screen)
        wall.draw(screen)
        pygame.display.flip()

    def handle_key_event(key_event):
        """游戏按键事件监听"""
        key = key_event.key  # 拿取到所按的按键的值
        if key == pygame.K_F2:  # 如果为F2则重新开始游戏
            reset_game()
        # 如果key为w,a,s,d则为方向控制
        elif key in (pygame.K_w, pygame.K_d, pygame.K_s, pygame.K_a):
            if snake.aline:  # 如果蛇还活着则进行操作
                if key == pygame.K_w:  # 按下了W
                    new_dir = UP  # 设置新的方向向上
                elif key == pygame.K_d:  # 按下了D
                    new_dir = RIGHT  # 设置新的方向向右
                elif key == pygame.K_s:  # 按下了S
                    new_dir = DOWN  # 设置新的方向向下
                else:  # 按下了A
                    new_dir = LEFT  # 设置新的方向向左
                snake.change_dir(new_dir)  # 调用蛇的方法,发送消息改变方向
        pygame.event.clear()  # 清除按键事件

    def reset_game():
        """重设游戏的食物和蛇的对象,游戏重开"""
        nonlocal food, snake, game_over  # 设置为嵌套值域,而非当前函数
        food = create_apple(snake)  # 创建新的食物对象
        snake = Snake()  # 创建新的蛇对象
        game_over = False  # 游戏继续

    scores = []  # 分数容器
    pygame.init()  # 初始化pygame
    wall = Wall((10, 10), 600, 600, 3)  # 创建墙的对象
    screen = pygame.display.set_mode([620, 620])  # 设置窗口大小
    screen.fill((242, 242, 242))  # 设置填充的背景
    pygame.display.set_caption('贪吃蛇')  # 设置窗口标题
    pygame.display.flip()  # 刷新界面
    clock = pygame.time.Clock()  # 设置时钟
    snake = Snake()  # 初始化一个蛇对象
    food = create_apple(snake)  # 初始化一个食物对象
    reset_game()  # 重开游戏,刷新界面
    is_running = True  # 游戏运行状态
    game_over = False  # 游戏当前回合状态
    while is_running:
        for event in pygame.event.get():  # 获取事件
            if event.type == pygame.QUIT:  # 判断事件类型是否为退出
                is_running = False
            elif event.type == pygame.KEYDOWN:
                handle_key_event(event)  # 传递按钮点击事件
        if not game_over:  # 本回合是否继续
            if snake.aline:  # 当前蛇是否还存活
                refresh()  # 刷新游戏窗口
                snake.move()  # 蛇开始移动
                snake.collide(wall)  # 判断蛇是否撞墙
                snake.eat_me()  # 判断蛇是否吃到自己
                if snake.eat_food(food):  # 判断蛇是否吃到食物
                    food = create_apple(snake)  # 吃到食物,创建一个新的食物对象
            else:  # 本回合结束
                game_over = True  # 设置本回合结束
                scores.append(snake.snake_len - 5)  # 将分数添加到容器中
                show_info(screen, scores)  # 显示分数
        clock.tick(20)  # 帧数
    pygame.quit()  # 销毁pygame
    pass


def show_info(screen, scores):
    """
    在界面中绘制分数的函数

    :param screen:  绘制的表面
    :param scores:  绘制的分数列表
    :return:  None
    """
    scores.sort(reverse=True)  # 对分数进行排序
    my_font = pygame.font.SysFont('宋体', 60)  # 设置字体格式
    game_over = my_font.render('GAME OVER', False, [0, 0, 0])  # 字体的文本和颜色
    screen.blit(game_over, (180, 260))  # 开始绘制的坐标
    list_len = len(scores) if len(scores) < 5 else 5  # 获取分数容器的大小,最多展示前五
    for index in range(list_len):  # 遍历欠前五的分数
        my_font = pygame.font.SysFont('宋体', 40)  # 设置字体
        # 绘制的文字内容和分数
        score = my_font.render('Number ' + str(index + 1) + ':' + str(scores[index]), False, [255, 0, 0])
        screen.blit(score, (200, 300 + index * 40))  # 绘制的位置
    pygame.display.flip()  # 刷新界面


def create_apple(snake):
    """
    创建苹果在蛇的身体之外

    :param snake:  蛇的对象
    :return: 在蛇身之外的Food坐标
    """
    row = randint(0, 29)  # 随机行号
    col = randint(0, 29)  # 随机列号
    x, y = 10 + 20 * col, 10 + 20 * row  # 换算为对应的坐标点
    if not snake.is_in_nodes(x, y):  # 如果不在蛇身上
        return Food((10 + 20 * col, 10 + 20 * row), 20)  # 返回一个食物对象
    else:
        create_apple(snake)  # 递归调用函数直到有返回值


if __name__ == '__main__':
    main()
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值