跳转目录
前言
运行示例
程序分析
捕获键盘操作
输出游戏画面
代码分析一
安装运行环境
# 测试python版本为3.9.8
pip install keyboard
pip install win32
游戏地图的实现
- 地图的初始化
class Map():
def __init__(self, width=10, height=10):
if (width < 10):
width = 10
if (height < 10):
height = 10
self.size = (width, height)
self.__map = [[0 for i in range(width)] for i in range(height)]
# 0为空白块,1为食物,2为炸弹,3为蛇头,4为蛇尾
- 读取某位置的元素
def read(self, x, y):
if (x >= 0 and x < self.size[0] and y >= 0 and y < self.size[1]):
return self.__map[y][x]
return -1 # 如果该位置超过地图边界, 返回-1
- 改变某位置的元素
def write(self, x, y, val=0):
self.__map[y][x] = val
- 将地图以可显示形式输出
def list(self):
ls = []
ls.append('# ' * (self.size[0] + 2)) # 地图上边界
for line in self.__map:
li = '# '
for k in line:
if (k == 0): # 0表示空白块
li += ' '
elif (k == 1): # 1表示食物
li += "\033[0;32m$\033[0m "
elif (k == 2): # 2表示炸弹
li += "\033[0;31m@\033[0m "
elif (k == 3): # 3表示蛇头
li += "\033[0;33m■\033[0m "
elif (k == 4):# 4代表蛇的身体
li += "\033[0;36m■\033[0m "
li += '#'
ls.append(li)
ls.append('# ' * (self.size[0] + 2)) # 地图下边界
return ls
简单测试打印一下地图
ma = Map(20, 20)
ls = ma.list()
for line in ls:
print(line)
炸弹的实现
class Bomb():
def __init__(self, map : Map):
self.x = randint(0, map.size[0] - 1)
self.y = randint(0, map.size[1] - 1)
while (map.read(self.x, self.y) != 0): # 读取地图中该位置是否为空格
self.x = randint(0, map.size[0] - 1)
self.y = randint(0, map.size[1] - 1)
self.life = randint(3, 6) # 随机的存活时间
class Bombs():
def __init__(self):
self.list = [] # 储存每一个炸弹的信息
def update(self, map : Map):
tmp = self.list.copy() # 将炸弹的信息拷贝到一个临时列表中
self.list.clear()
if (randint(0, 49) == 0): # 按概率每秒生成一个新的炸弹
tmp.append(Bomb(map))
for bomb in tmp: # 遍历每一个炸弹
map.write(bomb.x, bomb.y, 0) # 先将炸弹位置的地图重置
bomb.life -= 1 / 50 # 计算存在时间
if (bomb.life > 0): # 如果存在时间大于零将其加入到炸弹列表中
self.list.append(bomb)
del tmp
for bomb in self.list: # 将炸弹显示在地图上
map.write(bomb.x, bomb.y, 2)
食物的实现
- 单个食物的实现
不能说和炸弹很相似, 只能说是一模一样
class Food():
def __init__(self, map : Map):
self.x = randint(0, map.size[0] - 1)
self.y = randint(0, map.size[1] - 1)
while (map.read(self.x, self.y) != 0):
self.x = randint(0, map.size[0] - 1)
self.y = randint(0, map.size[1] - 1)
self.life = randint(3, 6)
- 全部食物信息的实现, 相较于炸弹类, 仅多一个
eat()
方法
class Foods():
def __init__(self):
self.list = []
def update(self, map : Map):
tmp = self.list.copy()
self.list.clear()
if (randint(0, 49) == 0):
tmp.append(Food(map))
for food in tmp:
map.write(food.x, food.y, 0)
food.life -= 1 / 50
if (food.life > 0):
self.list.append(food)
del tmp
for food in self.list:
map.write(food.x, food.y, 1)
def eat(self, x, y): # 将坐标处被吃掉的食物的存在时间变为0, 下一次更新时食物会被删除
for index, food in enumerate(self.list):
if (food.x == x and food.y == y):
self.list[index].life = 0
蛇的实现
- 蛇的初始化
class Snake():
def __init__(self, map : Map):
# [x, y], 创建蛇时需要随机蛇头的位置和方向
self.__head = [randint(3, map.size[0] - 5), randint(3, map.size[1] - 5)] # 随机时需要防止太靠近边界导致开局碰墙
self.__direction = randint(1, 4)
# [[x, y], [x, y], ....]
self.__body = [] # 开始游戏时蛇的身体长度为0
- 蛇身体的移动
def move(self, map : Map, direction=0):
self.__body.insert(0, [self.__head[0], self.__head[1]])
map.write(self.__body[0][0], self.__body[0][1], 4) # 第一节身体位置移动到原蛇头位置
map.write(self.__body[-1][0], self.__body[-1][1], 0) # 删除最后一节蛇尾位置
- 蛇头根据给定方向移动
if (direction != 0): # 为0时表示无方向输入, 按照原来的轨迹移动
self.__direction = direction
if (self.__direction == 1): # 向上
self.__head[1] -= 1
elif (self.__direction == 2): # 向下
self.__head[1] += 1
elif (self.__direction == 3): # 向左
self.__head[0] -= 1
elif (self.__direction == 4): # 向右
self.__head[0] += 1
- 读取蛇头移动后位置处地图的情况
result = map.read(self.__head[0], self.__head[1]) # 移动结果
- 根据移动情况判断游戏下一步操作
longer = False # 是否变长
move = True # 是否能够移动
tip = "just move" # 提示信息
if (result == -1): # 碰墙
move = False
tip = "hit the wall"
elif (result == 1): # 碰到食物
longer = True
tip = "eat food"
elif (result == 2): # 碰到炸弹
move = False
tip = "hit the bomb"
elif (result == 4): # 碰到蛇尾
move = False
tip = "eat your body"
else:
pass
- 根据移动情况判断蛇尾是否变化, 以及返回移动信息
(提示词, (移动后蛇头的坐标x, y))
if (move): # 是否能够移动
if (not longer): # 是否变长
self.__body.pop()
else:
map.write(self.__body[-1][0], self.__body[-1][1], 4)
map.write(self.__head[0], self.__head[1], 3)
return (tip, (self.__head[0], self.__head[1]))
初步测试
game_map = Map(20, 20) # 初始化地图
foods = Foods() # 初始化食物
bombs = Bombs() # 初始化炸弹
snake = Snake(game_map) # 初始化蛇
tick = 0 # 游戏刻, 用于控制蛇的移动速度
while True:
move = ("just move", (0, 0)) # 用来记录蛇move之后的信息
if (tick == 0): # 0刻时蛇移动一次
move = snake.move(game_map, randint(1, 4))
if (move[0] == "eat food"): # 吃到食物执行eat()操作
foods.eat(move[1][0], move[1][1])
elif (move[0] != "just move"): # 触发游戏结束条件
break
foods.update(game_map) # 更新食物
bombs.update(game_map) # 更新炸弹
ls = game_map.list() # 地图可视化
for line in ls:
print(line)
tick = (tick + 1) % 5 # 游戏刻加一
time.sleep(0.02) # 控制游戏帧率
os.system("cls") # 清屏
- 运行效果
键盘控制的实现
key_event()
函数
def key_envent(key):
global direction # 全局变量direction, Snake.move()的方向参数
global gaming # 全局变量gaming, 记录游戏是否正在运行, 以及结束游戏
global pause # 全局变量pause, 用于游戏的暂停操作
if (key.name == "up"): # 按上方向键
direction = 1
elif (key.name == "down"): # 按下方向键
direction = 2
elif (key.name == "left"): # 按左方向键
direction = 3
elif (key.name == "right"): # 按右方向键
direction = 4
elif (key.name == "space"): # 按空格键, 暂停/继续
pause = not pause
elif (key.name == "esc" and gaming): # 按ESC键退出游戏
gaming = False
keyboard.on_press()
绑定
keyboard.on_press(key_envent)
主程序
game函数
def game():
global direction # 方向
global gaming # 游戏是否在进行
global pause # 是否暂停
buffers = Buffers() # 创建一个双缓冲区用于显示游戏画面
game_map = Map(20, 20) # 指定大小创建游戏地图
bombs = Bombs()
foods = Foods()
snake = Snake(game_map)
tick = 0
direction = 0
score = 0 # 记录游戏得分
tip = "" # 记录游戏退出时的提示次
gaming = True
pause = False
start_time = time.time()
while gaming: # 如果游戏结束退出循环
if (pause): # 游戏暂停, 休眠一秒后再判断pause的状态, 降低计算消耗
start_time += 1 # 休眠时时间不流动
time.sleep(1)
continue
loop_time = time.perf_counter() # 记录循环开始时间
move = ("just move", (0, 0))
if (tick == 0):
move = snake.move(game_map, direction)
if (move[0] == "eat food"):
foods.eat(move[1][0], move[1][1])
score += 1
elif (move[0] != "just move"):
tip = move[0]
gaming = False
break
foods.update(game_map)
bombs.update(game_map)
buffers.switch() # 切换画面缓冲区
map_ls = show_info(game_map.list(), score, int(time.time() - start_time)) # 在游戏地图后添加游戏时间, 游戏得分, 排版游戏画面
for line in map_ls: # 将游戏画面输出到下一个缓冲区
buffers.print(line+'\n')
buffers.print("ESC键退出游戏 空格键暂停\\继续")
buffers.flash() # 刷新游戏画面
tick = (tick + 1) % 5
time.sleep(0.02 - (loop_time - time.perf_counter())) # 按照固定时间(0.02s)运行游戏程序, 即指定游戏帧数
end(tip, score, map_ls) # 执行结束函数显示提示信息
图形界面显示分数, 得分
- 向该函数输入转换后的地图列表, 游戏时间, 分数信息, 返回一个新的地图列表, 列表中包含游戏的时间 T 和游戏分数 S
def show_info(map_ls, score, game_time):
pass
return map_ls
结束函数
def end(tip, score, map_ls):
os.system("cls")
for line in map_ls:
print(line)
if (tip == "hit the wall"):
print("\033[0;31m您撞墙后不治身亡!\033[0m")
elif (tip == "hit the bomb"):
print("\033[0;31m炸弹真美味, 可惜会爆炸\033[0m")
elif (tip == "eat your body"):
print("\033[0;31m您真狠, 饿了连自己都不放过\033[0m")
elif (tip == ""):
print("\033[0;31m请问你为什么要退出游戏呢?\033[0m")
print("\033[0;33m游戏结束\033[0m")
print("\033[0;34m您的得分为: \033[0;32m{}\033[0m".format(score))
print("\033[0;33m输入任意内容退出游戏 \033[0;32m输入\033[0;34m空格\033[0;32m重新开始游戏\033[0m")
游戏主函数
def main():
keyboard.on_press(key_envent) # 绑定键盘操作
while True: # 实现游戏的多次
game() # 执行游戏函数
if (input("\n") != " "): # 根据输入内容判断是否进行下一次游戏
break
main() # 运行主函数
源码下载
希望本文对您有所帮助, 感谢您花时间浏览本文