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 我找到

我用python开发了一个类似RogueLike的程序,我尽量利用OOP和我的一点知识为学生们构建一个python课程

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