pygame - 贪吃蛇小游戏

news2024/11/25 1:03:04
蛇每吃掉一个身体块,蛇身就增加一个长度。为了统一计算,界面的尺寸和游戏元素的位置都是身体块长度的倍数
1. 上下左右方向键(或者ASDW键)控制蛇的移动方向
2. 空格键暂停和继续

图片文件,复制到项目的asset\img目录下

import sys
import pygame
from pygame import Rect, font
import random


# control panel contains the controllers and score
ControlPanelColor = (100, 100, 100)
# game panel is the main area for gaming
GamePanelColor = (0, 0, 0)
SnakeSize = 30
MaxWidthBlock = 15
MaxHeightBlock = 10
ControlPanelHeightBlock = 2
SnakeStartX = MaxWidthBlock // 2
SnakeStartY = MaxHeightBlock // 2
ControlPanelHeight = ControlPanelHeightBlock * SnakeSize
GamePanelWidth = MaxWidthBlock * SnakeSize
GamePanelHeight = MaxHeightBlock * SnakeSize
ControlPanelRect = Rect((0, 0), (GamePanelWidth, ControlPanelHeight))
GamePanelRect = Rect((0, ControlPanelHeight), (GamePanelWidth, GamePanelHeight - ControlPanelHeight))
Tick = 20
Tick_Snake_Move = 10
# two buttons to increase and decrease the game speed
minus_btn_rect = None
plus_btn_rect = None
# score
score_value = 0


# the SnakeBody
class SnakeBody:
    def __init__(self, x, y, direction, ishead=False, istail=False):
        '''
        身体块,蛇身是由多个身体块组成,头和尾也是图案不同的身体块
        :param x: 身体块坐标x
        :param y: 身体块坐标y
        :param direction: 身体块显示的方向
        :param ishead: 是否头身体块
        :param istail:是否尾身体块
        '''
        self.__x = x
        self.__y = y
        self.__ishead = ishead
        self.__istail = istail
        self.__direction = direction

        name = None
        if self.__ishead:
            name = "head.png"
        elif self.__istail:
            name = "tail.png"
        else:
            name = "body.png"

        angle = 0
        match direction:
            case pygame.K_UP:
                angle = 0
            case pygame.K_DOWN:
                angle = 180
            case pygame.K_LEFT:
                angle = 90
            case pygame.K_RIGHT:
                angle = -90

        img = pygame.image.load(f"asset/img/{name}")
        img = pygame.transform.rotate(img, angle)
        self.image = pygame.transform.scale(img, (SnakeSize, SnakeSize))

    def get_rect(self):
        return Rect((self.__x, self.__y), (SnakeSize, SnakeSize))

    def move(self, x, y):
        self.__x = self.__x + x
        self.__y = self.__y + y

    def set_direction(self, direction):
        if self.__direction == direction:
            return

        self.__direction = direction
        name = None
        if self.__ishead:
            name = "head.png"
        elif self.__istail:
            name = "tail.png"
        else:
            name = "body.png"

        angle = 0
        match direction:
            case pygame.K_UP:
                angle = 0
            case pygame.K_DOWN:
                angle = 180
            case pygame.K_LEFT:
                angle = 90
            case pygame.K_RIGHT:
                angle = -90

        img = pygame.image.load(f"asset/img/{name}")
        img = pygame.transform.rotate(img, angle)
        self.image = pygame.transform.scale(img, (SnakeSize, SnakeSize))

    def get_direction(self):
        return self.__direction


