在面向对象游戏中管理物品

5 投票
6 回答
1962 浏览
提问于 2025-04-16 19:26

我时不时会停下手头的其他项目,尝试用Python做一个经典的文字冒险游戏,作为一个有趣的项目,但我在设计物品系统时总是遇到问题。

我希望游戏里的物品都能从一个基础的 Item 类派生出来,这个类包含每个物品都有的一些属性,比如伤害和重量。我的问题在于,当我想给这些物品添加一些功能时就开始了。当一个物品的伤害超过某个阈值时,它应该被销毁。这就是我遇到的问题:我不知道该怎么实现这个功能。

因为 del self 由于各种原因是行不通的,(编辑:我故意提到使用 'del' 是错误的。我知道什么是垃圾回收,也知道这不是我想要的。) 那我该怎么做呢(还有其他类似的任务)?每个物品是否应该包含一个指向它容器的引用(我想是玩家)并“请求”自己被删除呢?

我首先想到的是一个大字典,里面包含游戏中的每个物品,每个物品对象都有一个对这个列表的引用,并且都有自己独特的ID。我一点都不喜欢这个方案,我觉得这根本不是正确的做法。有没有人有其他建议?

编辑:我看到很多人以为我在担心垃圾回收。其实我说的不是垃圾回收,而是真正从游戏中移除这个物体。我不确定应该由哪些物体来发起移除等问题。

6 个回答

1

一种选择是使用信号系统

首先,我们有一个可以重复使用的类,它让你可以定义一个信号

class Signal(object):
    def __init__(self):
         self._handlers = []

    def connect(self, handler):
         self._handlers.append(handler)

    def fire(self, *args):
         for handler in self._handlers:
             handler(*args)

你的物品类使用这个信号来创建一个“被销毁”的信号,其他类可以监听这个信号。

 class Item(object):
    def __init__(self):
        self.destroyed = Signal()

    def destroy(self):
        self.destroyed.fire(self)

而库存则监听来自物品的信号,并相应地更新它的内部状态

 class Inventory(object):
     def __init__(self):
         self._items = []

     def add(self, item):
         item.destroyed.connect(self.on_destroyed)
         self._items.add(item)

     def on_destroyed(self, item):
         self._items.remove(item)
1

你把“销毁”这个概念搞混了。这里说的销毁是指在“游戏玩法”上,物品应该被销毁。至于什么时候作为一个对象被真正删除,那就让垃圾回收器来处理吧。

谁在引用这个物品呢?可能是玩家把它放在了自己的背包里,或者它在游戏中的某个房间里。不管是哪种情况,你的背包或者房间对象都知道这个物品的存在。你只需要告诉它们这个物品已经被“销毁”(在游戏玩法上),然后让它们来处理这个事情。也许它们会保留一个“损坏”物品的引用,或者可能会跟踪这个物品,但不再向用户展示。也有可能它们会删除对这个物品的所有引用,这样内存中的对象很快就会被删除。

面向对象编程的美妙之处在于,你可以把这些过程抽象出来,不用让物品自己处理:把消息传递给需要知道的人,让他们以自己的方式来实现物品被销毁的含义。

2

我建议让你的对象保存所有父对象的引用。这样,当它需要被销毁时,就可以通知它的父对象。如果你已经在使用事件系统,这样的做法会和游戏的其他部分很好地结合在一起。

为了避免让父对象每次添加或删除引用时都必须明确通知子对象,可以使用某种代理。Python支持properties,这可以让像self.weapon = Weapon()这样的代码,实际上把设置武器属性的任务交给一个用户定义的函数来处理。

下面是一个使用属性的示例代码:

class Weapon(object):
    def __init__(self, name):
        self.name = name
        self.parent = None
    def destroy(self):
        if self.parent:
            self.parent.weaponDestroyed()

def WeaponRef():
    def getWeapon(self):
        return self._weapon
    def setWeapon(self, newWeapon):
        if newWeapon == None: #ensure that this is a valid weapon
            delWeapon(self)
            return
        if hasattr(self, "weapon"): #remove old weapon's reference to us
            self._weapon.parent = None
        self._weapon = newWeapon
        newWeapon.parent = self
    def delWeapon(self):
        if hasattr(self, "weapon"):
            self._weapon.parent = None
            del self._weapon
    return property(getWeapon, setWeapon, delWeapon)

class Parent(object):
    weapon = WeaponRef()
    def __init__(self, name, weapon=None):
        self.name = name
        self.weapon = weapon
    def weaponDestroyed(self):
        print "%s deleting reference to %s" %(self.name, self.weapon.name)
        del self.weapon


w1 = Weapon("weapon 1")
w2 = Weapon("weapon 2")
w3 = Weapon("weapon 3")
p1 = Parent("parent 1", w1)
p2 = Parent("parent 2")

w1.destroy()

p2.weapon = w2
w2.destroy()

p2.weapon = w3
w3.destroy()

如果你在做一个物品管理系统,玩家可以拥有多个武器,并且任何一个武器都可以随时被销毁,那么你需要自己写一个集合类。
在这种情况下,记住x[2]会调用x.__getitem__(2)x[2] = 5会调用x.__setitem__(2, 5),而del x[2]会调用x.__delitem__(2)

撰写回答