0

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)
4
  • 2
    You can return the variables and assign them back to the originals outside your function Commented Sep 25, 2017 at 11:42
  • 2
    What do you mean exactly by " tkinter works differently to other code" ? Commented Sep 25, 2017 at 11:48
  • 2
    Have you ever heard about OOP? Commented Sep 25, 2017 at 11:58
  • @CoryKramer I would try to use the return functionality however I'm not sure how to work around the fact that the function is run through the button like so. Fight['command'] =lambda: Attack(EnemyHP,EnMax,GuiEnemyHP,EnemyBar,EnemyBar_Width) Commented Sep 28, 2017 at 4:18

3 Answers 3

2

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
Sign up to request clarification or add additional context in comments.

Comments

0

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).

Comments

0

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.)

3 Comments

I assume you meant objects not classes... And actually nothing is "passed by reference" (nor "passed by value") in Python, everything is passed the same way: local names (the argument names) bound to objects. The name is local so rebinding it has no effect outside the function, but the object passed is really the original object (not a copy) so mutating it does indeed change the object (as long as it's a mutable object of course). All this is clearly explained here : nedbatchelder.com/text/names.html
@brunodesthuilliers: fixed the class part. Thanks for the link - i think in one sentence the 'reference' vs. 'value' concept explains it better than your sentence, even though it does not fully describe reality. :( I was focusing on the list idea.
the problem is that "pass by reference" and "pass by value" have a well defined meaning that just doesn't apply to what Python "variables" really are (name=>object bindings), and which is very different from C/C++ type languages concept of "variable" (the combination of a memory address holding the data and some type informations). In languages that have "pass by reference", what is passed is actually a reference to the variable itself and assigning to it will write at the variable's memory address. Python's argument passing just does not work this way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.