在前面的讲解
已经实现了游戏的组成元素的类,从这种类的构造可以看到类的构造的基本原则:尽量小,只关注这一个类,类是有边界的。当然也可以看到:两个类其实还可以再优化一下,产生一个基类,然后从这个基类派生出这两个类会更合理一些。
有了基本游戏元素,那么就可以构造一个地图了。
三
游戏地图的构造及相关判断动作class gameMap,主要是加载地图上对应的元素,判断是否通关,判断1和2中计算出的位置是否可以到达。这里不涉及到pygame的应用,和前端页面没有太大关系,可以理解为就是个后端的服务,是在内存中的一个模拟,后续再一并画到游戏界面即可。在分层结构中,类似于中间层。
3.1
from workerSprite import *
from gameElementSprite import *
from params import Params
from itertools import chain
'''
构造游戏地图
'''
class gameMap():
def __init__(self, num_cols, num_rows):
self.walls = []
self.boxes = []
self.targets = []
self.num_cols = num_cols
self.num_rows = num_rows
'''
增加游戏元素
'''
def addGameElement(self, gameelementtype, col, row):
if gameelementtype == 'wall':
self.walls.append(gameElementSprite('wall.png', col, row))
elif gameelementtype == 'box':
self.boxes.append(gameElementSprite('box.png', col, row))
elif gameelementtype == 'target':
self.targets.append(gameElementSprite('target.png', col, row))
注解:
1、界面上所有的箱子box,外墙wall,终点target分别组成一个列表(数组)
2、addGameElement函数中可以看到将游戏各元素实例化,包括其坐标信息,不清楚的可以查阅
python+pygame实现推箱子小游戏之二的代码,串起来还是比较好理解的。
为了将这些元素都 “画” 在游戏界面上,就添加
3.2
from workerSprite import *
from gameElementSprite import *
from params import Params
from itertools import chain
'''
构造游戏地图
'''
class gameMap():
...
'''
创建游戏元素迭代器
'''
def elemsIter(self):
for elemyield in chain(self.targets, self.walls, self.boxes):
yield elemyield
'''
逐一画出游戏上的元素,最终完成游戏地图
'''
def draw(self, screen):
for elemyield in self.elemsIter():
elemyield.draw(screen)
注解:
1、为了画这些元素,所以首先将它们放在一起实现迭代器,平时可能接触的不多,或者使用起来很少,但这个可以作为一个容器的使用场景来学习一下。这样画的时候就不需要从各个元素列表中一并读取来实现了(想象一下,如果这个列表非常大,岂不麻烦),每当 yield 被调用时,函数会生成一个新的值,然后暂停执行。当下一个值被请求时(例如,在 for 循环中的下一次迭代),函数会从上次离开的地方恢复执行,再次运行到 yield 语句,然后再次暂停。这种方法对内存的利用更高效,因为我们并不需要在内存中一次性存储所有的数字。无论列表数组多大,这个生成器在任何时候都只会产生一个数字。
3.3
通过遍历该关卡中所有的箱子box是否都在指定位置target, 是的话就是通关了:
from workerSprite import *
from gameElementSprite import *
from params import Params
from itertools import chain
'''
构造游戏地图
'''
class gameMap():
...
'''
通过遍历该关卡中所有的箱子box是否都在指定位置target, 是的话就是通关了
'''
def curLevelCompleted(self):
for box in self.boxes:
is_completed = False
for target in self.targets:
if box.col == target.col and box.row == target.row:
is_completed = True
break
if not is_completed:
return False
return True
注解:
1、因为都是方块,所以只需要比较行,列坐标是否对应即可。
3.4
因为都是方块,所以利用python中的rect的碰撞测试函数“预判”箱子能不能移动的逻辑。
from workerSprite import *
from gameElementSprite import *
from params import Params
from itertools import chain
'''
构造游戏地图
'''
class gameMap():
...
'''
判断某位置是否可到达,推到了边界或有障碍物就不能通过
'''
def isCanPushPos(self, col, row):
if col >= 0 and row >= 0 and col < self.num_cols and row < self.num_rows:
block_size = Params.get('blockSize')
test1 = self.walls + self.boxes
test2 = pygame.Rect(col * block_size, row * block_size, block_size, block_size)
return test2.collidelist(test1) == -1 #pygame中rect的碰撞检测函数,本游戏都是规则的方块,所以用这最方便。
else:
return False
注解:
1、只有墙和箱子是障碍物,故首先test1 = self.walls + self.boxes,再测试。
2、注意测试函数collidelist,pygame中有几个类似的(spritrcollide,collide_mask,groupcollide,collide_rect...)使用的时候注意其使用场景
3.5
界面上的箱子可能有多个,所以要有获取“这个箱子”的对象的方法
from workerSprite import *
from gameElementSprite import *
from params import Params
from itertools import chain
'''
构造游戏地图
'''
class gameMap():
...
'''
获得某位置的box
'''
def getTheBoxByPos(self, col, row):
for box in self.boxes:
if box.col == col and box.row == row:
return box
return None
待续...