OO and game design questions

Jonathan Hartley tartley at tartley.com
Thu Oct 21 11:49:19 CEST 2010


On Oct 20, 12:11 pm, dex <josipmisko... at gmail.com> wrote:
> On Oct 20, 12:25 pm, Jonathan Hartley <tart... at tartley.com> wrote:
>
>
>
> > On Oct 18, 8:28 am, dex <josipmisko... at gmail.com> wrote:
>
> > > I'm building a turn based RPG game as a hobby. The design is becoming
> > > increasingly complicated and confusing, and I think I may have
> > > tendency to over-engineer simple things. Can anybody please check my
> > > problems-solutions and point me to more elegant solution?
>
> > > Every item/character/room is a separate object. Items/characters need
> > > to have references to room they are in, and room needs to have a list
> > > of references to items/characters that are contained within. I decided
> > > to use weak references. That way I can destroy object by deleting it,
> > > I don't have to destroy all references as well. In each object's
> > > __init__() that object is added to game_object list, and in each
> > > __del__() they are removed from game_object list. This mechanism keeps
> > > them safe from garbage collector. How pythonic is this design?
>
> > > In turn-based games, the order of action execution in battle can give
> > > unfair advantage to players. For example, if player's arm is crippled
> > > before his action is executed, he would do less damage. To offset
> > > this, I first execute all players' actions and calculate effects in
> > > first pass, then apply the effects in second pass. The effect can be
> > > health decrease by 15HP, item pick-up, 30p experience gain, etc. This
> > > means the player deals the same amount of damage no matter what
> > > happens to him in that turn. The difficult part is keeping track of
> > > various effects. I had to make separate class for various types of
> > > effects (ChangeAttributeEffect, GetItemEffect, LooseItemEffect). Each
> > > class stores weak reference to target object and has apply() method
> > > that applies the effect to object. I'm not satisfied with this as it's
> > > limiting, error-prone and uses metaprogramming. Is there a design
> > > pattern that would remember changes to an object, and apply them
> > > later?
>
> > > Sorry for the wall of text.
>
> > One common way to store delayed actions is as a lambda (an anonymous
> > function.) A lambda defines a new function:
>
> > , and you can call this function later. The created function has no
> > name, (but you can assign it to a variable to give it a name if you
> > like) and can be called later:
>
> > So in the game, you could have a collection 'effects', each one will
> > be a lambda:
>
> >   effects = []
>
> > At the start of the round, as each entity makes its moves, they add
> > lambdas to this collection.
>
> >   effects.append(
> >       lambda: decrease_hp(monster_a, 4)
> >   )
> >   effects.append(
> >       lambda: lose_item(monster_a, item_b)
> >   )
>
> > Instead of appending it directly like this, I imagine the lambdas
> > could be returned by the monster's 'act' or 'update' method:
>
> >   class Monster():
> >     def act(self):
> >       # blah and finally
> >       return lambda: decrease_hp(monster_a, 4)
>
> > Then for the start of a round, first you ask each monster what action
> > it is going to perform:
>
> >   for monster in room.monsters:
> >       effects.append(
> >           monster.act()
> >       )
>
> > Then for the end of the round, call all the lambdas
>
> >   for effect in effects:
> >       effect()
>
> Mr. Roy Smith already proposed using closures. I already did a similar
> thing in my code, but instead of decrease_hp() I have AttributeEffect
> class which is able to modify any attribute (in old RPGs some monsters
> could drain your intelligence, in my game laser gun hit will decrease
> HP as well as armor integrity). The first version looks like this
> (missing few checks):
>
> class AttributeEffect(object):
>     '''Effect changes object's attribute by delta'''
>     def __init__(self, obj, attrib, delta):
>         self.obj = obj               # reference to object the effect
> applies to
>         self.attrib = attrib         # name of attribute that effect
> applies to
>         self.delta = delta           # change of value for
> object.attribute
>     def apply(self):
>         value = getattr(self.obj, self.attrib) # todo: try, except
>         value += self.delta
>         setattr(self.obj(), self.attrib, value)
>
> Yesterday I learned that Python 3.0 introduces nonlocal keyword which
> would simplify defining effect functions and passing them along. Nice
> improvement.


Very cool, that looks like it would work. The thing I like about the
lambda idea though, is that you don't have to write any classes like
AttributeEffect, (and presumably other such classes, like
LoseItemEffect, etc.) Instead of *modelling* the different kind of
effects that could happen, you just write code that *performs* the
desired effect. (e.g. monster.hp -= damage) To my eyes, lambda's are
therefore more flexible and require less code. But I could be wrong,
and obviously you should do what you think is best for your
circumstances. Very best of luck with it.

Out of interest, is there anywhere you blog about the game
development, or are likely to make announcements?

  Jonathan Hartley



More information about the Python-list mailing list