Управление предметами в объектно-ориентированной игре - PullRequest
5 голосов
/ 13 июня 2011

Время от времени мне нравится отдыхать от других моих проектов, пытаясь сделать классическую приключенческую текстовую игру (на этот раз на Python) забавным проектом, но у меня всегда есть проблемы с дизайном, реализующие система предметов.

Я бы хотел, чтобы предметы в игре происходили из одного базового Item класса, содержащего некоторые атрибуты, которые есть у каждого предмета, такие как урон и вес. Мои проблемы начинаются, когда я пытаюсь добавить некоторые функции к этим элементам. Когда урон предмета превышает порог, он должен быть уничтожен. И в этом заключается моя проблема: я действительно не знаю, как этого добиться.

Поскольку del self не будет работать по миллиону различных причин, (Правка: я намеренно предоставляю использование del как нечто, что, как я знаю, неправильно. Я знаю, что такое сборка мусора и как это не то, что я хочу.) как мне это сделать (и другие подобные задачи)? Должен ли каждый элемент содержать какую-либо ссылку на его контейнер (игрок, я думаю) и «попросить» удалить себя?

Первое, что приходит на ум, - это большой словарь, содержащий каждый элемент в игре, и каждый объект будет иметь ссылку на этот список, и оба будут иметь и знать свой собственный уникальный идентификатор. Мне вообще не нравится это решение, и я не думаю, что это правильный путь. У кого-нибудь есть предложения?

РЕДАКТИРОВАТЬ: Я вижу много людей, которые думают, что я беспокоюсь о сборке мусора. Я имею в виду не сборку мусора, а удаление объекта из игрового процесса. Я не уверен, какие объекты должны инициировать удаление и т. Д.

Ответы [ 6 ]

2 голосов
/ 13 июня 2011

Я хотел бы, чтобы ваш объект оставил ссылку на всех своих родителей.Затем, когда он должен быть уничтожен, он уведомит своих родителей.Если вы уже используете систему событий, она должна хорошо интегрироваться с остальной частью игры.

Хороший способ избежать принуждения вашего родителя явно уведомлять объект при каждом удалении или добавлении ссылки - это использоватькакой-то прокси.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()

Теперь, если вы делаете какую-то систему инвентаря, где у игрока может быть более 1 оружия, и любое из них может быть уничтожено в любое время, тогда вам придется написать своюсобственный класс коллекции.
Для чего-то подобного, просто имейте в виду, что x[2] звонит x.__getitem__(2), x[2] = 5 звонит x.__setitem__(2, 5) и del x[2] звонит x.__delitem__(2)

1 голос
/ 09 октября 2011

Одним из вариантов будет использование системы сигналов

Во-первых, у нас есть класс многократного использования, который позволяет определять сигнал

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 голос
/ 13 июня 2011

Вы объединяете два значения "разрушающей" идеи.Предмет должен быть уничтожен в игровом смысле.Пусть сборщик мусора беспокоится о том, когда уничтожить его как объект.

У кого есть ссылка на Предмет?Возможно, у игрока это есть в инвентаре или в комнате в игре.В любом случае ваши объекты инвентаря или комнаты знают об этом предмете.Скажите им, что Предмет был уничтожен (в игровом смысле), и дайте им справиться с этим.Возможно, теперь они сохранят ссылку на «сломанный» предмет.Возможно, они будут отслеживать это, но не отображать это пользователю.Возможно, они удалят все ссылки на него, и в этом случае объект в памяти скоро будет удален.

Прелесть объектно-ориентированного программирования в том, что вы можете абстрагировать эти процессы от самого Предмета: передатьсообщения тому, кому нужно это знать, и позволить им по-своему реализовать, что означает уничтожение Предмета.

0 голосов
/ 13 июня 2011

Как насчет:

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
0 голосов
/ 13 июня 2011

сначала: у меня нет опыта работы с питоном, поэтому подумайте об этом более широко

ваш предмет не должен знать или заботиться ... у вашего предмета должен быть интерфейс, который говорит, что эточто-то разрушаемое.контейнеры и другие объекты, которые заботятся о вещах, которые могут быть уничтожены, могут использовать этот интерфейс

, чтобы разрушаемый интерфейс мог иметь некоторую опцию для потребления объектами для регистрации обратного вызова или события, срабатывающего при уничтожении элемента

0 голосов
/ 13 июня 2011

Предполагая, что вы вызываете метод, когда элемент используется, вы всегда можете вернуть логическое значение, указывающее, сломан ли он.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...