class Snake:
    bodys = []
    new_body = None
    __new_direction = pygame.K_UP
    __tick_movement = 0
    __tick_create_body = 0
    __stop = False
    __is_paused = False

    def __init__(self):
        self.bodys.insert(0, SnakeBody(SnakeSize * SnakeStartX, SnakeSize * SnakeStartY, pygame.K_UP, True, False))
        self.bodys.insert(1,
                          SnakeBody(SnakeSize * SnakeStartX, SnakeSize * (SnakeStartY + 1), pygame.K_UP, False, False))
        self.bodys.insert(2,
                          SnakeBody(SnakeSize * SnakeStartX, SnakeSize * (SnakeStartY + 2), pygame.K_UP, False, True))

    def set_direction(self, direction):
        # do not set inverse direction
        if ((self.bodys[0].get_direction() == pygame.K_UP and direction != pygame.K_DOWN) or
                (self.bodys[0].get_direction() == pygame.K_DOWN and direction != pygame.K_UP) or
                (self.bodys[0].get_direction() == pygame.K_LEFT and direction != pygame.K_RIGHT) or
                (self.bodys[0].get_direction() == pygame.K_RIGHT and direction != pygame.K_LEFT)):
            self.__new_direction = direction

    def move(self):
        if self.__stop:
            return
        if self.__is_paused:
            return

        self.__tick_movement += 1
        if self.__tick_movement <= Tick_Snake_Move:
            return

        self.__tick_movement = 0
        length = len(self.bodys)

        head = self.bodys[0]
        oldheadpos = head.get_rect()
        oldheaddirection = head.get_direction()
        # update head direction and move
        head.set_direction(self.__new_direction)
        match self.__new_direction:
            case pygame.K_UP:
                head.move(0, -SnakeSize)
            case pygame.K_DOWN:
                head.move(0, SnakeSize)
            case pygame.K_LEFT:
                head.move(-SnakeSize, 0)
            case pygame.K_RIGHT:
                head.move(SnakeSize, 0)

        if ((self.new_body is not None) and
                (head.get_rect().x == self.new_body.get_rect().x and head.get_rect().y == self.new_body.get_rect().y)):
            # as head move, the old head position is empty,
            # add the new body at the second position
            self.new_body.set_direction(head.get_direction())
            offsetx = oldheadpos.x - self.new_body.get_rect().x
            offsety = oldheadpos.y - self.new_body.get_rect().y
            self.new_body.move(offsetx, offsety)

            self.bodys.insert(1, self.new_body)
            self.new_body = None
            global score_value
            score_value += 1
        else:
            # as head move, the old head position is empty,
            # move the second-to-last body to the second body
            second2lastbody = self.bodys[length - 2]
            second2lastpos = second2lastbody.get_rect()
            second2lastdirection = second2lastbody.get_direction()
            offsetx = oldheadpos.x - second2lastpos.x
            offsety = oldheadpos.y - second2lastpos.y
            second2lastbody.set_direction(oldheaddirection)
            second2lastbody.move(offsetx, offsety)
            self.bodys.remove(second2lastbody)
            self.bodys.insert(1, second2lastbody)
            # move tail to the direction of the second-to-last body
            tailbody = self.bodys[length - 1]
            tailbody.set_direction(second2lastdirection)
            offsetx = second2lastpos.x - tailbody.get_rect().x
            offsety = second2lastpos.y - tailbody.get_rect().y
            tailbody.move(offsetx, offsety)

    def stop(self):
        self.__stop = True

    def create_body(self):
        self.__tick_create_body += 1
        if self.__tick_create_body <= 30:
            return
        if self.is_paused():
            return

        self.__tick_create_body = 0

        if self.new_body is not None:
            return

        x, y = 0, 0
        while True:
            isspare = True
            intx = random.randint(0, MaxWidthBlock - 1)
            inty = random.randint(ControlPanelHeightBlock, MaxHeightBlock - 1)
            x = intx * SnakeSize
            y = inty * SnakeSize
            for b in self.bodys:
                rect = b.get_rect()
                if rect.x == x and rect.y == y:
                    isspare = False
                    break
            if isspare:
                break

        print(f"create body block at {intx}, {inty}")
        self.new_body = SnakeBody(x, y, pygame.K_UP, False, False)

    def is_collided(self):
        iscollided = False
        head = self.bodys[0]
        headrect = self.bodys[0].get_rect()
        # boundary collision
        if headrect.x <= (0 - SnakeSize) or headrect.x >= GamePanelWidth or \
                headrect.y <= (ControlPanelHeight - SnakeSize) or headrect.y >= (ControlPanelHeight + GamePanelHeight):
            iscollided = True
        # body collision
        else:
            if head.get_direction() == pygame.K_LEFT:
                pass
            for b in self.bodys[1:len(self.bodys)]:
                if head.get_rect().colliderect(b.get_rect()):
                    iscollided = True
                    break
        return iscollided

    def pause(self):
        self.__is_paused = not self.__is_paused

    def is_paused(self):
        return self.__is_paused


