From ee3e7cba56794e8d5bc9d84c9a91d91bdf89144e Mon Sep 17 00:00:00 2001 From: yuantianyu5 Date: Thu, 10 Jul 2025 23:11:59 +0800 Subject: [PATCH] fix: resolve food overlap issue and add pause feature - Rewrite Food.set() to prevent spawning on snake body - Implement pause functionality with P key - Add unified info display area - Improve game restart mechanism --- main.py | 196 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 119 insertions(+), 77 deletions(-) diff --git a/main.py b/main.py index 724fc84..d383b27 100644 --- a/main.py +++ b/main.py @@ -3,26 +3,23 @@ import sys import random # 全局定义 +GAME_AREA_HEIGHT = 600 # 游戏区域高度 +INFO_AREA_HEIGHT = 50 # 信息区域高度 SCREEN_X = 600 -SCREEN_Y = 600 +SCREEN_Y = GAME_AREA_HEIGHT + INFO_AREA_HEIGHT # 总高度 = 游戏区 + 信息区 - -# 蛇类 -# 点以25为单位 class Snake(object): - # 初始化各种需要的属性 [开始时默认向右/身体块x5] def __init__(self): self.dirction = pygame.K_RIGHT self.body = [] for x in range(5): self.addnode() - # 无论何时 都在前端增加蛇块 def addnode(self): - left,top = (0,0) + left, top = (0, 0) if self.body: - left,top = (self.body[0].left,self.body[0].top) - node = pygame.Rect(left,top,25,25) + left, top = (self.body[0].left, self.body[0].top) + node = pygame.Rect(left, top, 25, 25) if self.dirction == pygame.K_LEFT: node.left -= 25 elif self.dirction == pygame.K_RIGHT: @@ -31,131 +28,176 @@ class Snake(object): node.top -= 25 elif self.dirction == pygame.K_DOWN: node.top += 25 - self.body.insert(0,node) + self.body.insert(0, node) - # 删除最后一个块 def delnode(self): self.body.pop() - # 死亡判断 def isdead(self): - # 撞墙 - if self.body[0].x not in range(SCREEN_X): + # 撞墙 - 只检查游戏区域 + if self.body[0].x not in range(SCREEN_X): return True - if self.body[0].y not in range(SCREEN_Y): + if self.body[0].y not in range(GAME_AREA_HEIGHT): return True # 撞自己 if self.body[0] in self.body[1:]: return True return False - # 移动! def move(self): self.addnode() self.delnode() - # 改变方向 但是左右、上下不能被逆向改变 - def changedirection(self,curkey): - LR = [pygame.K_LEFT,pygame.K_RIGHT] - UD = [pygame.K_UP,pygame.K_DOWN] - if curkey in LR+UD: + def changedirection(self, curkey): + LR = [pygame.K_LEFT, pygame.K_RIGHT] + UD = [pygame.K_UP, pygame.K_DOWN] + if curkey in LR + UD: if (curkey in LR) and (self.dirction in LR): return if (curkey in UD) and (self.dirction in UD): return self.dirction = curkey - - -# 食物类 -# 方法: 放置/移除 -# 点以25为单位 + class Food: def __init__(self): - self.rect = pygame.Rect(-25,0,25,25) + self.rect = pygame.Rect(-25, 0, 25, 25) def remove(self): - self.rect.x=-25 + self.rect.x = -25 - def set(self): + def set(self, snake_body): if self.rect.x == -25: - allpos = [] - # 不靠墙太近 25 ~ SCREEN_X-25 之间 - for pos in range(25,SCREEN_X-25,25): - allpos.append(pos) - self.rect.left = random.choice(allpos) - self.rect.top = random.choice(allpos) - print(self.rect) + # 获取蛇身所有位置 + snake_positions = [(rect.x, rect.y) for rect in snake_body] + # 生成所有可能位置 - 只在游戏区域内 + possible_positions = [] + for x in range(0, SCREEN_X-25, 25): + for y in range(0, GAME_AREA_HEIGHT-25, 25): + if (x, y) not in snake_positions: + possible_positions.append((x, y)) + + if possible_positions: + pos = random.choice(possible_positions) + self.rect.x, self.rect.y = pos -def show_text(screen, pos, text, color, font_bold = False, font_size = 60, font_italic = False): - #获取系统字体,并设置文字大小 - cur_font = pygame.font.SysFont("宋体", font_size) - #设置是否加粗属性 +def show_text(screen, pos, text, color, font_bold=False, font_size=25, font_italic=False): + cur_font = pygame.font.SysFont("Arial", font_size) # 使用更通用的字体 cur_font.set_bold(font_bold) - #设置是否斜体属性 cur_font.set_italic(font_italic) - #设置文字内容 - text_fmt = cur_font.render(text, 1, color) - #绘制文字 + text_fmt = cur_font.render(text, True, color) screen.blit(text_fmt, pos) - def main(): pygame.init() - screen_size = (SCREEN_X,SCREEN_Y) + screen_size = (SCREEN_X, SCREEN_Y) screen = pygame.display.set_mode(screen_size) pygame.display.set_caption('Snake') clock = pygame.time.Clock() scores = 0 isdead = False + paused = False - # 蛇/食物 snake = Snake() food = Food() + def reset_game(): + nonlocal snake, food, scores, isdead, paused + snake = Snake() + food = Food() + scores = 0 + isdead = False + paused = False + while True: for event in pygame.event.get(): if event.type == pygame.QUIT: + pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: - snake.changedirection(event.key) - # 死后按space重新 - if event.key == pygame.K_SPACE and isdead: - return main() + # 暂停/继续功能 (按P键) + if event.key == pygame.K_p: + paused = not paused - - screen.fill((255,255,255)) + # 方向控制(游戏未暂停且未死亡时有效) + if not paused and not isdead: + snake.changedirection(event.key) + + # 重新开始(死亡时按空格) + if event.key == pygame.K_SPACE and isdead: + reset_game() + + # 绘制游戏界面 + # 1. 游戏区域背景(蛇活动区域) + game_area = pygame.Rect(0, 0, SCREEN_X, GAME_AREA_HEIGHT) + pygame.draw.rect(screen, (255, 255, 255), game_area) + + # 2. 信息区域背景(分数和暂停提示) + info_area = pygame.Rect(0, GAME_AREA_HEIGHT, SCREEN_X, INFO_AREA_HEIGHT) + pygame.draw.rect(screen, (240, 240, 240), info_area) + pygame.draw.line(screen, (180, 180, 180), (0, GAME_AREA_HEIGHT), (SCREEN_X, GAME_AREA_HEIGHT), 2) - # 画蛇身 / 每一步+1分 - if not isdead: - scores+=1 + # 画网格背景(只在游戏区域内) + for x in range(0, SCREEN_X, 25): + pygame.draw.line(screen, (220, 220, 220), (x, 0), (x, GAME_AREA_HEIGHT)) + for y in range(0, GAME_AREA_HEIGHT, 25): + pygame.draw.line(screen, (220, 220, 220), (0, y), (SCREEN_X, y)) + + # 只有在未暂停且未死亡时才更新游戏状态 + if not paused and not isdead: + # 移动蛇 snake.move() - for rect in snake.body: - pygame.draw.rect(screen,(20,220,39),rect,0) - # 显示死亡文字 - isdead = snake.isdead() - if isdead: - show_text(screen,(100,200),'YOU DEAD!',(227,29,18),False,100) - show_text(screen,(150,260),'press space to try again...',(0,0,22),False,30) + # 食物处理 + if food.rect.colliderect(snake.body[0]): + scores += 10 + food.remove() + snake.addnode() - # 食物处理 / 吃到+50分 - # 当食物rect与蛇头重合,吃掉 -> Snake增加一个Node - if food.rect == snake.body[0]: - scores+=50 - food.remove() - snake.addnode() + # 检查死亡 + isdead = snake.isdead() - # 食物投递 - food.set() - pygame.draw.rect(screen,(136,0,21),food.rect,0) + # 画蛇身(只在游戏区域内) + for rect in snake.body: + pygame.draw.rect(screen, (20, 220, 39), rect, 0) + + # 食物投递(只在游戏区域内) + food.set(snake.body) + pygame.draw.rect(screen, (136, 0, 21), food.rect, 0) + + # 显示死亡文字(在游戏区域内居中) + if isdead: + show_text(screen, (SCREEN_X//2 - 150, GAME_AREA_HEIGHT//2 - 50), 'YOU DEAD!', (227, 29, 18), False, 60) + show_text(screen, (SCREEN_X//2 - 180, GAME_AREA_HEIGHT//2 + 20), 'Press SPACE to restart', (0, 0, 0), False, 30) + + # 显示暂停文字(在游戏区域内居中) + if paused and not isdead: + show_text(screen, (SCREEN_X//2 - 70, GAME_AREA_HEIGHT//2 - 30), 'PAUSED', (0, 0, 200), False, 60) + + # ===== 信息区域显示 ===== + # 使用统一的字体样式(大小25,颜色黑色) + info_font_size = 25 + info_color = (0, 0, 0) - # 显示分数文字 - show_text(screen,(50,500),'Scores: '+str(scores),(223,223,223)) + # 1. 显示分数(左侧) + show_text(screen, (20, GAME_AREA_HEIGHT + 15), f'SCORE: {scores}', info_color, False, info_font_size) + + # 2. 显示长度(中间) + show_text(screen, (SCREEN_X//2 - 50, GAME_AREA_HEIGHT + 15), f'LENGTH: {len(snake.body)}', info_color, False, info_font_size) + + # 3. 显示暂停状态(右侧) + pause_text = "PAUSED (P)" if paused else "PLAYING (P)" + show_text(screen, (SCREEN_X - 180, GAME_AREA_HEIGHT + 15), pause_text, info_color, False, info_font_size) pygame.display.update() - clock.tick(10) - - + + # 动态速度控制(仅在游戏进行中) + if not paused and not isdead: + speed = 10 + len(snake.body) // 3 + clock.tick(min(speed, 20)) + else: + # 暂停时保持较低帧率以减少CPU占用 + clock.tick(5) + if __name__ == '__main__': main() \ No newline at end of file -- Gitee