赞
踩
今天分享得是:用python自带的tkinter做游戏系列的第三弹,推箱子简易版 篇
之前的二篇博文介绍的分别是贪食蛇和俄罗斯方块。
用python自带的tkinter做游戏(一)—— 贪吃蛇 篇
用python自带的tkinter做游戏(二)—— 俄罗斯方块 篇
用python自带的tkinter做游戏(三)—— 推箱子简易版 篇
首先我要申明的是,本人也是新手一名,刚学Python半年。
其实网上相关的游戏教程也有很多,我自己也阅读过不少,不过无奈自身水平有限,很多都是一知半解。不过借助各位大神们的思路,再加上自己的瞎折腾,也算是完成了几个入门的小游戏。
拿出来分享也是为了给更多的新手们提供不同的思路,希望对你们有所帮助。
毕竟本人也是新手一枚,太复杂高深的代码也不会。
也希望各位高手给点鼓励和支持,谢谢~
好了,回到正题。之前的两款游戏,贪吃蛇和俄罗斯方块,都属于方块类游戏,画面比较单一,规则也简单,制作起来比较容易上手。
其实推箱子这个游戏严格上来说也属于方块类游戏,毕竟人物和箱子的移动距离还是以单元格为单位的,只是游戏规则更为复杂一些。
之所以称之为简易版,因为今天要介绍的只是推箱子这个游戏系统的本身。
所以游戏的画面还是以各种颜色的方格为主。
下一期再介绍一下如何美化(用图片或者像素作画)。
推箱子的游戏界面中一共会出现7种状态。
self.color_dict = {0: 'white', # 0表示空白
1:'#808080', # 1表示墙
2: 'yellow', # 2表示空地上的人
3: 'green', # 3表示空地上的箱子
4: 'pink', # 4表示终点
5: 'red', # 5表示终点上的的箱子
6:'#ffa579', # 6表示在终点上的人
}
这里用七种颜色来指代七种状态。
这七种状态中,除了墙是固定不动的,其它的都会根据游戏的进程而产生变化,很考验逻辑思维。
定义好了7种状态,就可以画地图了。游戏地图我在网上找了几个,有兴趣的同学也可以自行编辑关卡。
try: # 同目录下搜寻m.txt地图扩充文件,若没有此文件就用内置地图
with open("m.txt", "r") as f:
game_map = f.read()
game_map = eval(game_map)
except:
game_map = [[
[0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 1, 4, 1, 0, 0, 0],
[0, 0, 1, 0, 1, 1, 1, 1],
[1, 1, 1, 3, 0, 3, 4, 1],
[1, 4, 0, 3, 2, 1, 1, 1],
[1, 1, 1, 1, 3, 1, 0, 0],
[0, 0, 0, 1, 4, 1, 0, 0],
[0, 0, 0, 1, 1, 1, 0, 0]
],[
我这里的设计思路是关卡和游戏本身分离,关卡是保存在m.txt中,方便日后升级更新。当然没有也无妨,还可以使用内置的地图。
代码里只展现了第一关,实际总共有六关,其中第六关是大地图测试关。
解释下什么是大地图,贪吃蛇和俄罗斯方块的游戏地图都是固定的,都属于小地图。
当游戏的地图大于显示屏幕的时候,这时候移动人物你会发现,其实人物只是在屏幕的中间摇摆,移动的只是地图,只有到了地图的尽头人物才正式开始移动。
其实大多数的RPG游戏和卷轴类游戏都属于大地图。所以这样看来,tkinter可以制作任何类型的游戏。当然了,tkinter不支持音效,不过这不在本文的讨论范围之内。
好了,言归正传,回到推箱子的游戏话题上。不管什么类型的游戏,只要是产生了位移,就需要获取坐标这个重要的数据。
def boxman_xy(self): """ 获取人物坐标 """ global boxman_x, boxman_y xy = [] for i in range(0,len(game_map[self.game_stage-1])): try: # 查找数值为2的坐标,没有就返回0。为防止人物出现在0列,先加上1,最后再减去。 x = game_map[self.game_stage-1][i].index(2) + 1 except: x = 0 xy.append(x) boxman_x = max(xy) boxman_y = xy.index(boxman_x) boxman_x = boxman_x - 1 # 之前加1,现在减回 if boxman_x == -1: # 没有数值为2,说明人物在终点,即数值等于6 xy = [] for i in range(0,len(game_map[self.game_stage-1])): try: x = game_map[self.game_stage-1][i].index(6) + 1 except: x = 0 xy.append(x) boxman_x = max(xy) boxman_y = xy.index(boxman_x) boxman_x = boxman_x - 1
因为人物有两种状态,不是站在空地上就是站在终点上。所以先搜索一遍空地上的人,没有的话再搜索终点上的,总有一款适合你~
人物的移动比贪吃蛇移动要复杂的多,一步步来吧,先看看不推箱子时的移动。
# 0表示空白, 1表示墙, 2表示人, 3表示箱子, 4表示终点, 5表示已完成的箱子, 6表示在终点上的人
def operation_forward_none(f1,f2,f3,f4,f5):
""" 前方是空地或是终点 """
if game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] == f1: ### 前方是空地或是终点
if game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] == 2: # 人站在空地上
game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = f2 ### 人离开后是空地
game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f3 ### 前方是空地或是终点
else: # 人站在终点上
game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = f4 ### 身后是终点
game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f5 ### 前方是空地或是终点
在前方没有箱子的情况下,人物行走的代码就出现了。
operation_forward_none(0,0,2,4,2)
operation_forward_none(4,0,6,4,6)
当时写代码的时候大脑也是短路了。人站在空地上,离开后当然应该是空地了,终点也是。不然还能少写两个参数,现在也懒得改了,多两个参数就多两个吧。
反正就是空地(0),空地上的人(2),终点(4)和终点上的人(6)之间的关系。真正难的还在后面,前方是箱子的情况下,该如何操作呢?
def operation_forward_box(f1,f2,f3,f4,f5,f6,f7):
""" 前方是空地上的箱子或是已完成的箱子 """
if game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] == f1: ### 前方是空地上的箱子或是已完成的箱子
if game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] == 2: # 人站在空地
if game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] == f2: ### 箱子的前方是空地或终点
game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = 0 # 人离开后是空地
game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f3 ### 前方是空地或是终点
game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] = f4 ### 前方是空地上的箱子或是已完成的箱子
else: ### 人站在终点上
if game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] == f5: ### 箱子的前方是空地或是终点
game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = 4 # 身后是终点
game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f6 ### 前方是空地或是终点
game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] = f7 ### 箱子的前方是空地或是终点
有点晕是不是?没事,请慢慢理解~
operation_forward_box(3,0,2,3,0,2,3)
operation_forward_box(3,4,2,5,4,2,5)
operation_forward_box(5,0,6,3,0,6,3)
operation_forward_box(5,4,6,5,4,6,5)
箱子也分两种状态,空地上的箱子和终点上的箱子。加上人的两种状态,一共有四种结果。
其实本游戏最烧脑的地方是撤消功能。最初我用的是简单粗暴的方式,直接是每走一步就复制一份地图,撤消时直接换地图就是。但这样做太耗内存,而且太没技术含量。为了追求完美,最终还是花了点心思在这个撤消功能上。
temp = [] # 记录每步的操作,供撤消使用。 temp.append(boxman_y) # 保存XY轴坐标值,即人物所在单元格的坐标 temp.append(boxman_x) # 后面6个分别是中,上,下,左,右和前方单元格的值 temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y - 1][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y + 1][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x - 1]) temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x + 1]) temp.append(game_map[self.game_stage-1][boxman_y + y][boxman_x + x]) record_list.append(temp) if len(record_list) > 1: if record_list[-1] == record_list[-2]: del record_list[-1] # 删除连续相同的数据
首先呢,在走出第一步的时候,就开始记录各项数据,包括人物的坐标和上下左右等一共八个数据。为了精简,删除了连续相同的数据(因为前面是墙而滞留的一步)。
def restore(): """ 撤销步骤的函数 """ m = game_map[self.game_stage-1] before_forward = 0 # 之前所面对的(0是临时值) before_stand = record_list[-2][2] # 之前所站的单元格的值 now_stand = record_list[-1][2] # 当前所站的单元格的值 now_forward = record_list[-1][7] # 当前所面对的单元格的值 before_x = record_list[-2][1] # 之前所站的X轴坐标 before_y = record_list[-2][0] # 之前所站的Y轴坐标 now_x = record_list[-1][1] # 当前所站的X轴坐标 now_y = record_list[-1][0] # 当前所站的Y轴坐标 b_up = record_list[-2][3] # 之前上方单元格的值 b_dw = record_list[-2][4] # 之前下方单元格的值 b_lf = record_list[-2][5] # 之前左方单元格的值 b_rg = record_list[-2][6] # 之前右方单元格的值 # 推断出之前所面对的单元格的值 if before_x > now_x: next_x = now_x - 1 before_forward = b_lf elif before_x < now_x: next_x = now_x + 1 before_forward = b_rg else: next_x = now_x if before_y > now_y: next_y = now_y - 1 before_forward = b_up elif before_y < now_y: next_y = now_y + 1 before_forward = b_dw else: next_y = now_y # 0表示空白, 1表示墙, 2表示人, 3表示箱子, 4表示终点, 5表示已完成的箱子, 6表示在终点上的人 m[before_y][before_x] = before_stand # 人退回之前的状态,2或者6 m[ now_y][ now_x] = before_forward # 推断出当前面对的单元格的值 if before_forward == 3: if now_forward == 3: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 0 elif now_forward == 5: if now_stand == 2: m[next_y][next_x] = 4 elif now_stand == 6: m[next_y][next_x] = 0 elif before_forward == 5: if now_forward == 3: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 0 elif now_forward == 5: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 4 restore() del record_list[-1] # 每撤消一步就删除最后一组列表
撤消的逻辑要比之前的推箱子更复杂,撤消过程中每撤消一步就删除最后一组记录。
等record_list删除到只剩最后一组数据后,最后一步的撤消就直接恢复初始地图就可以了。
移动的问题搞定了,后面的都简单了,先看看通关条件。
def game_pass(): # 通关条件为箱子数为0
""" 获取箱子数量,等于0的话过关 """
xy = []
for i in range(0,len(game_map[self.game_stage-1])):
x = game_map[self.game_stage-1][i].count(3)
xy.append(x)
box_number = sum(xy)
if box_number == 0:
pass_win()
空地上的箱子数为0就算通关了,就这么简单。
其它的功能也简单,就不逐一说明了。
至于大地图,其实也简单,就是加入两个调节值,调节坐标的偏差。
移动的时候,根据人物的坐标,选择是移动地图还是人物。
当然了,如果加载的是大地图,必须要以人物为坐标中心载入。具体的可以自行去第六关测试关体验一下。
因为关卡少,所以选关画面做的粗糙了些。
游戏中也可以直接用数字键来选择关卡(反正也只有六关)。
展示一下游戏图(第四关为小地图):
画面是简陋了些,下一期就来谈谈如何改进游戏画面。
敬请期待:
用python自带的tkinter做游戏(四)—— 推箱子重制版 篇
最终附上本篇的完整代码:
# -*- coding: utf-8 -*- """ Created on Tue Mar 16 13:31:29 2021 @author: Juni Zhu (wechat:znix1116) """ import tkinter as tk import copy from tkinter.messagebox import showinfo try: # 同目录下搜寻m.txt地图扩充文件,若没有此文件就用内置地图 with open("m.txt", "r") as f: game_map = f.read() game_map = eval(game_map) except: game_map = [[ [0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 1, 4, 1, 0, 0, 0], [0, 0, 1, 0, 1, 1, 1, 1], [1, 1, 1, 3, 0, 3, 4, 1], [1, 4, 0, 3, 2, 1, 1, 1], [1, 1, 1, 1, 3, 1, 0, 0], [0, 0, 0, 1, 4, 1, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0] ],[ [0, 0, 0, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0, 0, 0, 1, 0], [1, 1, 4, 0, 3, 1, 1, 0, 1, 1], [1, 4, 4, 3, 0, 3, 0, 0, 2, 1], [1, 4, 4, 0, 3, 0, 3, 0, 1, 1], [1, 1, 1, 1, 1, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0] ],[ [0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 1, 4, 4, 1, 0, 0], [0, 1, 1, 0, 4, 1, 1, 0], [0, 1, 0, 0, 3, 4, 1, 0], [1, 1, 0, 3, 0, 0, 1, 1], [1, 0, 0, 1, 3, 3, 0, 1], [1, 0, 0, 2, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1] ],[ [1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 0, 0, 0, 1], [1, 0, 3, 4, 4, 3, 0, 1], [1, 2, 3, 4, 5, 0, 1, 1], [1, 0, 3, 4, 4, 3, 0, 1], [1, 0, 0, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1] ],[ [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0, 3, 3, 0, 1, 0], [1, 1, 1, 1, 1, 1, 0, 3, 1, 0, 0, 1, 0], [1, 4, 4, 4, 1, 1, 1, 0, 1, 0, 0, 1, 1], [1, 4, 0, 0, 1, 0, 0, 3, 0, 1, 0, 0, 1], [1, 4, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 1], [1, 4, 0, 0, 1, 0, 0, 3, 0, 1, 0, 0, 1], [1, 4, 4, 4, 1, 1, 1, 0, 1, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 0, 3, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 2, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0], ],[ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1], [1, 0, 4, 4, 4, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 4, 4, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 5, 0, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ]] class Boxman(): """ 推箱子游戏 """ def __init__(self, game_stage): """ 游戏参数设置 """ self.game_stage = game_stage # 游戏关卡 self.canvas_bg = '#d7d7d7' # 游戏背景色 self.cell_size = 48 # 方格单元格大小 self.cell_gap = 1 # 方格间距 self.frame_x = 25 # 左右边距 self.frame_y = 25 # 上下边距 self.max_cells = 10 # 游戏画面长宽最大单元格数 self.win_w_plus = 220 # 窗口右边额外多出的宽度 self.big_map = 0 # 判断当前地图是否是超出窗口大小。1为是,0为不是 # 根据地图自动调整窗口大小 self.canvas_w = len(game_map[self.game_stage-1][0]) * self.cell_size + self.frame_x*2 self.canvas_h = len(game_map[self.game_stage-1] ) * self.cell_size + self.frame_y*2 self.color_dict = {0: 'white', # 0表示空白 1:'#808080', # 1表示墙 2: 'yellow', # 2表示空地上的人 3: 'green', # 3表示空地上的箱子 4: 'pink', # 4表示终点 5: 'red', # 5表示终点上的的箱子 6:'#ffa579', # 6表示在终点上的人 } # 若地图过大,窗口则根据max_cells值来设定大小 if len(game_map[self.game_stage-1][0]) > self.max_cells: self.big_map = 1 self.canvas_w = self.cell_size*self.max_cells + self.frame_x*2 if len(game_map[self.game_stage-1]) > self.max_cells: self.big_map = 1 self.canvas_h = self.cell_size*self.max_cells + self.frame_y*2 self.win_w_size = self.canvas_w + self.win_w_plus self.win_h_size = self.canvas_h def create_canvas(self): """ 创建canvas """ global canvas canvas = tk.Canvas(window, bg=self.canvas_bg, height=self.canvas_h, width=self.canvas_w, highlightthickness = 0) def window_center(self,window,w_size,h_size): """ 窗口居中 """ screenWidth = window.winfo_screenwidth() # 获取显示区域的宽度 screenHeight = window.winfo_screenheight() # 获取显示区域的高度 left = (screenWidth - w_size) // 2 top = (screenHeight - h_size) // 2 window.geometry("%dx%d+%d+%d" % (w_size, h_size, left, top)) def create_game_cells(self,a,b): # a,b值为偏差值,若地图大于窗口的话,用于调节起始坐标 """ 创建初始版的游戏单元格 """ # 通过game_map列表,对应字典color_dict里的颜色画出地图 for y in range(0,len(game_map[self.game_stage-1])-b): for x in range(0,len(game_map[self.game_stage-1][0])-a): canvas.create_rectangle(self.frame_x + self.cell_size * x + self.cell_gap, self.frame_y + self.cell_size * y + self.cell_gap, self.frame_x + self.cell_size * (x+1), self.frame_y + self.cell_size * (y+1), fill = self.color_dict[game_map[self.game_stage-1][y+b][x+a]], outline = self.canvas_bg, width = 0) canvas.place(x=0,y=0) def boxman_xy(self): """ 获取人物坐标 """ global boxman_x, boxman_y xy = [] for i in range(0,len(game_map[self.game_stage-1])): try: # 查找数值为2的坐标,没有就返回0。为防止人物出现在0列,先加上1,最后再减去。 x = game_map[self.game_stage-1][i].index(2) + 1 except: x = 0 xy.append(x) boxman_x = max(xy) boxman_y = xy.index(boxman_x) boxman_x = boxman_x - 1 # 之前加1,现在减回 if boxman_x == -1: # 没有数值为2,说明人物在终点,即数值等于6 xy = [] for i in range(0,len(game_map[self.game_stage-1])): try: x = game_map[self.game_stage-1][i].index(6) + 1 except: x = 0 xy.append(x) boxman_x = max(xy) boxman_y = xy.index(boxman_x) boxman_x = boxman_x - 1 def move_action(self,event): """ 按键控制 """ def Boxman_move(event,key,x,y): """ 操控人物 """ # 0表示空白, 1表示墙, 2表示人, 3表示箱子, 4表示终点, 5表示已完成的箱子, 6表示在终点上的人 def operation_forward_none(f1,f2,f3,f4,f5): """ 前方是空地或是终点 """ if game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] == f1: ### 前方是空地或是终点 if game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] == 2: # 人站在空地上 game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = f2 ### 人离开后是空地 game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f3 ### 前方是空地或是终点 else: # 人站在终点上 game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = f4 ### 身后是终点 game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f5 ### 前方是空地或是终点 def operation_forward_box(f1,f2,f3,f4,f5,f6,f7): """ 前方是空地上的箱子或是已完成的箱子 """ if game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] == f1: ### 前方是空地上的箱子或是已完成的箱子 if game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] == 2: # 人站在空地 if game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] == f2: ### 箱子的前方是空地或终点 game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = 0 # 人离开后是空地 game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f3 ### 前方是空地或是终点 game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] = f4 ### 前方是空地上的箱子或是已完成的箱子 else: ### 人站在终点上 if game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] == f5: ### 箱子的前方是空地或是终点 game_map[self.game_stage-1][boxman_y + y*0][boxman_x + x*0] = 4 # 身后是终点 game_map[self.game_stage-1][boxman_y + y*1][boxman_x + x*1] = f6 ### 前方是空地或是终点 game_map[self.game_stage-1][boxman_y + y*2][boxman_x + x*2] = f7 ### 箱子的前方是空地或是终点 direction = event.keysym if(direction == key): operation_forward_none(0,0,2,4,2) operation_forward_none(4,0,6,4,6) operation_forward_box(3,0,2,3,0,2,3) operation_forward_box(3,4,2,5,4,2,5) operation_forward_box(5,0,6,3,0,6,3) operation_forward_box(5,4,6,5,4,6,5) Boxman(self.game_stage).boxman_xy() # 刷新坐标 temp = [] # 记录每步的操作,供撤消使用。 temp.append(boxman_y) # 保存XY轴坐标值,即人物所在单元格的坐标 temp.append(boxman_x) # 后面6个分别是中,上,下,左,右和前方单元格的值 temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y - 1][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y + 1][boxman_x + 0]) temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x - 1]) temp.append(game_map[self.game_stage-1][boxman_y + 0][boxman_x + 1]) temp.append(game_map[self.game_stage-1][boxman_y + y][boxman_x + x]) record_list.append(temp) if len(record_list) > 1: if record_list[-1] == record_list[-2]: del record_list[-1] # 删除连续相同的数据 def game_select_stage(event,key): """ 返回游戏主页面 """ global game_map direction = event.keysym if(direction == key): game_map = copy.deepcopy(backup_map) window.destroy() # 窗口关闭并启动起始窗口 Boxman(self.game_stage).index_game() def reset_stage(event,key): """ 重置关卡 """ global game_map direction = event.keysym if(direction == key): del record_list[:] game_map = copy.deepcopy(backup_map) def to_stage(event,key,stage): # 直接按数字键选关 """ 选择游戏关卡 """ global game_map direction = event.keysym if(direction == key): del record_list[:] game_map = copy.deepcopy(backup_map) window.destroy() Boxman(stage).window_open() def restore_stage(event,key): """ 撤销功能 """ direction = event.keysym if(direction == key): def restore(): """ 撤销步骤的函数 """ m = game_map[self.game_stage-1] before_forward = 0 # 之前所面对的(0是临时值) before_stand = record_list[-2][2] # 之前所站的单元格的值 now_stand = record_list[-1][2] # 当前所站的单元格的值 now_forward = record_list[-1][7] # 当前所面对的单元格的值 before_x = record_list[-2][1] # 之前所站的X轴坐标 before_y = record_list[-2][0] # 之前所站的Y轴坐标 now_x = record_list[-1][1] # 当前所站的X轴坐标 now_y = record_list[-1][0] # 当前所站的Y轴坐标 b_up = record_list[-2][3] # 之前上方单元格的值 b_dw = record_list[-2][4] # 之前下方单元格的值 b_lf = record_list[-2][5] # 之前左方单元格的值 b_rg = record_list[-2][6] # 之前右方单元格的值 # 推断出之前所面对的单元格的值 if before_x > now_x: next_x = now_x - 1 before_forward = b_lf elif before_x < now_x: next_x = now_x + 1 before_forward = b_rg else: next_x = now_x if before_y > now_y: next_y = now_y - 1 before_forward = b_up elif before_y < now_y: next_y = now_y + 1 before_forward = b_dw else: next_y = now_y # 0表示空白, 1表示墙, 2表示人, 3表示箱子, 4表示终点, 5表示已完成的箱子, 6表示在终点上的人 m[before_y][before_x] = before_stand # 人退回之前的状态,2或者6 m[ now_y][ now_x] = before_forward # 推断出当前面对的单元格的值 if before_forward == 3: if now_forward == 3: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 0 elif now_forward == 5: if now_stand == 2: m[next_y][next_x] = 4 elif now_stand == 6: m[next_y][next_x] = 0 elif before_forward == 5: if now_forward == 3: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 0 elif now_forward == 5: if now_stand == 2: m[next_y][next_x] = 0 elif now_stand == 6: m[next_y][next_x] = 4 restore() del record_list[-1] # 每撤消一步就删除最后一组列表 def game_pass(): # 通关条件为箱子数为0 """ 获取箱子数量,等于0的话过关 """ xy = [] for i in range(0,len(game_map[self.game_stage-1])): x = game_map[self.game_stage-1][i].count(3) xy.append(x) box_number = sum(xy) if box_number == 0: pass_win() def pass_win(): """ 箱子为零时,显示过关窗口 """ global game_map showinfo('恭喜过关','返回首页') window.destroy() game_map = copy.deepcopy(backup_map) Boxman(0).index_game() def move_map_ws(event,key,a,b): """ 若是大地图,则上下移动地图 """ global py direction = event.keysym if(direction == key): my = len(game_map[self.game_stage-1]) if self.big_map == 1: if boxman_y >= int(self.max_cells//2) + a: if boxman_y < my - int(self.max_cells//2): if game_map[self.game_stage-1][boxman_y + b] [boxman_x] not in [1] and \ game_map[self.game_stage-1][boxman_y + b*2][boxman_x] not in [1,3,5]: py = boxman_y - int(self.max_cells//2) + b elif boxman_y >= my - int(self.max_cells//2): py = my - self.max_cells else: py = 0 else: py = 0 def move_map_ad(event,key,a,b): """ 若是大地图,则左右移动地图 """ global px direction = event.keysym if(direction == key): mx = len(game_map[self.game_stage-1][0]) if self.big_map == 1: if boxman_x >= int(self.max_cells/2) + a: if boxman_x < mx - int(self.max_cells//2): if game_map[self.game_stage-1][boxman_y][boxman_x + b ] not in [1] and \ game_map[self.game_stage-1][boxman_y][boxman_x + b*2] not in [1,3,5] : px = boxman_x - int(self.max_cells//2) + b elif boxman_x >= mx - int(self.max_cells//2): px = mx - self.max_cells else: px = 0 else: px = 0 move_map_ws(event, 'w', 1, -1) move_map_ws(event, 's', 0, 1) move_map_ad(event, 'a', 1, -1) move_map_ad(event, 'd', 0, 1) move_map_ws(event, 'W', 1, -1) move_map_ws(event, 'S', 0, 1) move_map_ad(event, 'A', 1, -1) move_map_ad(event, 'D', 0, 1) move_map_ws(event, 'Up', 1, -1) move_map_ws(event, 'Down', 0, 1) move_map_ad(event, 'Left', 1, -1) move_map_ad(event,'Right', 0, 1) Boxman_move(event, 'w', 0, -1) Boxman_move(event, 's', 0, 1) Boxman_move(event, 'a', -1, 0) Boxman_move(event, 'd', 1, 0) Boxman_move(event, 'W', 0, -1) Boxman_move(event, 'S', 0, 1) Boxman_move(event, 'A', -1, 0) Boxman_move(event, 'D', 1, 0) Boxman_move(event, 'Up', 0, -1) Boxman_move(event, 'Down', 0, 1) Boxman_move(event, 'Left', -1, 0) Boxman_move(event,'Right', 1, 0) game_select_stage(event, 'p') # 返回主页面 game_select_stage(event, 'P') reset_stage(event, 'j') # 重置该关卡 reset_stage(event, 'J') to_stage(event, '1', 1) to_stage(event, '2', 2) to_stage(event, '3', 3) to_stage(event, '4', 4) to_stage(event, '5', 5) to_stage(event, '6', 6) if len(record_list) <= 1: reset_stage(event, 'r') reset_stage(event, 'R') else: restore_stage(event, 'r') restore_stage(event, 'R') move_map_ws(event, 'r', 0, 0) move_map_ad(event, 'r', 0, 0) move_map_ws(event, 'R', 0, 0) move_map_ad(event, 'R', 0, 0) Boxman(self.game_stage).create_canvas() # 不刷新的话在大地图中有小BUG canvas.delete('all') Boxman(self.game_stage).create_game_cells(px,py) Boxman(self.game_stage).boxman_xy() # 重新刷新人物坐标,不然会有BUG game_pass() def window_open(self): """ 开启游戏窗口 """ global window,txt_lable,px,py window = tk.Tk() window.focus_force() window.title('Boxman') Boxman(self.game_stage).window_center(window,self.win_w_size,self.win_h_size) if self.game_stage == 0: # 若等于0,代表是最后一关 n = len(game_map) else: n = self.game_stage txt_lable = tk.Label(window, text= "当前为第" + str(n) + "关" +"\n白色单元格为空地" +"\n灰色单元格为墙" +"\n黄色单元格为打工人" +"\n绿色单元格为箱子" +"\n粉色单元格为终点" +"\n红色单元格为已完成的箱子" +"\n橘色单元格为站在终点上的人" +"\n" +"\n字母ADSW或方向键移动" +"\n字母键P返回主页面" +"\n字母键J重置本关" +"\n字母键R为撤消" +"\n数字键1~6直接选择关卡" +"\n(第六关为测试关)" +"\n" +"\n" +"\nBy Juni Zhu" +"\n微信: znix1116" , font=('Arial', 11),anchor="ne", justify="left") txt_lable.pack(side='right') Boxman(self.game_stage).boxman_xy() # 如果是大地图,更换起始坐标,以人物为中心建立地图 mx = len(game_map[self.game_stage-1][0]) my = len(game_map[self.game_stage-1]) if self.big_map == 1: if boxman_x > int(self.max_cells//2): if boxman_x < mx - int(self.max_cells//2): px = boxman_x - int(self.max_cells//2) elif boxman_x >= mx - int(self.max_cells//2): px = mx - self.max_cells else: px = 0 else: px = 0 if self.big_map == 1: if boxman_y > int(self.max_cells//2): if boxman_y < my - int(self.max_cells//2): py = boxman_y - int(self.max_cells//2) elif boxman_y >= my - int(self.max_cells//2): py = my - self.max_cells else: py = 0 else: py = 0 Boxman(self.game_stage).create_canvas() Boxman(self.game_stage).create_game_cells(px,py) window.bind('<Key>', Boxman(self.game_stage).move_action) window.mainloop() def run_game(self): """ Run Game """ global backup_map,record_list record_list = [] # 记录每步的操作,供撤消用 backup_map = [] # 备份地图,供恢复用 backup_map = copy.deepcopy(game_map) # 备份地图,需要用深度复制 Boxman(self.game_stage).window_open() def index_game(self): """ 起始界面 """ # 窗口大小会根据按键的数量自动调整 for i in range(1,len(game_map)+1): # 批量生成函数,to_game_1,2,3,4,5... exec("def to_game_" + str(i) + "():\n\ index_win.destroy()\n\ Boxman(" + str(i) + ").run_game()") global index_win index_win = tk.Tk() index_win.focus_force() index_win.title('Welcome') Boxman(self.game_stage).window_center(index_win,200,(len(game_map)+1)*30) for i in range(1,len(game_map)+1): # 批量添加按键 exec("b" + str(i) + " = tk.Button(index_win, text='" + str(i) + "', font=('Arial', 12), width=10, height=1, command=to_game_" + str(i) + ")") exec("b" + str(i) + ".pack()") index_win.mainloop() if __name__ == '__main__': Boxman(0).index_game()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。