def display_result():
    final_text1 = "Game Over"
    final_surf = pygame.font.SysFont("Arial", SnakeSize * 2).render(final_text1, 1, (242, 3, 36))  # 设置颜色
    screen.blit(final_surf, [screen.get_width() / 2 - final_surf.get_width() / 2,
                             screen.get_height() / 2 - final_surf.get_height() / 2])  # 设置显示位置


def display_paused():
    paused_text = "Paused"
    paused_surf = pygame.font.SysFont("Arial", SnakeSize * 2).render(paused_text, 1, (242, 3, 36))
    screen.blit(paused_surf, [screen.get_width() / 2 - paused_surf.get_width() / 2,
                              screen.get_height() / 2 - paused_surf.get_height() / 2])


def display_control_panel():
    global minus_btn_rect, plus_btn_rect
    color = (242, 3, 36)
    speed_text = "Speed"
    speed_surf = pygame.font.SysFont("Arial", SnakeSize).render(speed_text, 1, "blue")  # 设置颜色
    speed_rect = speed_surf.get_rect()
    speed_rect.x, speed_rect.y = 0, 0
    screen.blit(speed_surf, speed_rect)
    offsetx = speed_rect.x + speed_rect.width + 10

    text_minus = "-"
    minus_btn = pygame.font.SysFont("Arial", SnakeSize).render(text_minus, 1, color)  # 设置颜色
    minus_btn_rect = minus_btn.get_rect()
    minus_btn_rect.x, minus_btn_rect.y = offsetx, 0
    screen.blit(minus_btn, minus_btn_rect)
    offsetx = minus_btn_rect.x + minus_btn_rect.width + 10

    text_speed_value = str(Tick - Tick_Snake_Move)
    speed_value_surf = pygame.font.SysFont("Arial", SnakeSize).render(text_speed_value, 1, color)  # 设置颜色
    speed_value_rect = speed_value_surf.get_rect()
    speed_value_rect.x, speed_value_rect.y = offsetx, 0
    screen.blit(speed_value_surf, speed_value_rect)
    offsetx = speed_value_rect.x + speed_value_rect.width + 10

    text_plus = "+"
    plus_btn = pygame.font.SysFont("Arial", SnakeSize).render(text_plus, 1, color)  # 设置颜色
    plus_btn_rect = plus_btn.get_rect()
    plus_btn_rect.x, plus_btn_rect.y = offsetx, 0
    screen.blit(plus_btn, plus_btn_rect)

    score_value_text = str(score_value)
    score_value_surf = pygame.font.SysFont("Arial", SnakeSize).render(score_value_text, 1, color)  # 设置颜色
    score_value_rect = score_value_surf.get_rect()
    score_value_rect.x = GamePanelWidth - score_value_rect.width
    score_value_rect.y = 0
    screen.blit(score_value_surf, score_value_rect)

    score_text = "Score"
    score_surf = pygame.font.SysFont("Arial", SnakeSize).render(score_text, 1, "blue")  # 设置颜色
    score_rect = score_surf.get_rect()
    score_rect.x = score_value_rect.x - score_rect.width - 10
    score_rect.y = 0
    screen.blit(score_surf, score_rect)

def check_click(position):
    global Tick_Snake_Move
    if minus_btn_rect == None or plus_btn_rect == None:
        return

    x, y = position[0], position[1]
    minus_btn_x, minus_btn_y = minus_btn_rect.x, minus_btn_rect.y
    plus_btn_x, plus_btn_y = plus_btn_rect.x, plus_btn_rect.y
    if minus_btn_x < x < minus_btn_x + minus_btn_rect.width and \
            minus_btn_y < y < minus_btn_y + minus_btn_rect.height:
        Tick_Snake_Move += 1
    elif plus_btn_x < x < plus_btn_x + plus_btn_rect.width and \
            plus_btn_y < y < plus_btn_y + plus_btn_rect.height:
        Tick_Snake_Move -= 1


pygame.init()
pygame.font.init()  # 初始化字体
screen = pygame.display.set_mode((GamePanelWidth, ControlPanelHeight + GamePanelHeight))
clock = pygame.time.Clock()
snake = Snake()

