使用pygame实现的贪吃蛇,有BUG或意见欢迎留言
# 导入所需的模块
import sys
import pygame
import random
"""
方框大小 默认9; 空间管理 默认10;实际上只有1px的间隙
BUG思考:1.当用户按了太多无用按钮,会导致事件处理延后
"""
# 默认方框大小
unitLength = 15
rectWidth = 14
# 默认方框颜色
rectColor = pygame.Color("blue")
bgColor = pygame.Color("white")
startColor = pygame.Color("black")
# 默认窗口大小
windowWidth = 600
windowHeight = 600
# 默认size
sizeX = windowWidth // unitLength
sizeY = windowHeight // unitLength
# 默认最大空余list
maxSpare = 200
# 默认空白空间
spareWidth = 10
# 默认起始位置
startPos = spareWidth // 2
# 点对象 实现点之间的运算
class Point:
def __init__(self, x=0, y=0):
self.X = x
self.Y = y
# 加法方法
def __add__(self, other):
x = self.X + other.X
y = self.Y + other.Y
return Point(x, y)
# 减法方法
def __sub__(self, other):
x = self.X - other.X
y = self.Y - other.Y
return Point(x, y)
# 转字符串方法
def __str__(self):
return "(%d,%d)" % (self.X, self.Y)
# 相等方法
def __eq__(self, other):
return self.X == other.X and self.Y == other.Y
# 绝对值方法
def __abs__(self):
return Point(abs(self.X), abs(self.Y))
# 获取坐标
def get_coordinate(self):
return self.X * unitLength, self.Y * unitLength
# 测试是否超出窗口
def is_out_of_window(self):
return not (0 <= self.X < sizeX and 0 <= self.Y < sizeY)
# 用list实现队列,为方便扩容,把list前部当队列尾部
class Queue:
def __init__(self):
self.list = [Point()]
self.start = 0
# 向头部添加数据
def push_front(self, data):
self.list.append(data)
# 获取头部数据
def get_top(self):
return self.list[len(self.list) - 1]
# 输出尾部数据
def pop_back(self):
# 如果前面空闲空间太多,缩短list的长度
if self.start > maxSpare:
self.list = self.list[self.start:]
self.start = 0
self.start = self.start + 1
return self.list[self.start - 1]
# 蛇的头的前一格位置在头的方向
def get_forbid_direct(self):
length = len(self.list)
if length - self.start == 1:
return None
else :
return self.list[length - 2] - self.list[length - 1]
def __getitem__(self, item):
return self.list[self.start + item]
# 用于for in遍历
def __iter__(self):
for i in range(len(self.list) - self.start):
yield self.list[self.start + i]
# 查找某个点在队列中的位置,找到返回序号,否则返回-1
def find(self, item):
for i in range(len(self.list) - self.start):
data = self.list[self.start + i]
if data == item:
return i
else:
return -1
def __str__(self):
string = "["
for i in range(len(self.list) - self.start):
string += str(self.list[self.start + i]) + ","
string = string[:-1]
string = string + "]"
return string
# 使用pygame之前必须初始化
pygame.init()
# 游戏数据主队列
mainQueue = Queue()
# 游戏是否已开始
isStart = False
# 游戏是否已暂停
isPause = False
# 游戏是否失败过
isGameOver = False
# 贪吃蛇运动方向 默认向右
direction = Point(1, 0)
# 设置主屏窗口 四周留下5px的边
screen = pygame.display.set_mode((windowWidth + spareWidth, windowHeight + spareWidth))
# 字体管理
font = pygame.font.SysFont("Garamond", size=30, bold=True)
# 渲染开始和结束字符串
welcome = font.render("Press Enter To Start A New Game", True, (255, 0, 0), startColor)
game_over = font.render("Game Over! Please Press Enter To Restart", True, (255, 0, 0), startColor)
# 获得渲染后的rect
welcome_rect = welcome.get_rect()
game_over_rect = game_over.get_rect()
# rect居中
welcome_rect.center = (windowWidth // 2, windowHeight // 2)
game_over_rect.center = (windowWidth // 2, windowHeight // 2)
# 初始化面板
initSurface = pygame.Surface((windowWidth + spareWidth, windowHeight + spareWidth), flags=pygame.HWSURFACE)
# 设置窗口的标题,即游戏名称
pygame.display.set_caption('贪吃蛇')
# 主图像面板
mainSurface = pygame.Surface((windowWidth, windowHeight), flags=pygame.HWSURFACE)
# 初始化方块对象
rectSurface = pygame.Surface((rectWidth, rectWidth), flags=pygame.HWSURFACE)
rectSurface.fill(color=rectColor)
# 删除使用的方块
deleteSurface = pygame.Surface((rectWidth, rectWidth), flags=pygame.HWSURFACE)
deleteSurface.fill(color=bgColor)
# 食物点
foodPos = Point()
# 产生新的点
def generate_new_point():
while True:
x = random.randint(0, sizeX - 1)
y = random.randint(0, sizeY - 1)
p = Point(x, y)
if mainQueue.find(p) == -1:
return p
def init_game():
global mainQueue, isStart, isGameOver, direction, foodPos
# 初始化游戏数据主队列
mainQueue = Queue()
# 游戏是否已开始初始化
isStart = True
# 游戏是否失败过初始化
isGameOver = False
# 贪吃蛇运动方向初始化 默认向右
direction = Point(1, 0)
mainQueue.push_front(direction)
mainQueue.push_front(Point(2, 0))
# 使用初始化面板初始化屏幕
initSurface.fill(color=bgColor)
screen.blit(initSurface, (0, 0))
# 主图像面板初始化
mainSurface.fill(color=bgColor)
# 初始化食物点
foodPos = generate_new_point()
# 主图像面板点初始化
for item in mainQueue:
mainSurface.blit(rectSurface, item.get_coordinate())
mainSurface.blit(rectSurface, foodPos.get_coordinate())
# python E:\可复用源代码\Python\游戏\pygameTest.py
# 数据项(x,y)指示方块的位置
if __name__ == '__main__':
# 创建时钟对象(控制游戏的FPS)
clock = pygame.time.Clock()
# 固定代码段,实现点击"X"号退出界面的功能,几乎所有的pygame都会使用该段代码
while True:
# 通过时钟对象,指定循环频率,每秒循环60次
clock.tick(5)
# 循环获取事件,监听事件状态
for event in pygame.event.get():
# 判断用户是否点了"X"关闭按钮,并执行if代码段
if event.type == pygame.QUIT:
# 卸载所有模块
pygame.quit()
# 终止程序,确保退出程序
sys.exit()
elif event.type == pygame.KEYDOWN:
# 获取禁止的方向
forbid_direction = mainQueue.get_forbid_direct()
if isStart:
# 根据按键初始化新方向
if event.key == pygame.K_UP:
new_direction = Point(0, -1)
elif event.key == pygame.K_DOWN:
new_direction = Point(0, 1)
elif event.key == pygame.K_LEFT:
new_direction = Point(-1, 0)
elif event.key == pygame.K_RIGHT:
new_direction = Point(1, 0)
# 暂停只在开始时有效
elif event.key == pygame.K_SPACE:
# 保证direction不变
new_direction = direction
isPause = not isPause
elif event.key == pygame.K_ESCAPE:
# 卸载所有模块
pygame.quit()
# 终止程序,确保退出程序
sys.exit()
else:
if event.key == pygame.K_RETURN:
# 只有非游戏中才可以按Enter开始游戏
init_game()
new_direction = direction
if forbid_direction:
if forbid_direction != new_direction:
direction = new_direction
else:
direction = new_direction
if isStart and not isPause:
new_pos = mainQueue.get_top() + direction
# 如果超出范围,游戏结束
if new_pos.is_out_of_window():
isGameOver = True
isStart = False
continue
# 如果新位置是食物位置
if new_pos == foodPos:
# 贪吃蛇吃了食物
mainQueue.push_front(foodPos)
# 生成新的食物点
foodPos = generate_new_point()
# 绘制新的食物点
mainSurface.blit(rectSurface, foodPos.get_coordinate())
else:
# 帧生成
# 第一步 移动贪吃蛇
last_pos = mainQueue.pop_back()
# 丢弃最后的位置后才可以判断贪吃蛇是否头尾相撞 在队列里找到新位置则头尾相撞
if mainQueue.find(new_pos) != -1:
isGameOver = True
isStart = False
continue
# 新位置入队列
mainQueue.push_front(new_pos)
# 删除被移掉的位置
mainSurface.blit(deleteSurface, last_pos.get_coordinate())
# 新增下一个位置
mainSurface.blit(rectSurface, new_pos.get_coordinate())
screen.blit(mainSurface, (startPos, startPos))
# 暂停时只画缓存的内容
elif isStart and isPause:
screen.blit(mainSurface, (startPos, startPos))
else:
# 转为黑色背景
initSurface.fill(color=startColor)
screen.blit(initSurface, (0, 0))
if isGameOver:
screen.blit(game_over, game_over_rect)
else:
screen.blit(welcome, welcome_rect)
pygame.display.flip() # 更新屏幕内容