Reputation: 45
I'm currently making an RPG style game using the python tkinter libraries. I'm trying to work on battle system however I am running into issues with the damaging system
I'm trying to change the variables given into the function as arguments but of course I can't do that.. I've tried looking at other solutions and they simply wouldn't help due to the fact that tkinter works differently to other code.
Here is my code:
def Attack(EnemyHP,EnMax,GuiEnemyHP,EnemyHPBar,Width):
Dmg = AtkDmg()
EnemyHP = EnemyHP - Dmg
GuiEnemyHP['text'] = Enemy + ": " + str(EnemyHP)+ '/' + str(EnMax)
Loss = (Width / EnMax) * Dmg
Width = EnemyBar_Width - Loss
EnemyBar.place(x=110,y=0,width=Width,height=20)
Upvotes: 0
Views: 94
Reputation: 973
As far as I know objects are passed over as references so you can modify them inside the function. As others pointed out, even though the pythonic way of passing an object as argument is not via reference, it makes it possible to alter the object's attributes from inside the function.
Another way to go around this limitation if you don't want to create objects is to pass over lists as they are mutable - you can replace an item of the list inside the function and then the caller will see the new modified list element instead of the old when the function returns. (Not a nice solution though.)
Upvotes: 0
Reputation: 54283
Your code will become a huge mess if you continue that way.
Python is an object-oriented language, so you probably should use classes and instances to describe the characters and interactions between them.
Here's a very basic implementation of Characters:
class Character:
def __init__(self, name, hp_max):
self.name = name
self.xp = 0
self.hp_max = hp_max
self.hp = hp_max
# TODO: define hp_bar here
def is_dead(self):
return self.hp <= 0
def attack(self, opponent, damage):
opponent.hp -= damage
self.xp += damage
def __str__(self):
return '%s (%d/%d)' % (self.name, self.hp, self.hp_max)
hero = Character('Mario', 1000)
enemy = Character('Goomba', 100)
print(enemy)
# Goomba (100/100)
hero.attack(enemy, 50)
print(enemy)
# Goomba (50/100)
hero.attack(enemy, 50)
print(enemy)
# Goomba (0/100)
print(enemy.is_dead())
# True
print(hero.xp)
# 100
Upvotes: 2
Reputation: 77912
As you already noticed, rebinding arguments within a function won't work here as you expected, since argument names are locals to the function. This leaves you with two solutions:
The functional way: return the new/updated values from your function:
def foo(arg1, arg2, argN):
arg1 += 42
arg2 = arg1 + argN
argN -= arg2
return arg1, arg2, argN
arg1, arg2, argN = foo(arg1, arg2, argN)
The OO way: pass a mutable object and mutate it:
class Arg(object):
def __init__(self, arg1, arg2, argN):
self.arg1 = arg1
self.arg2 = arg2
self.argN = argN
def foo(arg):
arg.arg1 += 42
arg.arg2 = arg.arg1 + arg.argN
arg.argN -= arg.arg2
arg = Arg(x, y, z)
foo(arg)
# now arg.arg1, arg.arg2 and arg.argN have been updated
# - but NOT x, y and z which are untouched
Note that in this second example, a better design would be to have a method on Arg
doing the effective update and either directly use this method or (if needed) call it from foo()
.
As a side note: I see quite a few unrelated things happening in your Attack
function - at least one part which is domain related (computing damages and updating EnemyHP
) and another part that is presentation related (updating GuiEnemyHP
and EnemyBar
). Those two parts would be better handled in two different layers of your app (resp. the model layer - which should not know anything about presentation - and the UI layer).
Upvotes: 0