screen.fill(ControlPanelColor, ControlPanelRect)

while True:
    clock.tick(20)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == pygame.K_a:
                snake.set_direction(pygame.K_LEFT)
            elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
                snake.set_direction(pygame.K_RIGHT)
            elif event.key == pygame.K_UP or event.key == pygame.K_w:
                snake.set_direction(pygame.K_UP)
            elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
                snake.set_direction(pygame.K_DOWN)
            elif event.key == pygame.K_SPACE:
                snake.pause()

        if pygame.mouse.get_pressed()[0]:
            check_click(pygame.mouse.get_pos())

    screen.fill(GamePanelColor, (0, ControlPanelHeight, GamePanelWidth, ControlPanelHeight + GamePanelHeight))

    snake.move()
    for body in snake.bodys:
        screen.blit(body.image, body.get_rect())
    # collision detection
    if snake.is_collided():
        snake.stop()
        display_result()
    else:
        # new body
        snake.create_body()
        if snake.new_body is not None:
            screen.blit(snake.new_body.image, snake.new_body.get_rect())

    screen.fill(ControlPanelColor, (0, 0, GamePanelWidth, ControlPanelHeight))
    display_control_panel()

    if snake.is_paused():
        display_paused()

    pygame.display.flip()

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

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

相关文章

每天只需这样做,轻松提升你的演讲口才

锻炼自己的演讲口才需要长期的练习。以下几点方法可以帮助你提高&#xff1a; 1. 多观看优秀演讲视频。观看他人优秀的演讲&#xff0c;可以学习到他人的语调、节奏、动作表情等技巧&#xff0c;这对我们提高自己的很有帮助。例如可以观看TED演讲、企业年会等形式的演讲视频。…

从零开始的C++(二)

1.为什么C有函数重载但是C没有函数重载&#xff1f; 这涉及了C、C的函数命名习惯&#xff0c;在C语言编译阶段&#xff0c;函数的命名不会受其参数列表的影响&#xff0c;即只要函数名相同编译出的函数名就相同。但在C中&#xff0c;函数命名受影响&#xff0c;如下图所示&…

uboot启动流程-uboot链接接脚本

一. uboot 启动流程 本文我们来详细的分析一下 uboot 的启动流程&#xff0c;理清 uboot 是如何启动的。通过对 uboot 启动流程的梳理。 我们就可以掌握一些外设是在哪里被初始化的&#xff0c;这样当我们需要修改这些外设驱动的时候就会心里有数。另外&#xff0c;通过分析 …

华为云云耀云服务器L实例评测 | 实例场景体验之搭建个人博客:通过华为云云耀云服务器构建个人博客

华为云云耀云服务器L实例评测 &#xff5c; 实例场景体验之搭建个人博客&#xff1a;通过华为云云耀云服务器构建个人博客 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀…

COCO 数据集 人体关键点格式

图片与标注数据在 COCO官网 均提供下载 标注格式 COCO 的标注中包含 4 个部分/字段&#xff0c;"info" 描述数据集&#xff0c;"licenses" 描述图片来源&#xff0c;"images" 和 "annotations" 是主体部分 "images" 部分…

数学术语之源——纤维(fiber)

1. 数学术语“纤维”(英英“fibre”,美英“fiber”)在数学中的起源 fiber[ˈfaɪbə(r)]这个词始于14世纪晚期&#xff0c;词义为“肝叶的一瓣(a lobe of the liver)”,也指“内脏(entrails)”。来自中世纪拉丁语“fibre”,其又源自拉丁语“fibra”,词义为“纤维(a fiber)、细…

Logistic map混沌掩盖信号

开学接触了一些有关混沌知识的学习&#xff0c;阅读量一些混沌通信的论文&#xff0c;对于混沌掩盖信号以确保加密通信有一定的兴趣。混沌的产生我选用的是logistic map映射产生混沌&#xff0c;主要就是一个递推公式&#xff1a; 对于这样一个式子&#xff0c;可以看出&#x…

300元开放式耳机哪款好、百元开放式耳机推荐

我是一个非常喜欢戴着耳机出门的社恐宅男&#xff0c;但看了一些文章后发现长期戴耳机对耳朵也是有危害的&#xff0c;比如长时间佩戴耳机容易导致耳朵潮湿&#xff0c;从而增加细菌滋生的风险&#xff0c;其次&#xff0c;长时间高音量使用耳机可能会造成听力损伤以及长期佩戴…

