用python中的pygame库写一个贪吃蛇游戏,要求:贪吃蛇可以360度跟随鼠标方向移动,画布上每过一秒出现一个圆圈(可同时出现多个圆圈),在贪吃蛇触碰后消失,蛇的长度增加一格,在画布左上角实时显示蛇的长度,贪吃蛇不可移出画布外
时间: 2025-06-02 12:26:14 浏览: 33
<think>嗯,用户让我用pygame库写一个贪吃蛇游戏,有几个具体的要求。首先,贪吃蛇要能360度跟随鼠标方向移动。然后,画布上每过一秒出现一个圆圈,可以同时存在多个,蛇碰到后圆圈消失,蛇长度加一,还要在左上角显示长度,而且蛇不能移出画布。我得先理清楚每个功能怎么实现。
首先,关于蛇的移动。通常贪吃蛇是按键控制的,比如方向键,但这里要跟随鼠标。也就是说蛇的移动方向应该朝着鼠标的位置。可能需要计算蛇头当前位置到鼠标位置的向量,然后根据这个向量调整蛇头的移动方向。不过要处理360度,所以应该用角度来计算方向,速度分解成x和y方向的分量。
然后,每秒钟生成一个圆圈。这需要用到定时器,pygame的time模块可以设置事件,比如每隔1000毫秒触发一个自定义事件,然后在事件处理中生成圆圈的位置。圆圈的位置应该随机生成,但要确保在画布内,并且可能不要和蛇的位置重叠?不过用户没提,可能不需要,随机生成就行,碰到的时候消失。
当蛇触碰圆圈时,圆圈消失,蛇长度增加。碰撞检测的话,可以用pygame的rect或者计算距离,因为蛇头和圆圈都是圆形的话,可以判断两个圆心的距离是否小于半径之和。蛇的长度增加通常是在尾部添加一个节点,但移动的时候需要根据方向更新位置。
实时显示长度,这需要在每帧渲染的时候,在左上角绘制文字。pygame的font模块可以处理,需要创建字体对象,然后渲染成surface,再blit到屏幕上。
蛇不能移出画布,也就是蛇头的位置要在0到窗口宽度和高度之间。每次更新蛇头位置后,检查是否超出边界,如果超出的话,可能就停止游戏或者不让移动。但用户可能希望蛇不能出去,所以当检测到要超出的时候,不让移动,或者修正位置?或者直接游戏结束?需要看用户的需求,但用户没说游戏结束的条件,可能只要限制在画布内,所以当蛇头超出时,就停止移动或者修正位置。例如,如果蛇头x坐标小于0,就设为0,或者不允许移动出去。可能需要根据具体情况处理。
接下来,设计类的结构。可能需要一个Snake类,管理蛇的身体、长度、移动方向等。Food类或者圆圈的管理,可能用一个列表来保存所有当前的圆圈,每个圆圈有位置和生成时间?或者不需要时间,因为生成后只要存在直到被吃掉。不过用户说每秒钟生成一个,不管之前的是否被吃掉,所以圆圈会越来越多,直到被蛇触碰。所以需要一个列表来维护所有未被吃掉的圆圈。
蛇的移动逻辑:每次更新时,计算蛇头朝向鼠标的方向向量,然后移动。蛇的身体跟随头部,每个身体段移动到前一个的位置。当吃到圆圈时,身体长度加一,所以需要有一个机制,比如在移动时不减少尾部,或者在吃到食物时增加一个段。
碰撞检测:遍历所有圆圈,检查蛇头是否与任何一个圆圈碰撞。如果有,移除该圆圈,增加蛇的长度。
边界检测:每次移动蛇头后,检查是否超出画布范围,如果是的话,可能游戏结束或者阻止移动。但用户没有提到游戏结束,可能只是限制在画布内。所以当蛇头超出边界时,不让它出去,比如将位置限制在画布范围内。
现在,具体实现步骤:
1. 初始化pygame,设置窗口大小,标题等。
2. 创建Snake类,属性包括身体段列表(每个段的位置)、长度、速度。方法包括更新位置、增加长度、绘制等。
3. 管理圆圈,用一个列表存储,每个圆圈有位置(随机生成)和可能的生成时间,不过这里只需要位置,因为每秒钟生成一个,不管其他。所以每秒钟向列表中添加一个新的随机位置的圆圈。
4. 游戏主循环,处理事件(包括生成圆圈的事件),更新蛇的位置,检测碰撞,更新显示。
关键点:
- 蛇的移动方向由鼠标位置控制。蛇头朝向鼠标,所以需要计算蛇头到鼠标的向量,然后归一化,乘以速度,得到每帧的移动量。
- 如何让蛇的身体跟随头部。通常,蛇的每一节移动到前一节的位置。所以每次移动时,从尾部开始,每一节的位置更新为前一节的位置,头部根据方向移动。
- 圆圈生成:使用pygame的USEREVENT事件,设置定时器,每隔1000毫秒触发一次,然后在事件处理中生成新的圆圈位置,并添加到列表。
- 碰撞检测:遍历所有圆圈,检查蛇头的位置和圆圈的位置之间的距离是否小于等于蛇头的半径加上圆圈的半径(假设都是圆形)。如果碰撞,则移除该圆圈,并增加蛇的长度。
- 显示蛇的长度:在每帧渲染时,使用pygame.font.Font渲染文本,显示当前蛇的长度(即身体段的数目)。
- 边界限制:每次移动蛇头后,检查是否超出画布的宽度或高度,如果是,将蛇头的位置限制在边界内。例如,如果蛇头的x坐标小于0,设置为0;如果超过窗口宽度,设置为窗口宽度减1,类似处理y坐标。
可能的错误点:
- 蛇的移动方向计算错误,导致移动不流畅或者方向不对。
- 碰撞检测的半径计算不正确,导致提前或延迟触发碰撞。
- 蛇的身体增长逻辑错误,导致身体段不跟随或显示异常。
- 圆圈生成的位置可能出现在蛇身上,但用户没要求避免,所以可以接受。
代码结构大致如下:
初始化pygame,设置窗口。
定义颜色常量。
Snake类:
- __init__方法:初始位置(比如窗口中心),身体段列表,颜色,速度,半径。
- move方法:计算鼠标位置,确定移动方向,更新头部位置,然后更新身体各段。
- grow方法:增加身体长度。
- draw方法:绘制蛇的身体各段为圆形。
Food(圆圈)管理:
- 列表foods,每秒钟添加一个新的随机位置。
碰撞检测函数:检查蛇头与所有foods中的圆圈是否碰撞。
主循环:
处理事件:QUIT事件,定时器事件(添加food)。
更新蛇的位置。
检查边界,限制蛇头位置。
检查碰撞,如果有,移除food,增加蛇长度。
绘制背景,foods,蛇,显示长度文字。
更新显示,控制帧率。
需要处理的问题:
蛇的移动速度可能太快或太慢,需要调整速度值。比如,每次移动的步长应该根据方向向量乘以速度,并且要考虑帧率,可能需要使用时间delta来平滑移动,但为了简单,可能固定每帧移动一定的像素。
另一个问题,蛇的转向是否立即改变,还是需要逐步转向。根据用户要求,是“跟随鼠标方向”,所以应该实时更新方向,每帧都计算朝向鼠标的方向。
可能将蛇的移动方向向量归一化,乘以速度,这样无论鼠标多远,移动速度一致。
例如,在Snake的move方法中:
mouse_x, mouse_y = pygame.mouse.get_pos()
dx = mouse_x - self.head.x
dy = mouse_y - self.head.y
distance = math.sqrt(dx**2 + dy**2)
if distance > 0:
dx = dx / distance
dy = dy / distance
self.head.x += dx * self.speed
self.head.y += dy * self.speed
但这里可能需要将蛇的位置用浮点数存储,然后取整绘制。或者使用向量来处理。
另外,蛇的身体段需要跟随头部,所以每次移动时,身体的每一段都移动到前一段的位置。例如,从最后一段开始,将前一节的位置赋给当前节,然后头部移动。
例如,在Snake类中,身体段列表中的每个元素是一个pygame.Rect或者包含x和y的位置。每次移动时,从尾部到头部,每个段的位置更新为前一个段的位置,然后头部根据方向移动。
或者,头部是第一个元素,身体段跟随,例如:
new_head = [head_x, head_y]
body.insert(0, new_head)
if not ate_food:
body.pop()
这样每次移动,头部新增位置,如果没吃到食物,就删除尾部,保持长度。但这里吃到食物后,长度加一,所以不需要删除尾部。
这可能是更常见的贪吃蛇身体处理方式。
但在这个问题中,用户要求的是当吃到圆圈时,蛇长度增加一格。所以每吃到一个,长度加一。所以蛇的身体段的数量应该等于初始长度加上吃到的食物数量。
因此,在初始化时,蛇的身体可能是一个列表,比如初始长度为1,吃到一个食物后变为2,依此类推。移动时,头部根据方向移动,然后身体各段跟随。
可能需要将蛇的身体表示为一系列的位置坐标列表,每次移动时,插入新的头部位置,如果长度没有增加,就删除最后一个元素;如果增加了,就不删除。但这样处理可能比较复杂。或者,每次移动都插入新的头部位置,然后根据当前长度决定保留多少段。
或者,另一种方式是,蛇有一个方向,每次移动时,头部向当前方向移动,身体各段跟随。但这里的方向是由鼠标控制的,每帧都可能变化,所以需要每帧计算移动方向,因此不能预先设定方向,而是实时根据鼠标位置调整。
这可能比较复杂,因为通常蛇的移动是按键改变方向,而这里是实时跟随鼠标,所以每帧的方向都可能不同。因此,蛇的移动更像是一个点朝着鼠标方向移动,而身体跟随。这可能需要记录蛇的移动路径,或者每个身体段的位置是头部历史位置的记录。
例如,每帧移动头部,并将头部的位置添加到身体列表的前面,然后根据当前蛇的长度保留最近的N个位置,其中N是蛇的长度。这样,身体会自然跟随头部,形成蛇的效果。
这可能是一个可行的方法。例如:
class Snake:
def __init__(self):
self.length = 1
self.positions = [(WINDOW_WIDTH//2, WINDOW_HEIGHT//2)] # 初始位置
self.speed = 5
self.radius = 10
def move(self):
# 计算头部的新位置
head_x, head_y = self.positions[0]
mouse_x, mouse_y = pygame.mouse.get_pos()
# 计算方向向量
dx = mouse_x - head_x
dy = mouse_y - head_y
distance = math.sqrt(dx**2 + dy**2)
if distance > 0:
dx_normalized = dx / distance
dy_normalized = dy / distance
else:
dx_normalized = 0
dy_normalized = 0
new_head_x = head_x + dx_normalized * self.speed
new_head_y = head_y + dy_normalized * self.speed
# 限制在画布内
new_head_x = max(self.radius, min(WINDOW_WIDTH - self.radius, new_head_x))
new_head_y = max(self.radius, min(WINDOW_HEIGHT - self.radius, new_head_y))
# 插入新头部位置
self.positions.insert(0, (new_head_x, new_head_y))
# 保持长度为当前长度
if len(self.positions) > self.length:
self.positions.pop()
这样,每次移动时,头部朝着鼠标方向移动,身体的位置是之前头部的位置,从而形成蛇的身体。当吃到食物时,self.length +=1,这样下一次移动时,positions列表就会保留多一个位置,从而增加身体长度。
这样的实现可能更简单。然后,绘制蛇的时候,遍历positions列表中的每个位置,绘制圆形。
关于碰撞检测,每个food是一个圆圈,存储其位置。检查蛇头(positions[0])与每个food的位置之间的距离是否小于等于蛇的半径加上food的半径(假设food的半径相同或不同)。
例如,FOOD_RADIUS = 5,在生成food的时候,随机x和y坐标,然后添加到foods列表。
当检测到碰撞时,从foods列表中移除该food,并增加蛇的长度。
现在,编写代码的大纲:
导入必要的库:pygame, math, random, sys。
初始化pygame:
pygame.init()
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("贪吃蛇")
定义颜色:
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
定义Snake类:
class Snake:
def __init__(self):
self.length = 1
self.positions = [(WINDOW_WIDTH//2, WINDOW_HEIGHT//2)]
self.speed = 5
self.radius = 10
def move(self):
# 计算新头部位置
head_x, head_y = self.positions[0]
mouse_x, mouse_y = pygame.mouse.get_pos()
dx = mouse_x - head_y
distance = math.sqrt(dx**2 + dy**2)
if distance > 0:
dx /= distance
dy /= distance
else:
dx, dy = 0, 0
new_head_x = head_x + dx * self.speed
new_head_y = head_y + dy * self.speed
# 限制在画布内
new_head_x = max(self.radius, min(WINDOW_WIDTH - self.radius, new_head_x))
new_head_y = max(self.radius, min(WINDOW_HEIGHT - self.radius, new_head_y))
self.positions.insert(0, (new_head_x, new_head_y))
# 保持长度
while len(self.positions) > self.length:
self.positions.pop()
def grow(self):
self.length += 1
def draw(self, surface):
for pos in self.positions:
pygame.draw.circle(surface, GREEN, (int(pos[0]), int(pos[1])), self.radius)
生成food的逻辑:
FOOD_RADIUS = 5
foods = []
生成food的函数:
def add_food():
x = random.randint(FOOD_RADIUS, WINDOW_WIDTH - FOOD_RADIUS)
y = random.randint(FOOD_RADIUS, WINDOW_HEIGHT - FOOD_RADIUS)
foods.append( (x, y) )
设置定时器:
pygame.time.set_timer(pygame.USEREVENT, 1000) # 每秒触发一次
碰撞检测:
def check_collision(snake_head, food_pos, snake_radius, food_radius):
dx = snake_head[0] - food_pos[0]
dy = snake_head[1] - food_pos[1]
distance = math.sqrt(dx**2 + dy**2)
return distance <= snake_radius + food_radius
主循环:
snake = Snake()
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.USEREVENT:
add_food()
snake.move()
# 检查是否碰到food
snake_head = snake.positions[0]
for food in foods[:]: # 遍历副本,避免修改原列表时出错
if check_collision(snake_head, food, snake.radius, FOOD_RADIUS):
foods.remove(food)
snake.grow()
# 绘制
screen.fill(BLACK)
# 画foods
for food in foods:
pygame.draw.circle(screen, RED, food, FOOD_RADIUS)
# 画蛇
snake.draw(screen)
# 显示长度
text = font.render(f"Length: {snake.length}", True, WHITE)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(30)
pygame.quit()
这样应该可以实现用户的需求。不过需要测试,是否有问题。例如,蛇的移动是否平滑,碰撞检测是否正确,food生成是否正常,边界限制是否有效等。
可能需要注意的问题:
1. 蛇的移动速度:speed变量可能需要调整,例如设为5,可能在高帧率下移动过快。可以考虑根据帧率调整移动速度,或者固定每帧移动的像素,但这里使用clock.tick(30)来限制帧率为30,所以speed=5每帧移动5像素,每秒大约150像素,这可能需要调整。
2. 蛇头的位置更新后限制在画布内,这样蛇不会移出画布。当鼠标在画布外时,蛇会朝那个方向移动,但会被限制在边界。
3. 蛇的身体段的处理是否正确,当长度增加时,positions列表是否保留正确数量的段。
可能还需要处理当蛇头移动时,身体是否会穿过自己,但用户没提到自碰检测,所以可能不需要处理。
综上,这样的代码应该满足用户的要求。</think>下面是一个使用Pygame实现的满足你要求的贪吃蛇游戏代码:
```python
import pygame
import math
import random
import sys
# 初始化Pygame
pygame.init()
# 窗口设置
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("360度贪吃蛇")
# 颜色定义
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
# 蛇类
class Snake:
def __init__(self):
self.length = 1
self.positions = [(WINDOW_WIDTH//2, WINDOW_HEIGHT//2)] # 初始位置
self.speed = 5
self.radius = 10
def move(self):
# 获取鼠标位置
mouse_x, mouse_y = pygame.mouse.get_pos()
head_x, head_y = self.positions[0]
# 计算移动方向
dx = mouse_x - head_x
dy = mouse_y - head_y
distance = math.sqrt(dx**2 + dy**2)
if distance > 0:
dx_normalized = dx / distance
dy_normalized = dy / distance
else:
dx_normalized, dy_normalized = 0, 0
# 计算新头部位置
new_head_x = head_x + dx_normalized * self.speed
new_head_y = head_y + dy_normalized * self.speed
# 边界限制
new_head_x = max(self.radius, min(WINDOW_WIDTH - self.radius, new_head_x))
new_head_y = max(self.radius, min(WINDOW_HEIGHT - self.radius, new_head_y))
# 更新位置
self.positions.insert(0, (new_head_x, new_head_y))
# 保持长度
if len(self.positions) > self.length:
self.positions.pop()
def grow(self):
self.length += 1
def draw(self, surface):
for pos in self.positions:
pygame.draw.circle(surface, GREEN, (int(pos[0]), int(pos[1])), self.radius)
# 食物参数
FOOD_RADIUS = 5
foods = []
def add_food():
x = random.randint(FOOD_RADIUS, WINDOW_WIDTH - FOOD_RADIUS)
y = random.randint(FOOD_RADIUS, WINDOW_HEIGHT - FOOD_RADIUS)
foods.append((x, y))
# 设置定时器(每秒生成一个食物)
pygame.time.set_timer(pygame.USEREVENT, 1000)
# 碰撞检测
def check_collision(snake_head, food_pos):
dx = snake_head[0] - food_pos[0]
dy = snake_head[1] - food_pos[1]
distance = math.sqrt(dx**2 + dy**2)
return distance <= 10 + FOOD_RADIUS # 蛇头半径 + 食物半径
# 游戏主循环
snake = Snake()
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.USEREVENT:
add_food()
snake.move()
# 检测食物碰撞
snake_head = snake.positions[0]
for food in foods[:]:
if check_collision(snake_head, food):
foods.remove(food)
snake.grow()
# 绘制界面
screen.fill(BLACK)
# 绘制食物
for food in foods:
pygame.draw.circle(screen, RED, food, FOOD_RADIUS)
# 绘制蛇
snake.draw(screen)
# 显示长度
text = font.render(f"Length: {snake.length}", True, WHITE)
screen.blit(text, (10, 10))
pygame.display.flip()
clock.tick(30)
pygame.quit()
sys.exit()
```
主要功能说明:
1. **鼠标控制**:蛇会实时跟随鼠标位置移动,实现360度任意方向移动
2. **自动生成食物**:每秒自动在随机位置生成一个新的食物(红色圆圈)
3. **碰撞检测**:当蛇头碰到食物时,食物消失,蛇长度增加
4. **边界限制**:蛇无法移出画布,会自动停留在窗口边缘
5. **实时显示**:左上角实时显示当前蛇的长度
6. **视觉表现**:蛇身用绿色圆圈表示,食物用红色圆圈表示
游戏运行说明:
1. 需要安装pygame库:`pip install pygame`
2. 直接运行代码即可开始游戏
3. 移动鼠标控制蛇的移动方向
4. 通过吃掉红色食物(触碰即可)来增加蛇的长度
5. 关闭窗口或按ESC退出游戏
注意:蛇的移动速度(self.speed)和大小(radius)可以根据需要自行调整。食物生成频率(定时器间隔)也可以通过修改USEREVENT的间隔时间(当前为1000毫秒)来调整。
阅读全文
相关推荐















