Python 使用组合、策略模式和字典更好地实例化字典中存储的类
我用python开发了一个类似RogueLike的程序,我尽量利用OOP和我的一点知识为学生们构建一个python课程Python 使用组合、策略模式和字典更好地实例化字典中存储的类,python,oop,design-patterns,pygame,roguelike,Python,Oop,Design Patterns,Pygame,Roguelike,我用python开发了一个类似RogueLike的程序,我尽量利用OOP和我的一点知识为学生们构建一个python课程 mapRogue = ['~~~~~~~~~~', '~~~~.....Y', 'YYYYY+YYYY', 'YYYY....YY'] 我想将这个字符串映射转换为2D列表,其中包含定义RogueLike中我的平铺性质的对象。为此,我决定在读取此变量时使用字典映射要实例化的字符键和类mapRogue 我找到
mapRogue = ['~~~~~~~~~~',
'~~~~.....Y',
'YYYYY+YYYY',
'YYYY....YY']
我想将这个字符串映射转换为2D列表,其中包含定义RogueLike中我的平铺性质的对象。为此,我决定在读取此变量时使用字典映射要实例化的字符键和类mapRogue
我找到了一个使用继承的解决方案,但是imho这段代码并没有我想要的那么优雅,如果我以后想添加其他类型的平铺行为,可能也不是很灵活
使用继承的门类
使用组合的门类
我怀疑还有另外一个答案是使用构图和/或策略模式,所以我尝试用门的行为来装饰瓷砖对象,但我被这句话挡住了
事实上,我尝试了多种解决方案但没有成功,您有什么建议可以帮助我使用优雅的oop和python解决这个概念问题吗
class Tile(object):
#a tile of the map and its properties
def __init__(self, name, char, position, blocked, color=(255, 255, 255), bgcolor=(0, 0, 0), door=None):
self.name = name
self.blocked = blocked
self.char = char
self.color = color
self.bgcolor = bgcolor
self.door = door
self.position = position
# Door decorate the Tile object using composition
class Door(object):
def __init__(self, key, open=False):
self.state = open
self.key = key
def opening(self, key):
if self.key == key:
self.state = True
tilesObject = {".": {"name": 'floor', "obj": Tile, "bgcolor": (233, 207, 177), "block": False},
"Y": {"name": 'forest', "obj": Tile, "bgcolor": (25, 150, 64), "block": True},
"~": {"name": 'water', "obj": Tile, "bgcolor": (10, 21, 35), "block": False},
"+": {"name": 'doors', "obj": Door, "bgcolor": (10, 10, 25), "block": False}}
def load(mymap):
tileMap = []
x, y = (0,0)
for line in mymap:
tileLine = []
for value in line:
try:
tile = tilesObject[value]
except KeyError:
return "Error on key"
# Here i need to detect when obj is Door
# because i need to define a special Tile
# decorated by Door behavior,
# so it seems this is not a good solution :/
x += 1
tileLine.append(obj)
x = 0
y += 1
tileMap.append(tileLine)
return tileMap
更新了一些信息:
感谢您回答@User和@Hyperborreus,您是对的,我在这里简化了我的示例,在我的代码中,我有两层:
- 不移动的
磁贴
- 以及
,它可以移动、攻击、防御和许多其他功能,使用本教程中的游戏对象
进行装饰合成
pygame
,我使用draw\u tile()
函数显示我的所有tile
对象
因此,在这一点上,我需要在Door
和Tile
类之间建立一个链接,以便稍后为玩家正确计算fov,因为Door
具有行为并限制我角色的视觉(属性为blocked或fovState)。在那之后,我画了所有的游戏对象
,在这些已经绘制的平铺
表面之上。门是计算的一部分,只是特定于Tile,以及roguelike中的其他东西,这解释了为什么我像我希望的那样定义门
也许你对游戏定义字典的看法是对的,我需要改变实例化对象的方式,门/瓷砖的oop定义是一样的,但是当我读到初始字符串映射时,它包含item、Door和静态对象,我将gameObject
实例化和Tile
实例化分开
字典在字符串列表中定义的rogueLike映射上实例化元素的思想基于以下思想:
也许这段代码的创造者,@dominic kexel也能在这一点上帮助我们?IMHO,你应该区分“瓷砖”(底层底图)和“物体”(玩家可以与之交互的东西,如打开的门、攻击的龙或杀死的坑)
如果你想将其与3D视频游戏进行比较,“瓷砖”将是你无法与之交互的环境,“对象”将是可点击的东西
仅平铺可以是一个类的实例,并保存与所有平铺相关和通用的信息,如渲染提示(哪个字符使用哪种颜色)或运动方面(可以传递、运动速度等)
然后将对象放置在瓷砖顶部
假设你有一张有很多地板和墙壁的地图,在两个位置有两扇门。所有“瓷砖”的行为都是一样的(你可以在地板上行走,不管是哪一块地砖),但你会把头靠在墙上(不管墙在哪里)。但门是不同的:一扇门需要“绿色钥匙”,另一扇门需要“异见精灵的刺绣钥匙”
这种差异是您的if
问题出现的地方。这扇门需要额外的信息。为了定义贴图,您需要所有分幅(每个类中相同)和放置在特定分幅上的其他对象列表(每个对象不同)
门、井、龙、开关等可以继承自一个公共基类,该基类实现诸如“检查”、“交互”、“攻击”、“吼叫”等标准动作,也可以是用于特殊动作的特殊接口
因此,完整的游戏定义可以如下所示:
game = {'baseMap': '#here comes your 2D array of tiles',
'objects': [ {'class': Door, 'position': (x, y), 'other Door arguments': ...},
{'class': Door, 'position': (x2, y2), 'other Door arguments': ...},
{'class': Dragon, 'position': (x3, y3), 'dragon arguments': ...}, ] }
然后,为了实例化实际对象(OO意义上的对象,而不是游戏意义上的对象),遍历此定义,并使用字典项作为关键字参数(双星号)调用每个对象的命令。这只是许多方法中的一种
对于渲染,如果平铺为空,则显示平铺表示,如果平铺上有对象,则显示对象表示
这就是我用双星号表示的意思:
class Door:
def __init__ (self, position, colour, creaking = True):
print (position, colour, creaking)
objDefs = [...,
{'class': Door, 'kwargs': {'position': (2, 3), 'colour': 'black'} },
...]
#Here you actually iterate over objDefs
objDef = objDefs [1]
obj = objDef ['class'] (**objDef ['kwargs'] )
大编辑:
这是一个如何使用分片和对象实现贴图渲染的想法。(只有我的两分钱):
这就是结果:
我想,你应该区分“瓷砖”(底层底图)和“物体”(玩家可以与之互动的东西,如打开的门、攻击的龙或杀死的坑)
如果你想将其与3D视频游戏进行比较,“瓷砖”将是你无法与之交互的环境,“对象”将是可点击的东西
仅平铺可以是一个类的实例,并保存与所有平铺相关和通用的信息,如渲染提示(哪个字符使用哪种颜色)或运动方面(可以传递、运动速度等)
然后将对象放置在瓷砖顶部
假设你有一张有很多地板和墙壁的地图,在两个位置有两扇门。所有“瓷砖”的行为都是一样的(你可以在地板上行走,不管是哪一块地砖),但你会把头靠在墙上(不管墙在哪里)。但门是不同的:一扇门需要“绿色钥匙”,另一扇门需要“异见精灵的刺绣钥匙”
这种差异在哪里
class Door:
def __init__ (self, position, colour, creaking = True):
print (position, colour, creaking)
objDefs = [...,
{'class': Door, 'kwargs': {'position': (2, 3), 'colour': 'black'} },
...]
#Here you actually iterate over objDefs
objDef = objDefs [1]
obj = objDef ['class'] (**objDef ['kwargs'] )
#! /usr/bin/python3.2
colours = {'white': 7, 'green': 2, 'blue': 4, 'black': 0, 'yellow': 3}
class Tile:
data = {'.': ('Floor', 'white', True),
'Y': ('Forest', 'green', False),
'~': ('Water', 'blue', False) }
def __init__ (self, code, position):
self.code = code
self.position = position
self.name, self.colour, self.passable = Tile.data [code]
def __str__ (self):
return '\x1b[{}m{}'.format (30 + colours [self.colour], self.code)
class GameObject:
#here got he general interfaces common to all game objects
def __str__ (self):
return '\x1b[{}m{}'.format (30 + colours [self.colour], self.code)
class Door (GameObject):
def __init__ (self, code, position, colour, key):
self.code = code
self.position = position
self.colour = colour
self.key = key
def close (self): pass
#door specific interface
class Dragon (GameObject):
def __init__ (self, code, position, colour, stats):
self.code = code
self.position = position
self.colour = colour
self.stats = stats
def bugger (self): pass
#dragon specific interface
class Map:
def __init__ (self, codeMap, objects):
self.tiles = [ [Tile (c, (x, y) ) for x, c in enumerate (line) ] for y, line in enumerate (codeMap) ]
self.objects = {obj ['args'] ['position']: obj ['cls'] (**obj ['args'] ) for obj in objects}
def __str__ (self):
return '\n'.join (
''.join (str (self.objects [ (x, y) ] if (x, y) in self.objects else tile)
for x, tile in enumerate (line) )
for y, line in enumerate (self.tiles)
) + '\n\x1b[0m'
mapRouge = ['~~~~~~~~~~',
'~~~~.....Y',
'YYYYY.YYYY',
'YYYY....YY']
objects = [ {'cls': Door,
'args': {'code': '.', 'position': (5, 2), 'colour': 'black',
'key': 'Ancient Key of Constipation'} },
{'cls': Dragon,
'args': {'code': '@', 'position': (7, 3), 'colour': 'yellow',
'stats': {'ATK': 20, 'DEF': 20} } } ]
theMap = Map (mapRouge, objects)
print (theMap)
kw = tile.copy()
cls = kw.pop('obj')
obj = cls(**kw)
if tile["obj"].__name__ == "Door":
obj = tile["obj"](name=tile["name"], position=(x, y), char=value, blocked=tile["block"],bgcolor=tile["bgcolor"], key="42isTheKey", open=False)
else:
obj = tile["obj"](name=tile["name"], position=(x, y), char=value, blocked=tile["block"],bgcolor=tile["bgcolor"])
class Tile:
def __init__(self):
self.left_tile = None
self.right_tile = None
...
self.content = [FreeSpaceToWalk()]
def can_I_walk_there(self, person):
for content in self.content:
if not content.can_I_walk_there(person): return False
return True
class Door:
password = '42'
def can_I_walk_there(self, person):
return person.what_is_the_password_for(self) == self.password
class FreeSpaceToWalk:
def can_I_walk_there(self, person):
return True
class Wall:
def can_I_walk_there(self, person):
return False