需要实现的功能列表:
1.贪吃蛇的控制,通过上下左右方向键;
2.触碰到边缘、墙壁、自身则游戏结束;
3.接触到食物则食物消失,同时根据食物类型身体会变长;
4.目前长度显示;
5.暂停、死亡界面;
我自己最开始的思路(有错误):
首先是界面的绘制,蛇的初始化(小方格,蛇的起始坐标,默认移动方向),食物的位置随机生成
贪吃蛇的上下左右方向,会想到980题.不同路径3用[-1, 0], [1, 0], [0, -1], [0, 1]这种形式代表上下左右四个方向(蛇头根据这四个方向移动,蛇身是后一个小方格移动到前一个小方格的位置(覆盖)),在蛇移动的时候需要设置移动的速度,通过设置时间间隔来实现(之前用labview的时候遇到过蛇身移动速度太快的问题)。蛇往哪个方向移动需要接收来自键盘的指示。
接触到食物:食物的位置坐标和蛇头的位置坐标一致,可用判断逻辑实现,如果一致,蛇身加一,不一致,蛇身继续移动
蛇身长度用一个变量来记录,每吃到一个食物蛇身加一
触碰到边缘、墙壁:需要计算边缘墙壁的坐标,判断蛇头坐标是否与这些坐标一致。是否碰到自身:蛇头与除蛇头外的部位的坐标是否一致。
分步代码:
各种绘制
# 游戏背景以及最下方用于显示文字的背景
def draw_background():
# 填充白色背景
screen.fill(COLORS['white'])
# 绘制底部用于显示文字的黑色矩形区域
pygame.draw.rect(screen, COLORS['black'], (-100, GAME_SIZE[1], 3000, 200), 0)
# 绘制墙壁
def draw_wall():
for xy in wall_list:
# 绘制墙壁,使用灰色矩形表示
pygame.draw.rect(screen, COLORS['darkgray'], (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT), 0)
# 绘制蛇,包括头和身体
def draw_snake():
head = snake_list[0] # 获取蛇头的位置
# 绘制蛇头,使用深红色圆形表示
pygame.draw.circle(screen, COLORS['darkred'], (head[0], head[1]), int(SNAKE_WIDTH / 2), 0)
for xy in snake_list[1:]:
# 绘制蛇身,使用深红色矩形表示
pygame.draw.rect(screen, COLORS['darkred'], (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT), 2)
# 绘制食物
def draw_food():
for xyz in food_list:
# 绘制食物,使用不同颜色的矩形表示
pygame.draw.rect(screen, FOOD_COLORS[xyz[2] - 1], (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT), 0)
# 绘制下方的身体长度记录
def draw_context():
# 显示蛇的长度,使用浅蓝色字体
txt = FONT_M.render('Snake length: ' + str(len(snake_list) - 1), True, COLORS['lightblue'])
x, y = 10, GAME_SIZE[1] + (int((SIZE[1] - GAME_SIZE[1]) / 2)) # 确定显示文本的位置
y = int(y - FONT_M.size('Count')[1] / 2) # 调整文本的垂直位置
screen.blit(txt, (x, y)) # 在屏幕上显示文本
# 绘制暂停界面
def draw_pause():
s = pygame.Surface(SIZE, pygame.SRCALPHA) # 创建一个具有透明度的表面
s.fill((255, 255, 255, 220)) # 用带透明度的白色填充表面
screen.blit(s, (0, 0)) # 将表面覆盖到屏幕上
txt = FONT_M.render('PAUSE', True, COLORS['darkgray']) # 显示“PAUSE”文本,使用深灰色字体
x, y = SIZE[0] / 2, SIZE[1] / 2 # 确定文本显示的中心位置
x, y = int(x - FONT_M.size('PAUSE')[0] / 2), int(y - FONT_M.size('PAUSE')[1] / 2) # 调整文本的位置,使其居中
screen.blit(txt, (x, y)) # 在屏幕上显示文本
# 绘制死亡界面
def draw_dead():
s = pygame.Surface(SIZE, pygame.SRCALPHA) # 创建一个具有透明度的表面
s.fill((255, 255, 255, 240)) # 用带透明度的白色填充表面
screen.blit(s, (0, 0)) # 将表面覆盖到屏幕上
txt = FONT_M.render('YOU DEAD', True, COLORS['black']) # 显示“YOU DEAD”文本,使用黑色字体
x, y = SIZE[0] / 2, SIZE[1] / 2 # 确定文本显示的中心位置
x, y = int(x - FONT_M.size('YOU DEAD')[0] / 2), int(y - FONT_M.size('YOU DEAD')[1] / 2) # 调整文本的位置,使其居中
screen.blit(txt, (x, y)) # 在屏幕上显示文本
碰撞检查(死亡和食物)
# 矩形覆盖检查作为碰撞检测,思路是取反,即取所有不会覆盖的情况的反即可
def rect_cover(rect1, rect2):
# 计算第一个矩形的左、右、上、下边界
left1 = int(rect1[0])
right1 = int(rect1[0] + rect1[2])
up1 = int(rect1[1])
down1 = int(rect1[1] + rect1[3])
# 计算第二个矩形的左、右、上、下边界
left2 = int(rect2[0])
right2 = int(rect2[0] + rect2[2])
up2 = int(rect2[1])
down2 = int(rect2[1] + rect2[3])
# 检查两个矩形是否覆盖,判断条件是两个矩形不相交的情况的取反
if not (right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1):
return True # 如果两个矩形相交,返回True
return False # 否则,返回False
# 检查是否碰到食物
def check_food():
# 获取蛇头位置
first = snake_list[0]
# 计算蛇头的矩形边界
snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
for i in range(len(food_list)):
xyz = food_list[i]
# 计算食物的矩形边界
food_rect = (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT)
# 检查蛇头是否与当前食物碰撞
if rect_cover(snake_head_rect, food_rect):
add_body(xyz[2]) # 增加蛇的长度
del food_list[i] # 删除被吃掉的食物
return True # 返回True,表示吃到了食物
return False # 返回False,表示没有吃到食物
# 检查是否碰到边缘、墙壁或者自己的身体
def check_dead():
first = snake_list[0] # 获取蛇头位置
# 计算蛇头的矩形边界
snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
# 检查蛇头是否碰到游戏边缘
if first[0] < 0 or first[0] > GAME_SIZE[0] or first[1] < 0 or first[1] > GAME_SIZE[1]:
return True # 如果碰到边缘,返回True
# 检查蛇头是否碰到墙壁
for xy in wall_list:
# 计算墙壁的矩形边界
wall_rect = (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT)
if rect_cover(snake_head_rect, wall_rect):
return True # 如果碰到墙壁,返回True
# 检查蛇头是否碰到自己的身体
for xy in snake_list[1:]:
# 计算身体的矩形边界
body_rect = (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
if rect_cover(snake_head_rect, body_rect):
return True # 如果碰到自己的身体,返回True
return False # 返回False,表示没有发生碰撞
更新食物和增加蛇的身体长度
# 添加食物
def add_food():
while (True):
# 随机生成食物的位置和类型
xyz = [random.choice(X_LIST), random.choice(Y_LIST), random.choice([1, 2, 3, 4])]
# 检查生成的食物位置是否在墙壁列表中
if xyz not in wall_list:
food_list.append(xyz) # 如果不在墙壁中,添加到食物列表中
break # 结束循环
# 增加蛇的身体长度
def add_body(length=1):
for c in range(length):
# 获取蛇的最后两节的位置
last2, last1 = snake_list[-2], snake_list[-1]
# 如果最后两节的x坐标相同,说明蛇身是竖着的
if last2[0] == last1[0]: # 竖着的两段
if last2[1] > last1[1]: # 如果前一节在后一节的上方,说明蛇身朝下
snake_list.append([last1[0], last1[1] - SNAKE_WIDTH]) # 在蛇的尾部加上一节,向上延伸
else: # 否则,蛇身朝上
snake_list.append([last1[0], last1[1] + SNAKE_WIDTH]) # 在蛇的尾部加上一节,向下延伸
else: # 如果x坐标不同,说明蛇身是横着的
if last2[0] > last1[0]: # 如果前一节在后一节的右侧,说明蛇身朝左
snake_list.append([last1[0] - SNAKE_WIDTH, last1[1]]) # 在蛇的尾部加上一节,向左延伸
else: # 否则,蛇身朝右
snake_list.append([last1[0] + SNAKE_WIDTH, last1[1]]) # 在蛇的尾部加上一节,向右延伸
蛇自身的移动
# 通过按键判断当前蛇的朝向
if event.key == K_LEFT:
if head in ['up','down']:
head = 'left'
elif event.key == K_RIGHT:
if head in ['up','down']:
head = 'right'
elif event.key == K_UP:
if head in ['left','right']:
head = 'up'
elif event.key == K_DOWN:
if head in ['left','right']:
head = 'down'
# 通过朝向判断蛇的下一个位置
first = snake_list[0]
snake_list[1:] = snake_list[:-1]
if head == 'up':
snake_list[0] = [first[0],first[1]-SNAKE_WIDTH]
elif head == 'down':
snake_list[0] = [first[0],first[1]+SNAKE_WIDTH]
elif head == 'left':
snake_list[0] = [first[0]-SNAKE_WIDTH,first[1]]
elif head == 'right':
snake_list[0] = [first[0]+SNAKE_WIDTH,first[1]]
完整代码
import random
import pygame
from pygame.color import THECOLORS as COLORS
from pygame.locals import *
# 游戏背景以及最下方用于显示文字的背景
def draw_background():
# 填充白色背景
screen.fill(COLORS['white'])
# 绘制底部用于显示文字的黑色矩形区域
pygame.draw.rect(screen, COLORS['black'], (-100, GAME_SIZE[1], 3000, 200), 0)
# 绘制墙壁
def draw_wall():
for xy in wall_list:
# 绘制墙壁,使用灰色矩形表示
pygame.draw.rect(screen, COLORS['darkgray'], (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT), 0)
# 绘制蛇,包括头和身体
def draw_snake():
head = snake_list[0] # 获取蛇头的位置
# 绘制蛇头,使用深红色圆形表示
pygame.draw.circle(screen, COLORS['darkred'], (head[0], head[1]), int(SNAKE_WIDTH / 2), 0)
for xy in snake_list[1:]:
# 绘制蛇身,使用深红色矩形表示
pygame.draw.rect(screen, COLORS['darkred'], (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT), 2)
# 绘制食物
def draw_food():
for xyz in food_list:
# 绘制食物,使用不同颜色的矩形表示
pygame.draw.rect(screen, FOOD_COLORS[xyz[2] - 1], (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT), 0)
# 绘制下方的身体长度记录
def draw_context():
# 显示蛇的长度,使用浅蓝色字体
txt = FONT_M.render('Snake length: ' + str(len(snake_list) - 1), True, COLORS['lightblue'])
x, y = 10, GAME_SIZE[1] + (int((SIZE[1] - GAME_SIZE[1]) / 2)) # 确定显示文本的位置
y = int(y - FONT_M.size('Count')[1] / 2) # 调整文本的垂直位置
screen.blit(txt, (x, y)) # 在屏幕上显示文本
# 绘制暂停界面
def draw_pause():
s = pygame.Surface(SIZE, pygame.SRCALPHA) # 创建一个具有透明度的表面
s.fill((255, 255, 255, 220)) # 用带透明度的白色填充表面
screen.blit(s, (0, 0)) # 将表面覆盖到屏幕上
txt = FONT_M.render('PAUSE', True, COLORS['darkgray']) # 显示“PAUSE”文本,使用深灰色字体
x, y = SIZE[0] / 2, SIZE[1] / 2 # 确定文本显示的中心位置
x, y = int(x - FONT_M.size('PAUSE')[0] / 2), int(y - FONT_M.size('PAUSE')[1] / 2) # 调整文本的位置,使其居中
screen.blit(txt, (x, y)) # 在屏幕上显示文本
# 绘制死亡界面
def draw_dead():
s = pygame.Surface(SIZE, pygame.SRCALPHA) # 创建一个具有透明度的表面
s.fill((255, 255, 255, 240)) # 用带透明度的白色填充表面
screen.blit(s, (0, 0)) # 将表面覆盖到屏幕上
txt = FONT_M.render('YOU DEAD', True, COLORS['black']) # 显示“YOU DEAD”文本,使用黑色字体
x, y = SIZE[0] / 2, SIZE[1] / 2 # 确定文本显示的中心位置
x, y = int(x - FONT_M.size('YOU DEAD')[0] / 2), int(y - FONT_M.size('YOU DEAD')[1] / 2) # 调整文本的位置,使其居中
screen.blit(txt, (x, y)) # 在屏幕上显示文本
# 矩形覆盖检查作为碰撞检测,思路是取反,即取所有不会覆盖的情况的反即可
def rect_cover(rect1, rect2):
# 计算第一个矩形的左、右、上、下边界
left1 = int(rect1[0])
right1 = int(rect1[0] + rect1[2])
up1 = int(rect1[1])
down1 = int(rect1[1] + rect1[3])
# 计算第二个矩形的左、右、上、下边界
left2 = int(rect2[0])
right2 = int(rect2[0] + rect2[2])
up2 = int(rect2[1])
down2 = int(rect2[1] + rect2[3])
# 检查两个矩形是否覆盖,判断条件是两个矩形不相交的情况的取反
if not (right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1):
return True # 如果两个矩形相交,返回True
return False # 否则,返回False
# 检查是否碰到食物
def check_food():
# 获取蛇头位置
first = snake_list[0]
# 计算蛇头的矩形边界
snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
for i in range(len(food_list)):
xyz = food_list[i]
# 计算食物的矩形边界
food_rect = (xyz[0] - FOOD_WIDTH / 2, xyz[1] - FOOD_WIDTH / 2, FOOD_WIDTH, FOOD_HEIGHT)
# 检查蛇头是否与当前食物碰撞
if rect_cover(snake_head_rect, food_rect):
add_body(xyz[2]) # 增加蛇的长度
del food_list[i] # 删除被吃掉的食物
return True # 返回True,表示吃到了食物
return False # 返回False,表示没有吃到食物
# 检查是否碰到边缘、墙壁或者自己的身体
def check_dead():
first = snake_list[0] # 获取蛇头位置
# 计算蛇头的矩形边界
snake_head_rect = (first[0] - SNAKE_WIDTH / 2, first[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
# 检查蛇头是否碰到游戏边缘
if first[0] < 0 or first[0] > GAME_SIZE[0] or first[1] < 0 or first[1] > GAME_SIZE[1]:
return True # 如果碰到边缘,返回True
# 检查蛇头是否碰到墙壁
for xy in wall_list:
# 计算墙壁的矩形边界
wall_rect = (xy[0] - WALL_WIDTH / 2, xy[1] - WALL_WIDTH / 2, WALL_WIDTH, WALL_HEIGHT)
if rect_cover(snake_head_rect, wall_rect):
return True # 如果碰到墙壁,返回True
# 检查蛇头是否碰到自己的身体
for xy in snake_list[1:]:
# 计算身体的矩形边界
body_rect = (xy[0] - SNAKE_WIDTH / 2, xy[1] - SNAKE_WIDTH / 2, SNAKE_WIDTH, SNAKE_HEIGHT)
if rect_cover(snake_head_rect, body_rect):
return True # 如果碰到自己的身体,返回True
return False # 返回False,表示没有发生碰撞
# 添加食物
def add_food():
while (True):
# 随机生成食物的位置和类型
xyz = [random.choice(X_LIST), random.choice(Y_LIST), random.choice([1, 2, 3, 4])]
# 检查生成的食物位置是否在墙壁列表中
if xyz not in wall_list:
food_list.append(xyz) # 如果不在墙壁中,添加到食物列表中
break # 结束循环
# 增加蛇的身体长度
def add_body(length=1):
for c in range(length):
# 获取蛇的最后两节的位置
last2, last1 = snake_list[-2], snake_list[-1]
# 如果最后两节的x坐标相同,说明蛇身是竖着的
if last2[0] == last1[0]: # 竖着的两段
if last2[1] > last1[1]: # 如果前一节在后一节的上方,说明蛇身朝下
snake_list.append([last1[0], last1[1] - SNAKE_WIDTH]) # 在蛇的尾部加上一节,向上延伸
else: # 否则,蛇身朝上
snake_list.append([last1[0], last1[1] + SNAKE_WIDTH]) # 在蛇的尾部加上一节,向下延伸
else: # 如果x坐标不同,说明蛇身是横着的
if last2[0] > last1[0]: # 如果前一节在后一节的右侧,说明蛇身朝左
snake_list.append([last1[0] - SNAKE_WIDTH, last1[1]]) # 在蛇的尾部加上一节,向左延伸
else: # 否则,蛇身朝右
snake_list.append([last1[0] + SNAKE_WIDTH, last1[1]]) # 在蛇的尾部加上一节,向右延伸
if __name__ == "__main__":
# 初始化pygame
pygame.init()
# 常量设置
GAME_SIZE = [900, 900] # 游戏区大小
SIZE = [GAME_SIZE[0], GAME_SIZE[1] + 100] # 窗口大小,包括下方显示区域
FONT_S = pygame.font.SysFont('Times', 50) # 小号字体
FONT_M = pygame.font.SysFont('Times', 90) # 中号字体
DIRECTION = ['up', 'right', 'down', 'left'] # 方向列表
X_LIST = [x for x in range(GAME_SIZE[0])] # x坐标范围列表
Y_LIST = [y for y in range(GAME_SIZE[1])] # y坐标范围列表
FOOD_COLORS = ((46, 139, 87), (199, 21, 133), (25, 25, 112), (255, 215, 0)) # 食物颜色
# 墙壁
wall_list = [[100, 200], [600, 500], [350, 200], [500, 800]] # 墙壁位置列表
WALL_WIDTH, WALL_HEIGHT = 30, 30 # 墙壁宽度和高度
# 食物
food_list = [(150, 200, 1), (300, 500, 1), (740, 542, 1), (300, 600, 1), (700, 600, 1)] # 食物位置和类型
FOOD_WIDTH, FOOD_HEIGHT = 14, 14 # 食物的宽度和高度
# 创建窗口,大小为SIZE
screen = pygame.display.set_mode(SIZE)
# 变量参数
snake_list = [[100 + 12 * 4, 100], [100 + 12 * 3, 100], [100 + 12 * 2, 100], [100 + 12 * 1, 100],
[100, 100]] # 初始化蛇的位置列表
SNAKE_WIDTH, SNAKE_HEIGHT = 12, 12 # 蛇的宽度和高度
snake_v = 0 # 蛇的速度
count_time = 0 # 时间计数器
# 游戏等级
frame = 0.05 # 帧数
level = 1 # 等级
# 主循环控制变量
running = True # 游戏是否运行
pause = False # 游戏是否暂停
dead = False # 游戏是否结束
head = 'right' # 初始蛇的方向为向右
# 游戏主循环
while running:
# 事件处理
for event in pygame.event.get():
if event.type == pygame.QUIT: # 如果点击关闭按钮,退出游戏
running = False
break
elif event.type == pygame.MOUSEBUTTONDOWN: # 鼠标按下,切换暂停状态
pause = not pause
elif event.type == pygame.KEYUP: # 键盘松开,改变蛇的方向
if event.key == K_LEFT:
if head in ['up', 'down']:
head = 'left'
elif event.key == K_RIGHT:
if head in ['up', 'down']:
head = 'right'
elif event.key == K_UP:
if head in ['left', 'right']:
head = 'up'
elif event.key == K_DOWN:
if head in ['left', 'right']:
head = 'down'
# 更新数据
if not pause and not dead:
count_time += frame * level # 时间计数增加
first = snake_list[0] # 获取蛇头的位置
snake_list[1:] = snake_list[:-1] # 蛇身每一节的位置往前移动一格
if head == 'up': # 根据蛇的当前方向更新蛇头的位置
snake_list[0] = [first[0], first[1] - SNAKE_WIDTH]
elif head == 'down':
snake_list[0] = [first[0], first[1] + SNAKE_WIDTH]
elif head == 'left':
snake_list[0] = [first[0] - SNAKE_WIDTH, first[1]]
elif head == 'right':
snake_list[0] = [first[0] + SNAKE_WIDTH, first[1]]
# 绘制背景
draw_background()
# 绘制墙壁
draw_wall()
# 绘制蛇
draw_snake()
# 绘制食物
draw_food()
# 绘制下方的身体长度记录
draw_context()
# 绘制暂停界面
if not dead and pause:
draw_pause()
# 绘制死亡界面
if dead:
draw_dead()
# 更新屏幕显示
pygame.display.flip()
# 暂停20毫秒
pygame.time.delay(int(frame / level * 1000))
# 检查是否死亡
dead = check_dead()
# 检查是否吃到食物
if check_food():
add_food() # 添加新食物
# 退出pygame
pygame.quit()
运行图:(由于我不知道怎么放动画,只能贴一张截图)
写代码过程中我遇到的各种问题的解答如下
一、FONT_M.size('YOU DEAD')[0] / 2
是什么意思?
FONT_M.size('YOU DEAD')[0] / 2
是用于计算文本 "YOU DEAD" 的宽度一半的代码。
-
FONT_M
是一个字体对象(pygame.font.SysFont('Times', 90)
),用于渲染特定大小和样式的字体。 -
FONT_M.size('YOU DEAD')
调用size()
方法来获取字符串 "YOU DEAD" 在使用此字体对象渲染时的宽度和高度。size()
方法返回一个元组,格式为(width, height)
,其中:-
width
是文本的像素宽度。 -
height
是文本的像素高度。
-
-
FONT_M.size('YOU DEAD')[0]
取元组的第一个元素,即文本的宽度。 -
FONT_M.size('YOU DEAD')[0] / 2
将文本的宽度除以 2,得到文本宽度的一半。
在游戏中,这段代码用于将 "YOU DEAD" 文本水平居中显示在屏幕的中间:
x = SIZE[0] / 2 - FONT_M.size('YOU DEAD')[0] / 2
-
SIZE[0] / 2
计算出屏幕的水平中心位置。 -
FONT_M.size('YOU DEAD')[0] / 2
计算出文本宽度的一半。
通过从屏幕中心位置减去文本宽度的一半,x
位置就是文本左上角的坐标,使得文本 "YOU DEAD" 水平居中显示在屏幕上。
二、snake_list = [[100 + 12 * 4, 100], [100 + 12 * 3, 100], [100 + 12 * 2, 100], [100 + 12 * 1, 100], [100, 100]]
这个初始化是什么意思?
snake_list
中的每个元素都是蛇身体部分的坐标,snake_list
初始化的是蛇身体每一节的中心点坐标,这些坐标让蛇在游戏屏幕上水平地从右向左排列,开始时长度为 5 个单位。蛇的每一节之间的距离是蛇的宽度 SNAKE_WIDTH
(即 12
个像素)。
此游戏代码中的所有坐标位置初始化都是类似意思(表示中心点)。
三、right2 <= left1 or left2 >= right1 or down2 <= up1 or up2 >= down1
为什么不相交的情况只有这四种?
建议画图辅助理解!!!
-
rect2
在rect1
的左侧:
right2 <= left1
这意味着rect2
的右边界在rect1
的左边界的左侧或正好重合,因此两个矩形不重叠。 -
rect2
在rect1
的右侧:
left2 >= right1
这意味着rect2
的左边界在rect1
的右边界的右侧或正好重合,因此两个矩形不重叠。 -
rect2
在rect1
的上方:
down2 <= up1
这意味着rect2
的下边界在rect1
的上边界的上方或正好重合,因此两个矩形不重叠。 -
rect2
在rect1
的下方:
up2 >= down1
这意味着rect2
的上边界在rect1
的下边界的下方或正好重合,因此两个矩形不重叠。
为什么只有这四种情况?
矩形是二维的封闭形状,如果两个矩形不重叠,则意味着它们在水平方向上没有交集,或者在垂直方向上没有交集。上面的四种情况涵盖了这两种可能性:
-
水平方向:
right2 <= left1
或left2 >= right1
-
垂直方向:
down2 <= up1
或up2 >= down1
只要在水平方向或垂直方向上有一种情况成立,两个矩形就不会重叠。
四、add_body(xyz[2])
为什么传入的参数是xyz[2]
?
add_body(xyz[2])
的作用是根据食物的类型增加相应数量的蛇的身体节数。
food_list
是一个包含多个元组的列表。每个元组代表一个食物的信息,其中:
-
xyz[0]
和xyz[1]
表示食物在屏幕上的坐标位置(x, y)
。 -
xyz[2]
表示食物的类型(或等级),例如,1, 2, 3, 4 等。每种类型可能对应不同的效果,例如类型为1
的食物可能增加蛇1
节,类型为2
的食物可能增加蛇2
节,依此类推。
五、为什么这里还要判断一下if head in ['up', 'down']:
?
if event.key == K_LEFT:
if head in ['up', 'down']:
head = 'left'
这里的条件判断 if head in ['up', 'down']:
是为了避免蛇在相反方向上进行急转弯(即180度转弯),从而防止蛇直接撞到自己。
在游戏中,蛇只能沿着四个方向移动:'up'
(向上)、'down'
(向下)、'left'
(向左)、'right'
(向右)。为了防止蛇在移动时发生180度转弯,导致立即撞上自己的身体,代码中加入了一个检查条件来限制方向的改变。
-
180度转弯会导致游戏失败:如果允许蛇进行180度转弯(例如,从
'left'
直接转为'right'
,或者从'up'
直接转为'down'
),蛇的头部会立即与其身体的第二节相撞,导致游戏失败。 - 合理的控制逻辑:限制蛇只能转向左或右,而不能直接转向相反方向,是游戏控制中的一个合理设计。这样可以避免无意中的错误操作导致游戏过早结束。
- 当用户按下 左键 (
K_LEFT
):- 代码检查当前蛇的移动方向是否为
'up'
或'down'
。 - 如果蛇正在向上或向下移动(
head in ['up', 'down']
),这意味着蛇可以转向左边('left'
)。因此,将蛇的方向设置为'left'
。 - 如果蛇正在向左或向右移动(
head in ['left', 'right']
),则不会改变蛇的方向,以避免180度转弯。
其他情况同理。
- 代码检查当前蛇的移动方向是否为
六、snake_list[1:] = snake_list[:-1]
详细解释?
此句为关键代码!!!!!
snake_list[1:] = snake_list[:-1]
是用于更新蛇的位置的一行代码。这个操作会让蛇的身体(除头部外的部分)跟随头部移动。
假设:
snake_list = [[100, 100], [88, 100], [76, 100], [64, 100], [52, 100]]
每个子列表 [x, y]
代表蛇的一节(身体部分)的坐标。上面的例子中,蛇的头在 [100, 100]
位置,其身体依次在 [88, 100]
, [76, 100]
, [64, 100]
, [52, 100]
位置。
-
snake_list[:-1]
: 这个切片操作表示从snake_list
的开始到倒数第二个元素(不包括最后一个元素)的所有元素。因此,snake_list[:-1]
将返回蛇的身体部分(不包括尾巴)。在上面的例子中,snake_list[:-1]
结果是:[[100, 100], [88, 100], [76, 100], [64, 100]]
-
snake_list[1:]
: 这个切片操作表示从snake_list
的第二个元素开始到最后一个元素的所有元素。因此,snake_list[1:]
将返回蛇的所有部分(不包括头部)。在上面的例子中,snake_list[1:]
结果是:[[88, 100], [76, 100], [64, 100], [52, 100]]
-
赋值操作
snake_list[1:] = snake_list[:-1]
: 这个操作的目的是将蛇的每一节身体部分(不包括头)向前移动一格,继承前一节的坐标位置。- 在赋值操作之后,
snake_list[1:]
的每个元素都将被更新为snake_list[:-1]
的对应位置。 - 结果是,蛇的身体的每一节都会移动到它的前一节的位置上。例如,赋值后
snake_list
变为:
注意,这里蛇的头部还是snake_list = [[100, 100], [100, 100], [88, 100], [76, 100], [64, 100]]
[100, 100]
(它会在后续代码中更新),而原来的第二节身体([88, 100]
)现在变成了新的第二节位置,原来的第三节身体([76, 100]
)变成了新的第三节位置,依此类推。 - 在赋值操作之后,
七、Pygame屏幕的坐标系是什么样的?
这个问题很关键!!!!因为代码中存在大量坐标修改的式子,这个问题是修改坐标的前提,加减很容易出错,需要多在纸上模拟。
使用 Pygame 进行开发时,屏幕的坐标系通常如下:
- X 轴: 从左向右增加。
-
Y 轴: 从上向下增加。
这意味着: - 当
x
坐标增加时,物体向右移动。 - 当
y
坐标增加时,物体向下移动。
八、怎么样让贪吃蛇移动的速度变慢,需要修改哪几个变量的数值?
-
frame
: 这是控制游戏循环间隔时间的变量。增大frame
的值会增加每次循环之间的时间间隔,从而降低蛇的移动速度。 -
level
: 这是控制游戏速度的一个变量。减少level
的值会减慢蛇的移动速度。 -
pygame.time.delay()
: 这个函数控制每次游戏循环后的等待时间。增大其参数值会让每次循环等待更长时间,从而使蛇的移动速度变慢。
例如:
# 控制蛇移动的速度
frame = 0.05 # 增大这个值,比如改为 0.1,会让蛇的速度变慢
level = 1 # 减小这个值,比如改为 0.5,会让蛇的速度变慢
# 在主循环的末尾,控制每帧之间的延迟
pygame.time.delay(int(frame / level * 1000))
我认为此游戏还能改进的地方,待以后代码功力提高再实现
- 比如可以增加游戏难度,如随着时间的推移,逐渐增加蛇的速度或减少食物出现的间隔等等。添加不同的关卡,每个关卡有不同的障碍物布局或增加新的挑战(此代码只能玩一局,也没有一个最终胜利的机制)。
- 引入不同类型的食物,每种食物有不同的效果。虽然有draw_food()函数,且在这个函数中食物颜色有改变的公式,但是在主函数中food_list的z都为1,实际运行的时候食物只有(46, 139, 87)这一种颜色,有待改进。
- 设置更炫酷的界面(可以点击开始、暂停和游戏结束等,添加分数系统,根据蛇吃到的食物数量或游戏时间来计算分数)和图形,还可以添加背景音乐和游戏音效,如吃到食物、碰撞、游戏结束等音效。
4.可以设置排行榜,记录和显示玩家的最高分数或游戏记录。