Search code examples
pythonpygamelivewires

How to fix TypeError: unbound method must be called with Dropper instance as first argument


I am writing a game where a rock falls down and you use the mouse to have your chef not get crushed. When the rock falls off the screen, two additional rocks spawn and fall. This continues until your character gets smooshed.

However I got the error:

TypeError: unbound method additonal_drop() must be called with Dropper instance as first argument (got nothing instead)

I'm not sure what I should be putting in the (). Can someone explain this error to me?

In addition, how do I get the Dropper sprite to not be visible?

Here's my code:

from livewires import games, color
import random

games.init(screen_width = 640, screen_height = 480, fps = 50)


class Chef(games.Sprite):

    image = games.load_image("chef.bmp")

    def __init__(self):
        super(Chef, self).__init__(image = Chef.image,
                                  x = games.mouse.x,
                                  bottom = games.screen.height)
    def update(self):
        """ Move to mouse x position. """
        self.x = games.mouse.x

        if self.left < 0:
            self.left = 0

        if self.right > games.screen.width:
            self.right = games.screen.width

        self.check()

    def check(self):
        """ Check if hit by rocks. """
        for rock in self.overlapping_sprites:
            rock.end_game()

class Rock(games.Sprite):
    """
    A rock which falls to the ground.
    """ 
    image = games.load_image("rock.bmp")
    speed = 1

    def __init__(self, x = 320, y = 90):
        """ Initialize a rock object. """
        super(Rock, self).__init__(image = Rock.image,
                                    x = x, y = y,
                                    dy = Rock.speed)

    def end_game(self):
        """ End the game. """
        end_message = games.Message(value = "Game Over",
                                    size = 90,
                                    color = color.red,
                                    x = games.screen.width/2,
                                    y = games.screen.height/2,
                                    lifetime = 2 * games.screen.fps,
                                    after_death = games.screen.quit)
        games.screen.add(end_message)

    def update(self):
        """ Check if bottom edge has reached screen bottom. """
        if self.bottom > games.screen.height:
            self.destroy()
            Dropper.additonal_drop()


class Dropper(games.Sprite):
    """
    A invisible sprite that drops the rocks.
    """
    image = games.load_image("rock.bmp")
    def __init__(self, y = 55, speed = 2, odds_change = 200):
        """ Initialize the dropper object. """
        super(Dropper, self).__init__(image = Dropper.image,
                                      x = games.screen.width / 2, y = y,
                                      dx = speed)

        self.odds_change = odds_change
        self.time_til_drop = 0


    def update(self):
        """ Determine if direction needs to be reversed. """
        if self.left < 0 or self.right > games.screen.width:
            self.dx = -self.dx
        elif random.randrange(self.odds_change) == 0:
           self.dx = -self.dx

    def additonal_drop(self):
        new_rock = Rock(x = self.x)
        games.screen.add(new_rock)

        new_rock = Rock(x = self.x)
        games.screen.add(new_rock)


def main():
    """ Play the game. """
    wall_image = games.load_image("wall.jpg", transparent = False)
    games.screen.background = wall_image

    the_chef = Chef()
    games.screen.add(the_chef)

    the_rock = Rock()
    games.screen.add(the_rock)

    the_dropper = Dropper()
    games.screen.add(the_dropper)

    games.mouse.is_visible = False

    games.screen.event_grab = True
    games.screen.mainloop()

# start it up!
main()

Solution

  • Try defining the Rock class with an additional parameter dropper:

    def __init__(self, x = 320, y = 90, dropper=Dropper()):
    

    dropper will be the Dropper instance. Then create Rock instances from inside Dropper as follows:

    Rock(x=self.x, dropper=self)
    

    This will pass the Dropper instance itself to each Rock instance that the Dropper instance creates. In Rock's __init__(), save a reference to the Dropper instance:

    self.dropper = dropper
    

    Call additional_drop() with:

    self.dropper.additional_drop()