项目分析:
对于贪吃蛇,需要设计页面布局,在此基础上添加监听器,实现按键响应,然后根据键盘控制改变蛇的位置,设计随机出现的食物,在蛇吃掉食物的情况下改变蛇身长度,在蛇吃到自身或者撞墙的情况下死亡,重新开启游戏。
实验目标:
实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点,得分增加,“蛇”碰到边界或,蛇头与蛇身相撞,蛇死亡,游戏结束。为游戏设计初始欢迎界面,游戏界面,游戏结束界面。
运算符优先级认知
开始之前,先来安装Pygame。可使用pip模块来帮助下载安装python包。
anaconda用户可按以下操作
输入conda list,然后回车
conda list
再输入python -m pip install --upgrade pip
然后输入pip install pygame
python -m pip install --upgrade pip pip install pygame
安装成功!
可以输入python -m pygame.examples.aliens来检验是否成功安装
python -m pygame.examples.aliens
若正常运行,说明安装成功
1.贪吃蛇一开始出生在左上角,只有一节身体一个头
2.蛇如果碰到了自己的身体或者碰到了游戏边界,那么就直接死亡
3.如果死亡或者想要暂停可以按下空格键
1.我们使用监听键盘上的方向键(↑、↓、←、→)来控制蛇的运动轨迹
1.得分设为吃到一个豆子得5分,初始为0分
2.食物必须满足是在游戏窗口随机生成的,如果蛇头跟食物碰到了,那就代表蛇吃到了食物,然后食物再次刷新随机位置
3.食物出现30s内,贪吃蛇没吃到,那么食物就刷新
4.游戏会随着你蛇的增长,也会变快
(1)蛇身增加与行动
用列表来储存身体,当头部和食物坐标重合时,在列表第0项添加一些身体,通过在蛇头添加一些身体并删除蛇尾来实现移动
(2)随机食物
在game_item.py模块的顶部,导入random模块,以方便使用随机数代码如下import random
在Food类中定义random_rect方法,随机确定游戏窗口的任意格子设置食物出现的位置,每30秒刷新一次,吃到食物时改变移动速度。
(3)蛇死亡判定思路
既要判断舍得死亡又要记录死亡时的状态
①在Snake类定义is_dead方法:如果碰到边界或者身体测返回True
②完善update方法:一旦发现移动身体之后贪吃蛇挂了,则回复之前的身体数据,并且返回Flase,表示无法移动
运行界面
import random import pygame # 全局变量定义 SCREEN_RECT = pygame.Rect(0, 0, 640, 480) # 游戏窗口矩形区域 CELL_SIZE = 20 # 小格子大小 BACKGROUND_COLOR = (255,255, 255) # 主窗口背景颜色 SCORE_TEXT_COLOR = (0, 0, 0) # 分数文字颜色 TIP_TEXT_COLOR = (0, 0, 255) # 提示文字颜色 FOOD_UPDATE_EVENT = pygame.USEREVENT # 食物更新事件 SNAKE_UPDATE_EVENT = pygame.USEREVENT + 1 # 贪吃蛇移动事件 class Label(object): """文字标签类""" def __init__(self, size=48, is_score=True): self.font = pygame.font.SysFont("simhei", size) # 黑体字 self.is_score = is_score """在窗口中绘制文本内容""" def draw(self, window, text): # 使用字体渲染文本内容 color = SCORE_TEXT_COLOR if self.is_score else TIP_TEXT_COLOR text_surface = self.font.render(text, True, color) # 获得文本图像的矩形区域 text_rect = text_surface.get_rect() # 获得主窗口的矩形区域 window_rect = window.get_rect() # 设置位置 if self.is_score: # 分数文字显示在右下角 text_rect.bottomright = window_rect.bottomright else: # 其他文字显示在中间 text_rect.center = window_rect.center # 在游戏窗口中绘制渲染结果 window.blit(text_surface, text_rect) # 食物类 class Food(object): def __init__(self): self.color = (0, 255, 0) # 绿色食物 self.score = 5 # 每颗食物得分 5 分 self.rect = pygame.Rect(0, 0, CELL_SIZE, CELL_SIZE) # 食物位置 self.random_rect() # 设置食物随机位置 def random_rect(self): col = SCREEN_RECT.w / CELL_SIZE - 1 # 屏幕上小格子的列数 row = SCREEN_RECT.h / CELL_SIZE - 1 # 屏幕上小格子的行数 x = random.randint(0, col) * CELL_SIZE y = random.randint(0, row) * CELL_SIZE self.rect = pygame.Rect(x, y, CELL_SIZE, CELL_SIZE) # 食物初始不可见 self.rect.inflate_ip(-CELL_SIZE, -CELL_SIZE) pygame.time.set_timer(FOOD_UPDATE_EVENT, 30000) # 设置更新食物事件 def draw(self, window): # # 判断宽度是否达到小格子宽度 if self.rect.w < CELL_SIZE: self.rect.inflate_ip(2, 2) # 向四周各自放大 1 个像素 pygame.draw.ellipse(window, self.color, self.rect) # 贪吃蛇类 class Snake(object): def __init__(self): self.dir = pygame.K_RIGHT # 初始向右运动 self.score = 0 # 初始得分 self.time_interval = 250 # 运动间隔时间 self.color = (255, 144, 255) # 身体颜色-紫粉 self.body_list = [] # 身体列表 self.reset_snake() """重置蛇属性""" def reset_snake(self): self.dir = pygame.K_RIGHT self.score = 0 self.time_interval = 250 self.body_list.clear() # 清空身体列表 for i in range(2): # 添加一节身体 self.add_node() """增加身体""" def add_node(self): # 判断是否有身体 if self.body_list: head = self.body_list[0].copy() else: head = pygame.Rect(-CELL_SIZE, 0, CELL_SIZE, CELL_SIZE) # 根据运动方向,调整 head 的位置 if self.dir == pygame.K_RIGHT: head.x += CELL_SIZE elif self.dir == pygame.K_LEFT: head.x -= CELL_SIZE elif self.dir == pygame.K_UP: head.y -= CELL_SIZE elif self.dir == pygame.K_DOWN: head.y += CELL_SIZE # 将蛇头插入到身体列表第 0 项 self.body_list.insert(0, head) # 设置贪吃蛇移动定时器 pygame.time.set_timer(SNAKE_UPDATE_EVENT, self.time_interval) def draw(self, window): # 遍历绘制每一节身体 for idx, rect in enumerate(self.body_list): pygame.draw.rect(window, self.color, rect.inflate(-2, -2), # 缩小矩形区域 idx == 0) # 蛇头绘制边框不填充 def update(self): """移动贪吃蛇的整个身体 一旦发现贪吃蛇移动后会死亡,则恢复整个身体数据 移动成功返回 True,贪吃蛇死亡表示不能移动,返回 False """ # 备份身体列表 body_list_copy = self.body_list.copy() # 移动身体 self.add_node() # 沿着运动方向在蛇头位置增加一节身体 self.body_list.pop() # 删除蛇尾 # 判断是否死亡,如果是,恢复备份的身体 if self.is_dead(): self.body_list = body_list_copy return False return True """行动""" def change_dir(self, to_dir): """改变贪吃蛇的运动方向 :param to_dir: 要变化的方向 """ hor_dirs = (pygame.K_RIGHT, pygame.K_LEFT) # 水平方向 ver_dirs = (pygame.K_UP, pygame.K_DOWN) # 垂直方向 # 判断当前运动方向及要修改的方向 if ((self.dir in hor_dirs and to_dir not in hor_dirs) or (self.dir in ver_dirs and to_dir not in ver_dirs)): self.dir = to_dir def has_eat(self, food): """判断蛇头是否与食物相遇 - 吃到食物 :param food: 食物对象 :return: 是否吃到食物 """ if self.body_list[0].contains(food.rect): self.score += food.score # 增加分数 # 修改运动时间间隔(速度) if self.time_interval > 100: # self.time_interval -= 0.03 * (food.score // 100) self.time_interval -= 5 self.add_node() # 增加一节身体 return True return False """死亡""" def is_dead(self): """判断贪吃蛇是否死亡 :return: 死亡返回 True,否则返回 False """ # 记录蛇头的矩形区域 head = self.body_list[0] # 判断蛇头是否移出屏幕 if not SCREEN_RECT.contains(head): return True # 判断是否与身体其他部分重叠 for body in self.body_list[1:]: if head.contains(body): return True return False # 初始化 pygame.mixer.init() pygame.init() window = pygame.display.set_mode((800, 600)) pygame.display.set_caption("Greedy Snake") clock = pygame.time.Clock() start_window = pygame.Surface(window.get_size()) # 充当开始界面的画布 start_window2 = pygame.Surface(window.get_size()) # 充当第一关的画布界面暂时占位(可以理解为游戏开始了) start_window = start_window.convert() start_window2 = start_window2.convert() start_window.fill((255, 255, 255)) # 白色画布 start_window2.fill((0, 0, 0)) # 加载各个素材图片 并且赋予变量名 i11 = pygame.image.load("ChosenStart.png") i11.convert() # 以下为选择开始界面鼠标检测结构。 n1 = True while n1: clock.tick(30) start_window.blit(i11, (200, 240)) buttons = pygame.mouse.get_pressed() # x1, y1 = pygame.mouse.get_pos() # if x1 >= 227 and x1 <= 555 and y1 >= 261 and y1 <= 327: if buttons[0]: n1 = False window.blit(start_window, (0, 0)) pygame.display.update() # 下面是监听退出动作 # 监听事件 for event in pygame.event.get(): # 判断事件类型是否是退出事件 if event.type == pygame.QUIT: print("游戏退出...") # quit 卸载所有的模块 pygame.quit() # exit() 直接终止当前正在执行的程序 exit() # 贪吃蛇代码 n2 = True while n2: clock.tick(30) class Game(object): """游戏类""" def __init__(self): self.main_window = pygame.display.set_mode((640, 480)) pygame.display.set_caption("Greedy Snake") self.score_label = Label() # 得分文本标签 self.tip_label = Label(24, False) # 提示标签 self.is_game_over = False # 游戏结束标记 self.is_pause = False # 游戏暂停标记 self.food = Food() # 食物 self.snake = Snake() # 贪吃蛇 print(self.snake.body_list) def reset_game(self): """游戏复位""" self.is_game_over = False # 游戏结束标记 self.is_pause = False # 游戏暂停标记 self.food.random_rect() # 重新设置食物位置 self.snake.reset_snake() # 设置蛇属性 def start(self): """开始贪吃蛇游戏""" clock = pygame.time.Clock() # 游戏时钟 while True: # 事件监听 for event in pygame.event.get(): # 遍历同一时刻发生的事件列表 if event.type == pygame.QUIT: # 判断退出事件 return elif event.type == pygame.KEYDOWN: # 判断按键事件 if event.key == pygame.K_ESCAPE: return elif event.key == pygame.K_SPACE: if self.is_game_over: self.reset_game() else: self.is_pause = not self.is_pause # 仅在游戏状态才会更新食物、移动蛇的位置以及改变蛇的运动方向 if not self.is_pause and not self.is_game_over: if event.type == FOOD_UPDATE_EVENT: # 判断更新食物事件 self.food.random_rect() if event.type == SNAKE_UPDATE_EVENT: # 判断贪吃蛇移动事件 self.is_game_over = not self.snake.update() if event.type == pygame.KEYDOWN: # 判断按键事件 # 判断是否方向键 if event.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN): self.snake.change_dir(event.key) # 依次绘制游戏元素 self.main_window.fill(BACKGROUND_COLOR) # 判断游戏状态 if self.is_game_over: self.tip_label.draw(self.main_window, "游戏结束,按空格键开启新游戏...") elif self.is_pause: self.tip_label.draw(self.main_window, "游戏暂停,按空格键继续...") else: # 判断是否吃到食物 if self.snake.has_eat(self.food): self.food.random_rect() self.score_label.draw(self.main_window, "得分:%d" % self.snake.score) self.food.draw(self.main_window) self.snake.draw(self.main_window) # 更新显示 pygame.display.update() clock.tick(60) # 刷新帧率 if __name__ == '__main__': pygame.init() # 初始化所有模块 # 游戏代码 Game().start() # 创建游戏对象并且启动游戏 pygame.quit() # 取消初始化所有模块 pygame.display.update() for event in pygame.event.get(): # 判断事件类型是否是退出事件 if event.type == pygame.QUIT: print("游戏退出...") # quit 卸载所有的模块 pygame.quit() # exit() 直接终止当前正在执行的程序 exit()