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

5
偶尔我会从其他项目中抽出时间来尝试制作一个经典的基于文本的冒险游戏(这次是用Python),作为一个有趣的项目,但我总是在实现物品系统时遇到设计问题。
我希望游戏中的物品都可以从同一个基础的Item类继承下来,并包含一些每个物品都具有的属性,例如伤害和重量。当我尝试为这些物品添加一些功能时,问题就开始了。当物品的伤害超过某个阈值时,应该销毁它。这就是我的问题所在:我真的不知道如何解决这个问题。
由于“del self”由于各种各样的原因都行不通,所以我应该怎么做呢?(还有其他类似的任务)?每个物品是否应该包含对它所在容器(我猜是玩家)的某种引用,并“请求”删除自己?
我想到的第一件事是一个包含游戏中每个物品的大字典,每个对象都将引用此列表,并且都拥有并知道自己唯一的ID。我一点也不喜欢这个解决方案,也不认为这是正确的方法。有人有什么建议吗?
编辑:我看到很多人认为我担心垃圾回收。我所说的不是垃圾回收,而是实际上将对象从游戏中移除。我不确定哪些对象应该启动删除等。
6个回答

2

我建议将对象引用其所有父级。当它应该被销毁时,它将通知其父级。如果您已经使用事件系统,则此方法应与游戏的其他部分很好地集成。

为了避免强制父项在引用被添加或删除时明确通知对象,一个不错的方法是使用某种代理。Python支持属性(properties), 允许像 self.weapon = 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)


1

你正在混淆“摧毁”概念的两个含义。在“游戏玩法”的意义上,该物品应该被摧毁。让垃圾回收器决定何时将其作为对象销毁。

谁拥有该物品的引用?也许玩家在他的库存中拥有它,或者它在游戏中的一个房间里。在任何一种情况下,您的库存或房间对象都知道该物品。告诉它们该物品已经被“游戏玩法”摧毁,并让它们处理。也许现在他们会保留对“损坏”的物品的引用。也许他们会跟踪它,但不会向用户显示它。也许他们会删除所有对它的引用,在这种情况下,内存中的对象很快就会被删除。

面向对象编程的优美之处在于您可以将这些过程抽象出来,远离物品本身:将信息传递给需要知道的人,并让他们以自己的方式实现物品被销毁的含义。


我并不困惑。我理解这两者之间的区别。我使用“del”示例是为了展示什么是_不起作用_的,以及我_不想要_的东西。现在我意识到这只会引起混乱。 - Squid_Tamer

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)

这是一种非常棒的方法,Signal类还有其他用例。 - shmup

0
假设您在使用物品时调用方法,您可以始终返回一个布尔值来指示它是否已损坏。

0

这样怎么样:

from collections import defaultdict

_items = defaultdict(set)
_owner = {}

class CanHaveItems(object):
    @property
    def items(self):
        return iter(_items[self])
    def take(self, item):
        item.change_owner(self)
    def lose(self, item):
        """ local cleanup """

class _nobody(CanHaveItems):
    def __repr__(self):
        return '_nobody'
_nobody = _nobody()

class Destroyed(object):
    def __repr__(self):
        return 'This is an ex-item!'

class Item(object):
    def __new__(cls, *a, **k):
        self = object.__new__(cls)
        _owner[self] = _nobody
        _items[_nobody].add(self)
        self._damage = .0
        return self
    def destroy(self):
        self.change_owner(_nobody)
        self.__class__ = Destroyed
    @property
    def damage(self):
        return self._damage
    @damage.setter
    def damage(self, value):
        self._damage = value
        if self._damage >= 1.:
            self.destroy()
    def change_owner(self, new_owner):
        old_owner = _owner[self]
        old_owner.lose(self)
        _items[old_owner].discard(self)
        _owner[self] = new_owner
        _items[new_owner].add(self)


class Ball(Item):
    def __init__(self, color):
        self.color = color
    def __repr__(self):
        return 'Ball(%s)' % self.color

class Player(CanHaveItems):
    def __init__(self, name):
        self.name = name
    def __repr__(self):
        return 'Player(%s)' % self.name

ball = Ball('red')
ball = Ball('blue')

joe = Player('joe')
jim = Player('jim')

print list(joe.items), ':', list(jim.items)
joe.take(ball)
print list(joe.items), ':', list(jim.items)
jim.take(ball)
print list(joe.items), ':', list(jim.items)

print ball, ':', _owner[ball], ':', list(jim.items)
ball.damage += 2
print ball, ':', _owner[ball], ':', list(jim.items)

print _items, ':', _owner

-1

首先:我没有任何Python编程经验,所以请从更一般的角度去思考。

你的物品(Item)既不应该知道也不关心...你的物品应该有一个接口表示它是可以被销毁的东西。关心可销毁的容器和其他对象可以利用这个接口。

可销毁的接口可以有一些选项来消耗对象以注册回调或事件,在物品被销毁时触发。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接