欢迎各位朋友阅读,先展示效果视频吧。
只是想玩游戏可以到末尾直接拿源代码,想要学习的朋友请继续阅读。
相对于开发的大部分贪吃蛇1.0版本,我增加了一些玩法:
改变蛇的形状,模样更贴近一些
设置积分制
吃一个食物速度增加,设置了最大速度
倒计时时间,时间结束死亡
可以按键暂停和重开
吃一个食物获得10积分,剩余时间增加2s,有2s的猫咪吃东西音效(个人爱好)
计算历史最高积分,死亡时结算积分
搭建基本的界面,定义游戏窗口大小、题目、背景色和退出功能,代码如下:
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
bgcolor = (40, 40, 60) # 背景色
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
空白界面有了,接下来我们先画一下展示栏
我先定义显示文本函数,然后在初始化界面中调用
代码和效果如下:
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2), 0)
self.print_text(self.screen, self.font2, 30, 7, '速度: ')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
到了关键的食物和蛇了
我理解pygame开发游戏就是先画出各种静态图,再通过玩家操作,让各个静态图发生交互
如果没有概念的话可以先百度了解动态图
所以接下来我们需要判断各种情况,并制作相关的静态图,为之后开始游戏做准备
我们可以把画静态图分为两步:
第一步 定义数据也就是以什么形式存放坐标
第二步 绘画就是根据存放的数据进行画出静态图
这么做方便修改和减少重复性代码
代码如下:
from collections import deque
import random
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2), 0)
self.print_text(self.screen, self.font2, 30, 7, '速度: ')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
接下来绘画比较麻烦,大家应该看过完整的效果图
食物只是一个圆就行
蛇的话我分为三个部分绘画,蛇头、蛇身、蛇尾
蛇头是半圆加矩形组成轮廓,再绘画眼睛由白色圆加黑色圆组成
蛇身是一个矩形,我定义为非蛇头和蛇尾的躯体
蛇尾是半圆加矩形组成
由于蛇头和蛇尾向左、右、上、下方向移动的静态图形状不同,所以我们需要画四张图
蛇头的话方向有定义,根据方向画就行
蛇尾方向比较麻烦,我想到通过比较蛇尾与相近蛇身的坐标差另定义方向
代码和效果如下:
import random
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, '速度: ')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
好了,我们我们静态图有了,就可以让他们发生交互了
先设定发生交互的原因是什么,常用是时间。到了指定时间后,换下一张静态图
我这里指定时间就是蛇的速度,而蛇速度单位是格/s
也就是说每过蛇速度x的时间后,移动一格距离
在3(2)讲过绘画静态图的原理,让蛇移动就是数据的坐标移动
所以我们正常修改数据,让数据变化产生动画效果,也就实现了游戏移动
代码和效果如下:
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
self.speed = 0.5 # 初始速度 0.5s/格
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, '速度: ')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
蛇能移动了,接下来需要添加键盘操作,让玩家能够正常控制
蛇的移动上节说过,就是数据的坐标发生改变
方向键:我们通过修改蛇头下一步坐标变化达到效果
开始键:我们需要在蛇移动前添加条件开始,死亡时将其关闭,重开再打开(别忘记初始化数据)
暂停键:我们需要在蛇移动前添加条件未暂停,暂停就打开,不暂停就关闭
代码和效果如下:
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
self.speed = 0.5 # 初始速度 0.5s/格
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, '速度: ')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
到这里已经完成一个贪吃蛇游戏了,只不过有些单调
我们添加一个设定,吃到一个食物蛇的速度增加,同时设置最大速度
显示栏的速度的值也可以同步了
代码和效果如下:
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
self.speed = 0.5 # 初始速度 0.5s/格
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, f'速度: {int((0.53 - self.speed) * 100 / 3)}')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, '时间: ')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
倒计时这一块比较麻烦。
python是单线程语言
所以我们写的倒计时函数,也必须放到主循环中运行
倒计时单位是秒,那么我们可以写成条件判断函数
计算时间间隔每过1s运行一次,直到倒计时时间结束
重点我们需要考虑暂停以及重开,一些数据别忘记重置
还有吃食物倒计时时间延长2s
代码和效果如下:
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
self.speed = 0.5 # 初始速度 0.5s/格
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
self.timer = '03:00' # 倒计时
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
self.seconds += 2
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, f'速度: {int((0.53 - self.speed) * 100 / 3)}')
self.print_text(self.screen, self.font2, 450, 7, '积分: ')
self.print_text(self.screen, self.font2, 240, 7, f'时间: {self.timer}')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start_time = time.time() # 开始倒计时
self.seconds = 3 * 60 # 重置倒计时
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def countdown(self):
"""
倒计时
"""
# 循环次数
global i
if self.start:
if not self.pause:
# 剩余时间大于0
if self.seconds >= 0:
mins, secs = divmod(self.seconds, 60)
self.timer = '{:02d}:{:02d}'.format(mins, secs)
last_time = time.time()
# 倒计时变化
if last_time >= self.start_time + i:
self.seconds -= 1
i += 1
else: # 空格暂停时
self.start_time = time.time() # 重置倒计时时间
i = 1 # 重置循序
if self.seconds < 0: # 倒计时结束
self.game_over = True
else:
i = 1 # 死亡重置
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.countdown() # 时间倒计时
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
7
朋友快完成了,加油
根据玩法,需要设定:
吃一个食物积分+10
死亡时计算积分并展示历史最高积分
历史最高分这个功能实现:
我写了两个函数,一个展示最高分,另一个存放最高分到本地文件
展示最高分函数:死亡时调用,通过比较最高分成绩文件中积分与当前积分实现两种文本展示
存放最高分函数:退出和重开初始化前调用,尝试读取最高分文件,没有默认为0
比较当前积分和历史最高分,结果决定是否覆盖文件
代码和效果如下:
import pickle
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
self.score = 0 # 得分
self.timer = '03:00' # 倒计时
self.speed = 0.5 # 初始速度 0.5s/格
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
# 显示当前积分和历史最高分
self.max_results()
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
self.score += 10
self.seconds += 2
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, f'速度: {int((0.53 - self.speed) * 100 / 3)}')
self.print_text(self.screen, self.font2, 450, 7, f'积分: {self.score}')
self.print_text(self.screen, self.font2, 240, 7, f'时间: {self.timer}')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
self.save_results() # 是否替换历史最高分
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.save_results() # 是否替换历史最高分
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start_time = time.time() # 开始倒计时
self.seconds = 3 * 60 # 重置倒计时
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def countdown(self):
"""
倒计时
"""
# 循环次数
global i
if self.start:
if not self.pause:
# 剩余时间大于0
if self.seconds >= 0:
mins, secs = divmod(self.seconds, 60)
self.timer = '{:02d}:{:02d}'.format(mins, secs)
last_time = time.time()
# 倒计时变化
if last_time >= self.start_time + i:
self.seconds -= 1
i += 1
else: # 空格暂停时
self.start_time = time.time() # 重置倒计时时间
i = 1 # 重置循序
if self.seconds < 0: # 倒计时结束
self.game_over = True
else:
i = 1 # 死亡重置
def max_results(self):
"""展示历史最高分"""
# 从文件加载变量
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file)
if self.score > loaded_data:
# 展示恭喜你打破历史最高积分和当前积分
self.centre_text(self.screen, self.font2, - SIZE * 2, f'恭喜你打破历史最高分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
else:
# 展示最高成绩和当前积分(先最高成绩后当前,居中上下展示)
self.centre_text(self.screen, self.font2, - SIZE * 2, f'历史最高积分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
def save_results(self):
"""
替换历史最高分
应该重开时或者关闭时引用
"""
# 从文件加载变量
try:
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file) # 获取历史成绩
except:
loaded_data = 0
if self.score >= loaded_data: # 覆盖第一次历史最高分
# 替换历史最高分
with open('max_results.pkl', 'wb') as file:
pickle.dump(self.score, file)
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.countdown() # 时间倒计时
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
8
最后一步,要求:启动时展示操作说明
用函数封装展示操作说明,那么放哪尼
我们思考一下,什么情况下只会启动时运行
如果朋友是一步步思考过来,那么有一个状态满足条件:未开始未结束阶段
代码和效果如下:
import pickle
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
self.score = 0 # 得分
self.timer = '03:00' # 倒计时
self.speed = 0.5 # 初始速度 0.5s/格
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
# 显示当前积分和历史最高分
self.max_results()
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
self.score += 10
self.seconds += 2
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE))
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
elif not self.start and not self.game_over: # 初始界面即未开始未死亡阶段
# 展示操作说明
self.help()
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2))
self.print_text(self.screen, self.font2, 30, 7, f'速度: {int((0.53 - self.speed) * 100 / 3)}')
self.print_text(self.screen, self.font2, 450, 7, f'积分: {self.score}')
self.print_text(self.screen, self.font2, 240, 7, f'时间: {self.timer}')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家键盘操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
self.save_results() # 是否替换历史最高分
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.save_results() # 是否替换历史最高分
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start_time = time.time() # 开始倒计时
self.seconds = 3 * 60 # 重置倒计时
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def countdown(self):
"""
倒计时
"""
# 循环次数
global i
if self.start:
if not self.pause:
# 剩余时间大于0
if self.seconds >= 0:
mins, secs = divmod(self.seconds, 60)
self.timer = '{:02d}:{:02d}'.format(mins, secs)
last_time = time.time()
# 倒计时变化
if last_time >= self.start_time + i:
self.seconds -= 1
i += 1
else: # 空格暂停时
self.start_time = time.time() # 重置倒计时时间
i = 1 # 重置循序
if self.seconds < 0: # 倒计时结束
self.game_over = True
else:
i = 1 # 死亡重置
def max_results(self):
"""展示历史最高分"""
# 从文件加载变量
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file)
if self.score > loaded_data:
# 展示恭喜你打破历史最高积分和当前积分
self.centre_text(self.screen, self.font2, - SIZE * 2, f'恭喜你打破历史最高分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
else:
# 展示最高成绩和当前积分(先最高成绩后当前,居中上下展示)
self.centre_text(self.screen, self.font2, - SIZE * 2, f'历史最高积分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
def save_results(self):
"""
替换历史最高分
应该重开时或者关闭时引用
"""
# 从文件加载变量
try:
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file) # 获取历史成绩
except:
loaded_data = 0
if self.score >= loaded_data: # 覆盖第一次历史最高分
# 替换历史最高分
with open('max_results.pkl', 'wb') as file:
pickle.dump(self.score, file)
def help(self):
"""
展示操作说明
"""
"""
w,s,a,d/上下左右 控制移动方向
回车键 开始/重开
空格键 暂停/取消暂停
"""
# 展示操作说明
self.centre_text(self.screen, self.font2, -SIZE * 4, '回车键:开始/重开')
self.centre_text(self.screen, self.font2, -SIZE * 2, '空格键:暂停/取消暂停')
self.centre_text(self.screen, self.font2, 0, 'w,s,a,d/上下左右:控制移动方向')
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.countdown() # 时间倒计时
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")
9
本人使用:
python3.7版本开发语言
pygame 2.1.2版本
pycharm 编译器
感谢大家看完这篇文章
写的不好的地方,欢迎大家指出
如果有看不懂地方,也欢迎在评论去留言
顺便说一下吃食物音效,因素材限制就没解说
如果需要的话,去下方获取完整代码另找素材包替换
不需要吃食物音效的完整代码,去上方开发流程的最后一步获取
想要有吃食物音效的完整代码如下(音效包需要自己另找wav包替代):
import pickle
import random
import time
from collections import deque
import pygame
from pygame.locals import *
SCREEN_LENGTH = 600 # 窗口长度
SCREEN_HEIGHT = 480 # 窗口宽度
SIZE = 20 # 基本单位
i = 1 # 用来倒计时,做循环次数
# 范围
scope_x = (0, SCREEN_LENGTH // SIZE - 1)
scope_y = (2, SCREEN_HEIGHT // SIZE - 1)
# 颜色
black = (0, 0, 0) # 显示栏
bgcolor = (40, 40, 60) # 背景色
orange = (240, 134, 80) # 蛇颜色
red = (200, 30, 30) # GAME OVER 的字体颜色和食物
class GAME:
"""
贪吃蛇游戏
"""
def __init__(self):
# 初始化
pygame.init()
# 窗口
self.screen = pygame.display.set_mode((SCREEN_LENGTH, SCREEN_HEIGHT))
# 题目
pygame.display.set_caption('贪吃蛇')
self.pause = False # 未暂停
self.start = False # 未开始
self.game_over = False # 未结束
self.score = 0 # 得分
self.timer = '03:00' # 倒计时
self.speed = 0.5 # 初始速度 0.5s/格
# 字体格式
self.font2 = pygame.font.SysFont('华文中宋', 26) # 大部分字体格式
self.font = pygame.font.SysFont('华文中宋', 72) # 字体
# 创建蛇对象
self.snake = deque() # deque(双端队列),deque([])
# 创建食物对象
self.food = [0, 0]
# 确保一个方向指令执行一次改变方向的移动
self.b = True
# 初始方向为右
self.direction = [1, 0]
# 初始化蛇性质
self.init_snake()
# 初始化食物性质
self.init_food()
def init_snake(self):
"""
初始化蛇
"""
# 添加数据坐标
self.snake.append((3, scope_y[0]))
self.snake.append((2, scope_y[0]))
self.snake.append((1, scope_y[0]))
self.snake.append((0, scope_y[0]))
def init_food(self):
"""
初始化食物
"""
# 定义食物坐标随机
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
# 删除食物出现在蛇内
while (self.food[0], self.food[1]) in self.snake:
self.food[0] = random.randint(scope_x[0], scope_x[1])
self.food[1] = random.randint(scope_y[0], scope_y[1])
def init_settings(self):
"""
初始化界面 ***
为那些数据(蛇,食物,墙等)绘画
"""
# 吃食物声音
sound = pygame.mixer.Sound('猫咪吃东西.wav')
self.screen.fill(bgcolor) # 背景色
# 游戏结束
if self.game_over:
self.start = False
# 居中展示 GAME OVER
self.centre_text(self.screen, self.font, 0, 'GAME OVER', color=red)
# 显示当前积分和历史最高分
self.max_results()
else:
# 正常运行时
if not self.pause and self.start:
curtime = time.time()
# 每过蛇速度时,开始移动一次
if curtime - self.last_move_time > self.speed:
self.b = True # 实现速度内只有一个方向
self.last_move_time = curtime
# 蛇头下一步位置
next_s = (self.snake[0][0] + self.direction[0], self.snake[0][1] + self.direction[1])
if next_s == (self.food[0], self.food[1]): # 吃到食物
self.snake.appendleft(next_s)
self.init_food()
self.score += 10
self.seconds += 2
sound.play()
# 设置最大速度
if int((0.53 - self.speed) * 100 / 3) < 15: # 显示速度
self.speed -= 0.01 * 3
else: # 蛇正常移动
# 正常移动范围内
if scope_x[0] <= next_s[0] <= scope_x[1] and scope_y[0] <= next_s[1] <= scope_y[
1] and next_s not in self.snake:
self.snake.appendleft(next_s)
self.snake.pop()
else: # 吃到自己或者墙壁,死亡
self.game_over = True
# 画蛇
for s in self.snake:
if s in list(self.snake)[1:-1]: # 蛇身体
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE,
SIZE, SIZE))
# 画蛇头和蛇尾需要知道方向,每个方向不一样
elif s == list(self.snake)[0]: # 蛇头
if self.direction == [0, -1]: # 向上
# 蛇头 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
# 蛇眼睛 白色圆和黑色圆组成
pygame.draw.circle(self.screen, (255, 255, 255), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10, 0)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4), SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10, 0)
elif self.direction == [0, 1]: # 向下
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10, 0)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10, 0)
elif self.direction == [-1, 0]: # 向左
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10, 0)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10, 0)
elif self.direction == [1, 0]: # 向右
# 蛇头
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
# 蛇眼睛
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE / 4),
SIZE / 10, 0)
pygame.draw.circle(self.screen, (255, 255, 255),
(s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4), SIZE / 5, 0)
pygame.draw.circle(self.screen, (0, 0, 0), (s[0] * SIZE + SIZE * 3 / 4, s[1] * SIZE + SIZE * 3 / 4),
SIZE / 10, 0)
else: # 蛇尾
dir = [self.snake[-2][0] - s[0], self.snake[-2][1] - s[1]] # 判断蛇尾方向,通过比较蛇尾与相近身体的坐标
if dir == [0, -1]: # 蛇尾方向向上,蛇头与蛇尾方向是两种
# 蛇尾 由半圆和矩形组成
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE + SIZE), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
if dir == [0, 1]: # 蛇尾方向向下
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE / 2, s[1] * SIZE), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
if dir == [-1, 0]: # 蛇尾方向向左
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE + SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
if dir == [1, 0]: # 蛇尾方向向右
# 蛇尾
pygame.draw.circle(self.screen, orange, (s[0] * SIZE, s[1] * SIZE + SIZE / 2), SIZE / 2, 0)
pygame.draw.rect(self.screen, orange, (s[0] * SIZE, s[1] * SIZE, SIZE, SIZE), 0)
# 画食物
if self.start:
pygame.draw.circle(self.screen, red, (self.food[0] * SIZE + SIZE / 2, self.food[1] * SIZE + SIZE / 2),
SIZE / 2)
elif not self.start and not self.game_over: # 初始界面即未开始未死亡阶段
# 展示操作说明
self.help()
# 画显示栏
pygame.draw.rect(self.screen, black, (0, 0, SCREEN_LENGTH, SIZE * 2), 0)
self.print_text(self.screen, self.font2, 30, 7, f'速度: {int((0.53 - self.speed) * 100 / 3)}')
self.print_text(self.screen, self.font2, 450, 7, f'积分: {self.score}')
self.print_text(self.screen, self.font2, 240, 7, f'时间: {self.timer}')
pygame.display.flip() # 刷新界面,不刷新界面没效果
def control(self):
"""
玩家操作
"""
for event in pygame.event.get(): # 截取游戏的事件
if event.type == QUIT: # 退出
self.save_results() # 是否替换历史最高分
exit()
elif event.type == KEYDOWN: # 键盘按键
if event.key == K_RETURN: # 回车键
if self.game_over or not self.start: # 开始游戏
self.save_results() # 是否替换历史最高分
self.__init__() # 初始化大部分数据
self.last_move_time = time.time() # 用来速度
self.start_time = time.time() # 开始倒计时
self.seconds = 3 * 60 # 重置倒计时
self.start = True # 开始
elif event.key == K_SPACE: # 暂停
self.pause = not self.pause
elif event.key in (K_w, K_UP):
# 这个判断是为了防止蛇向上移时按了向下键,导致直接 GAME OVER
if self.b and not self.direction[1]:
self.direction = [0, -1]
self.b = False
elif event.key in (K_s, K_DOWN):
if self.b and not self.direction[1]:
self.direction = [0, 1]
self.b = False
elif event.key in (K_a, K_LEFT):
if self.b and not self.direction[0]:
self.direction = [-1, 0]
self.b = False
elif event.key in (K_d, K_RIGHT):
if self.b and not self.direction[0]:
self.direction = [1, 0]
self.b = False
def print_text(self, screen, font, x, y, text, color=(255, 255, 255)):
"""
游戏窗口最上面显示的文本
screen 窗口
font 字体
x 横坐标
y 纵坐标
text 文本
color 字体颜色
"""
imgText = font.render(text, True, color) # 文本
screen.blit(imgText, (x, y)) # 渲染文本
def centre_text(self, screen, font, y, text, x=0, color=(255, 255, 255)):
"""
游戏窗口中间显示的文本
screen 窗口
font 字体
y 离中心的纵坐标偏差值
text 文本
x 离中心的横坐标偏差值
color 字体颜色
"""
Text = font.render(text, True, color) # 文本
width, height = font.size(text) # 文本宽,高
# 渲染文本
screen.blit(Text, ((SCREEN_LENGTH - width) // 2 + x, (SCREEN_HEIGHT - height) // 2 + y))
def countdown(self):
"""
倒计时
"""
# 循环次数
global i
if self.start:
if not self.pause:
# 剩余时间大于0
if self.seconds >= 0:
mins, secs = divmod(self.seconds, 60)
self.timer = '{:02d}:{:02d}'.format(mins, secs)
last_time = time.time()
# 倒计时变化
if last_time >= self.start_time + i:
self.seconds -= 1
i += 1
else: # 空格暂停时
self.start_time = time.time() # 重置倒计时时间
i = 1 # 重置循序
if self.seconds < 0: # 倒计时结束
self.game_over = True
else:
i = 1 # 死亡重置
def max_results(self):
"""展示历史最高分"""
# 从文件加载变量
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file)
if self.score > loaded_data:
# 展示恭喜你打破历史最高积分和当前积分
self.centre_text(self.screen, self.font2, - SIZE * 2, f'恭喜你打破历史最高分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
else:
# 展示最高成绩和当前积分(先最高成绩后当前,居中上下展示)
self.centre_text(self.screen, self.font2, - SIZE * 2, f'历史最高积分: {loaded_data}')
self.centre_text(self.screen, self.font2, - SIZE * 4, f'当前积分: {self.score}')
def save_results(self):
"""
替换历史最高分
应该重开时或者关闭时引用
"""
# 从文件加载变量
try:
with open('max_results.pkl', 'rb') as file:
loaded_data = pickle.load(file) # 获取历史成绩
except:
loaded_data = 0
if self.score >= loaded_data: # 覆盖第一次历史最高分
# 替换历史最高分
with open('max_results.pkl', 'wb') as file:
pickle.dump(self.score, file)
def help(self):
"""
展示操作说明
"""
"""
w,s,a,d/上下左右 控制移动方向
回车键 开始/重开
空格键 暂停/取消暂停
"""
# 展示操作说明
self.centre_text(self.screen, self.font2, -SIZE * 4, '回车键:开始/重开')
self.centre_text(self.screen, self.font2, -SIZE * 2, '空格键:暂停/取消暂停')
self.centre_text(self.screen, self.font2, 0, 'w,s,a,d/上下左右:控制移动方向')
def main(self):
"""
运行主窗口
"""
while True:
self.init_settings() # 初始化界面
self.countdown() # 时间倒计时
self.control() # 玩家操作
if __name__ == '__main__':
try:
a = GAME()
a.main()
except Exception as e:
# 处理异常的代码
print(f"异常: {e}")