贪吃蛇游戏实现
在本项目中,我们使用PyGame库来实现贪吃蛇游戏的可视化界面和基本交互逻辑。PyGame 是一个基于 Python 的跨平台游戏开发库,专为构建 2D 游戏而设计。它提供了一系列简单易用的工具与模块,用于处理游戏开发中的各种核心功能,如 图形绘制、声音播放、用户输入交互和事件管理等。PyGame 基于 SDL(Simple DirectMedia Layer)库开发,能够高效地管理低层次的多媒体操作,同时保持了 Python 语言的简洁和可读性。在贪吃蛇项目中,PyGame 的灵活性和高效性使其成为一个理想的选择,尤其是在快速构建游戏原型时。
在实现贪吃蛇游戏时,我们将其分为多个模块,包括画面初始化、蛇的逻辑、食物生成、奖励与状态更新等部分
游戏初始化
在游戏初始化阶段,我们使用PyGame创建游戏窗口并设置基本参数,如窗口大小、背景颜色以及帧率控制器。
我们设置窗口大小为 像素,并设置每个单元格的大小为 像素。这样,我们就建立了一个 的游戏场景供蛇活动。接着我们初始化屏幕填充颜色为黑色,并调用pygame.display.update()实现界面刷新。
1 2 3 4 5 6 7 8 9 import pygamedef main (): pygame.init() screen_size = (SCREEN_X, SCREEN_Y) screen = pygame.display.set_mode(screen_size) clock = pygame.time.Clock() screen.fill((0 , 0 , 0 )) pygame.display.set_caption("人工智能游戏" )
对于一个完整的游戏来说,参数的设置是必不可少的。这些参数往往是一些常数,决定了游戏的运行方式。在这里我们需要设置画面的宽和高,并对每一个单元格的尺寸进行基本的设置。同时,在游戏中,我们也需要对“上”、“下”、“左”、“右”四个方向进行基本的设置。考虑到这些设置应该全局统一,我们单独创建一个config.py文件用于存储这些设置。
1 2 3 4 5 6 7 8 SCREEN_X = 600 SCREEN_Y = 600 UNIT = 50 DIRECTION = ['up' , 'down' , 'left' , 'right' ] DIRECTIONS = {'up' : (0 , -1 ), 'down' : (0 , 1 ), 'left' : (-1 , 0 ), 'right' : (1 , 0 )}
蛇的实现
我们通过创建一个Snake类来实现蛇的逻辑,包括蛇的初始化、移动、增长、方向控制以及死亡检测等功能。为了让整个项目结构更清晰,我们单独创建snake.py来存储Snake类。
蛇的初始化
在Snake类的初始化阶段,我们通过一个列表存储蛇的身体,每个身体块为一个pygame.Rect对象,初始化方向为右。考虑到蛇身的初始化与蛇吃到食物时蛇身长度的增长均可以视为蛇身长度的增加,我们设计了add_body
方法来模块化这些代码。
在add_body
方法中,通过对self.body
列表的操作完成在蛇头处加入身体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import pygamefrom config import *class Snake : def __init__ (self ): self .direction = 'right' self .body = [] for x in range (5 ): self .add_body(self .direction) def add_body (self, direction ): left, top = (0 , 150 ) if self .body: left, top = (self .body[0 ].left, self .body[0 ].top) new_node = pygame.Rect(left, top, UNIT, UNIT) if direction == 'left' : new_node.left -= UNIT elif direction == 'right' : new_node.left += UNIT elif direction == 'up' : new_node.top -= UNIT elif direction == 'down' : new_node.top += UNIT self .body.insert(0 , new_node)
蛇的移动
蛇的移动是贪吃蛇游戏中最基础也最核心的功能。在我们的实现中,蛇的移动采用了一种简单而高效的方法:通过对蛇身体列表的操作来实现移动效果。每当蛇移动时,我们会在移动方向上创建一个新的身体块作为新的蛇头,同时删除蛇尾的最后一个身体块,这样就能实现蛇的前进效果。在此我们仍需考虑的是,蛇并不能原地“回头”,所以我们对蛇的行进方向进行判断,阻止与蛇行进方向相反的转向。
1 2 3 4 5 6 7 8 def move (self, direction ): if (direction == 1 and self .direction != DIRECTION[0 ]) or \ (direction == 0 and self .direction != DIRECTION[1 ]) or \ (direction == 2 and self .direction != DIRECTION[3 ]) or \ (direction == 3 and self .direction != DIRECTION[2 ]): self .direction = DIRECTION[direction] self .add_body(self .direction) self .body.pop()
死亡检测
在贪吃蛇游戏中,死亡检测是保证游戏规则和游戏结束判定的重要机制。我们需要实时检测两种可能导致游戏结束的情况:边界碰撞和自身碰撞。
边界碰撞检测。游戏场景有明确的边界,规定了蛇可以活动的范围。我们需要检查蛇头的坐标是否超出了屏幕的范围。一旦超出区域,则表示蛇已撞到边界,游戏结束。
自身碰撞检测。随着蛇身变长,蛇可能会与自己的身体发生碰撞。我们需要检查蛇头的位置是否与蛇身的任何一个部分重叠。这种检测通过比较蛇头与除蛇头外的所有身体块的位置来实现。一旦发生重叠,则表示蛇已经咬到了自己,游戏结束。
1 2 3 4 5 6 7 8 def is_dead (self ): if self .body[0 ].x not in range (SCREEN_X) or self .body[0 ].y not in range (SCREEN_Y): return True if self .body[0 ] in self .body[1 :]: return True return False
至此,我们已经完成对Snake类的定义,它具备移动和判断死亡的功能。
食物的生成
我们通过Food类来实现食物的创建和位置管理,确保食物在游戏场景中的合理分布。食物系统的核心是创建一个与蛇身体块大小相同的矩形对象。在游戏开始之前,食物对玩家并不可见,这其实从代码实现上很简单,只要让食物出现在场景的边界之外即可。故食物的初始位置设置在屏幕外(x=-50),表示食物尚未生成。
当需要在游戏区域内生成食物时,我们调用Food类中的set_food方法。set_food方法会在距离边界50像素以内的区域随机选择一个位置。在生成过程中,系统会检查新生成的食物位置是否与蛇的身体发生重叠,如果发生重叠,则会递归调用set_food方法,直到找到一个合适的位置。
同样的,为了让整个项目结构清晰,我们创建food.py来存储Food类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import pygameimport randomfrom config import *class Food : def __init__ (self ): self .rect = pygame.Rect(-50 , 0 , UNIT, UNIT) def set_food (self, snake ): all_positions = [pos for pos in range (50 , SCREEN_X - 50 , UNIT)] self .rect.left = random.choice(all_positions) self .rect.top = random.choice(all_positions) for body in snake.body: if self .rect.topleft == body.topleft: return self .set_food(snake)
游戏逻辑
现在我们已经做好了贪吃蛇游戏所有的准备工作,下一步就是根据游戏规则实现游戏逻辑。
在编写具体游戏过程之前,我们首先要对我们定义的Snake类和Food实例化,并设置初始食物的位置。此外,我们在游戏开始之前要将得分设置为0。
1 2 3 4 5 6 7 8 9 snake = Snake() food = Food() food.set_food(snake) direction = 1 KEYBOARD_CONTROL = True score = 0
在做好这些设置之后,我们就可以开始编写整个游戏了。对于一个2D图像游戏,其实是通过每秒高速刷新的画面让玩家观察游戏状态并交互。在程序中,我们通过编写一个循环完成画面的更新与交互判断。
游戏主循环
游戏主循环是贪吃蛇游戏逻辑的核心部分,负责处理游戏的各种事件、状态更新和画面渲染。其主要流程如图1所示:
图1 游戏主循环主要模块流程图
在事件处理部分,我们需要对游戏运行中的各种发生的事件进行相应的处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 while True : for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN and KEYBOARD_CONTROL: if event.key == pygame.K_UP: direction = 0 elif event.key == pygame.K_DOWN: direction = 1 elif event.key == pygame.K_LEFT: direction = 2 elif event.key == pygame.K_RIGHT: direction = 3
pygame的事件系统是一个核心机制,通过pygame.event.get()
获取事件队列中的所有事件,每个事件都是Event对象,包含type属性表示事件类型。pygame.QUIT
是点击窗口关闭按钮时触发的事件类型。通过循环遍历事件队列并检查事件类型,可以响应用户的输入操作。每帧都需要处理事件队列,否则程序会失去响应。当我们使用键盘控制蛇时,我们也需要检测键盘上是否有对应的按键被按下。pygame.KEYDOWN
是按下按键的事件,当键盘上有按键被按下时,我们检测是否是方向键,并记录下其对应的移动方向。
在接收到移动方向之后(或许没有),我们根据方向让蛇进行移动。
贪吃蛇游戏中,每一次蛇的位置发生移动,系统都需要判断是否触发碰撞。当蛇头与食物的位置发生碰撞,我们需要增加蛇的长度,并让食物刷新在新的位置。而如果蛇头与边界或蛇身发生碰撞,游戏就以失败告终了。在这里,我们只需要增加对吃到食物情况的判断,因为我们已经在Snake类中完成了对死亡情况的判断。
1 2 3 4 5 6 7 8 9 10 if snake.body[0 ].colliderect(food.rect): snake.body.append(snake.body[-1 ].copy()) food.set_food(snake) score += 1 elif snake.is_dead(): pygame.quit() sys.exit()
更新画面
游戏画面的更新是让游戏生动起来的关键环节。在贪吃蛇游戏中,我们需要绘制背景、蛇、食物和得分显示。pygame提供了丰富的绘图函数来帮助我们完成这些工作。
首先,我们绘制一个棋盘式的背景,使用深浅两种绿色交替出现:
1 2 3 4 5 6 7 8 screen.fill((0 , 0 , 0 )) for i in range (SCREEN_X // UNIT): for j in range (SCREEN_Y // UNIT): if (i+j) % 2 == 0 : pygame.draw.rect(screen, (0 , 150 , 0 ), (i * UNIT, j * UNIT, UNIT, UNIT)) else : pygame.draw.rect(screen, (0 , 180 , 0 ), (i * UNIT, j * UNIT, UNIT, UNIT))
接下来是游戏中最重要的角色——蛇的绘制。我们为蛇头使用图片来增加游戏的美观度,并根据移动方向旋转蛇头图片。图片应放在同级目录下的image文件夹中,以与游戏主目录进行区分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for i, rect in enumerate (snake.body): if i == 0 : head_file = './image/head.png' head_image = pygame.image.load(head_file) head_image = pygame.transform.scale(head_image, (UNIT, UNIT)) if snake.direction == 'down' : head_image = pygame.transform.rotate(head_image, 180 ) elif snake.direction == 'left' : head_image = pygame.transform.rotate(head_image, 90 ) elif snake.direction == 'right' : head_image = pygame.transform.rotate(head_image, -90 ) screen.blit(head_image, rect) else : color = (255 , 201 , 14 ) if i & 1 == 0 else (255 , 143 , 14 ) pygame.draw.rect(screen, color, rect)
食物使用红色圆形表示,这样可以与方块形状的蛇形成视觉区分:
1 pygame.draw.circle(screen, (255 , 0 , 0 ), (food.rect.center), 25 )
最后,我们使用pygame的文字渲染功能,在屏幕右上角显示当前得分:
1 2 3 4 font = pygame.font.Font(None , 36 ) text = font.render(f"Score: {score} " , True , (255 , 255 , 255 )) screen.blit(text, (10 , 10 ))
最后,我们使用pygame的文字渲染功能,在屏幕左上角显示当前得分:
1 2 3 pygame.display.update() clock.tick(10 )
通过这些绘制操作,我们实现了游戏画面的完整呈现。pygame.display.update()
确保所有绘制内容都显示到屏幕上,而clock.tick()
控制游戏运行速度,防止蛇移动过快影响游戏体验。至此,我们就完整的实现了贪吃蛇的游戏内容,玩家可以通过键盘上的方向键进行游戏。游戏画面如图2所示:
图2 游戏运行画面