功率放大器有哪些要求和标准参数

功率放大器是一种常见的电子设备&#xff0c;用于将输入信号增强到更高的功率级别。为了满足不同应用需求&#xff0c;功率放大器需要符合一些特定的要求和标准参数。 在现代电子设备中&#xff0c;功率放大器广泛应用于各种领域&#xff0c;如通信、音频放大、射频放大等。它们…

Vue之transition组件

Vue提供了transition组件&#xff0c;使用户可以更便捷地添加过渡动画效果。 transition组件 transition组件也是一个抽象组件&#xff0c;并不会渲染出真实dom。Vue会在其第一个真实子元素上添加过渡效果。 props render 这里将render分为两部分&#xff0c;第一部分界定真…

Golang的性能优化

欢迎&#xff0c;学习者们&#xff0c;来到Golang性能优化的令人兴奋的世界&#xff01;作为开发者&#xff0c;我们都努力创建高效、闪电般快速的应用程序&#xff0c;以提供出色的用户体验。在本文中&#xff0c;我们将探讨优化Golang应用程序性能的基本技巧。所以&#xff0…

XPD738协议系列-USB Type-C PD 和 Type-A 双口控制器

产品描述&#xff1a; XPD738 是一款集成 USB Type-C、USB Power Delivery&#xff08;PD&#xff09;2.0/3.0 以及 PPS、QC3.0/QC3.0/QC2.0 快充协议、华为 FCP/SCP/HVSCP 快充协议、三星 AFC 快充协议、BC1.2 DCP 以及苹果设备 2.4A 充电规范的多功能 USB Type-C 和 Type-A …

通达信筹码获利比例选股指标公式,寻找二次探底

获利比例的意思是当前价格的获利盘比例&#xff0c;从筹码分布理论的角度来说&#xff0c;获利比例越低&#xff0c;说明越少的人处于获利状态&#xff1b;获利比例越高&#xff0c;说明越多的人处于获利状态。当然这只是理论分析&#xff0c;获利比例是通过历史数据按照一定的…

蛋糕烘焙外卖配送小程序商城的效果是什么

随着人们消费水平提升&#xff0c;蛋糕烘焙成为餐饮行业重要的细分赛道之一&#xff0c;市场中大小品牌众多&#xff0c;同时又相对聚集&#xff0c;由于城市中消费者众多&#xff0c;每个商家都有一定生意&#xff0c;但想要破圈进一步增长却很困难&#xff0c;加之线上电商冲…

华为云云耀云服务器L实例评测 | 实例评测使用之软件性能评测:华为云云耀云服务器下的 Redis 性能评测

华为云云耀云服务器L实例评测 &#xff5c; 实例评测使用之软件性能评测&#xff1a;华为云云耀云服务器下的 Redis 性能评测 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云…

SW免安装的toolbox只读问题

把SOLIDWORKSDATA 整体复制到另外的目录&#xff0c;然后这里设置目录位置。不然原始位置有只读属性

ngrok内网穿透以及原理

〇、前言 如果要想在本地部署一个服务&#xff0c;且要向不在本局域网的用户展示我们的服务&#xff0c;此时就要用内网穿透工具&#xff0c;把我们的服务变成公网服务。ngrok就是一个很好的工具&#xff0c;操作简单&#xff0c;服务稳定。 一、使用 ngrok 1. 下载ngrok 下…

nextTick源码解读

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;Vue &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 nextTick原理nextTicktimerFuncflushCallbacks 异步更新流程updatequeueWatcherflushS…

uniapp小程序 - 隐私协议保护指引接入教程

文章目录 前提&#xff1a;__usePrivacyCheck__: true步骤一、封装弹窗组件步骤二、单个页面引用一、被动监听二、主动查询 前言&#xff1a;官方发布公告&#xff0c;自2023年9月15日起&#xff0c;对于涉及处理用户个人信息的小程序开发者&#xff0c;仅当开发者主动向平台同…

什么是GraphQL?它与传统的REST API有什么不同?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是GraphQL&#xff1f;⭐ 与传统的REST API 的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣…