Search code examples
pythonooppython-class

how do I use variables from functions in other functions without putting the variable in the __init__ method of a class?


I am new level coder, and I making a game that has different kinds of attacks, and I want to be able to combo into attacks seemlessly, but I cannot figure out how to make a delay between different attacks. So far I have tried to use to the same value that is the attack delay so that if the delay is reset, you can use a different attack, but it is not letting me do that because it doesn't know what the variable "now" is because I need "now" needs to be specifically in that function. Here is the code:

class Player(pg.sprite.Sprite):
    def __init__(self, game):
        self.game = game
        pg.sprite.Sprite.__init__(self)
        self.image = pg.Surface((30, 35))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)
        self.pos = vec(WIDTH / 2, HEIGHT / 2)
        self.vel = vec(0, 0)
        self.acc = vec(0, 0)
        self.attack_counter = 0
        self.dt = 0.2

        # this is all the attack delay stuff
        self.last_attack = 100
        self.last_attack_n_light_right = 700
        self.last_attack_s_light_right = 425
        self.last_attack_d_light_right = 700
        self.last_attack_s_air_right = 425

        self.last_attack_n_light_left = 700
        self.last_attack_s_light_left = 425
        self.last_attack_d_light_left = 700
        self.last_attack_n_air = 425
        self.last_attack_s_air_left = 425
        self.last_attack_d_air = 425

        self.attack_delay = pg.time.get_ticks()
        self.attack_delay_n_light_right = pg.time.get_ticks()
        self.attack_delay_s_light_right = pg.time.get_ticks()
        self.attack_delay_d_light_right = pg.time.get_ticks()
        self.attack_delay_s_air_right = pg.time.get_ticks()

        self.attack_delay_n_light_left = pg.time.get_ticks()
        self.attack_delay_s_light_left = pg.time.get_ticks()
        self.attack_delay_d_light_left = pg.time.get_ticks()
        self.attack_delay_n_air = pg.time.get_ticks()
        self.attack_delay_s_air_left = pg.time.get_ticks()
        self.attack_delay_d_air = pg.time.get_ticks()

        self.move_delay = pg.time.get_ticks()

    def jump(self):
        # jump if jump key is pressed, and triple jumps are recharged when touching platforms
        global player_jump_count
        self.rect.x += 1
        self.rect.x -= 1
        player_jump_count -= 1
        self.vel.y = -PLAYER_JUMP
        hits = pg.sprite.spritecollide(self, self.game.platforms, False)
        if hits:
            player_jump_count = 3
        if player_jump_count < 1:
            self.vel.y = PLAYER_GRAV

    def s_light_right(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        # print(now, "  ", self.attack_delay_s_light_right)
        if self.s_light_left.now - self.attack_delay_s_light_left > self.last_attack_s_light_left:
            if keys[pg.K_d]:
                if now - self.attack_delay_s_light_right > self.last_attack_s_light_right:     # this is where the problem is
                    self.attack_delay_s_light_right = now
                    bullet = Bullet(self.rect.centerx + 20, self.rect.centery - 10, self.vel.x + 10, 0, 20, 20, self.dt)
                    self.game.all_sprites.add(bullet)
                    self.game.bullets.add(bullet)

    def s_light_left(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_a]:
            if now - self.attack_delay_s_light_left > self.last_attack_s_light_left:
                self.attack_delay_s_light_left = now
                bullet = Bullet(self.rect.centerx - 20, self.rect.centery - 10, self.vel.x - 10, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)

    def n_light_right(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if not keys[pg.K_a] and not keys[pg.K_d] and not keys[pg.K_s]:
            if now - self.attack_delay_n_light_right > self.last_attack_n_light_right:
                self.attack_delay_n_light_right = now
                bullet = Bullet(self.rect.centerx + 40, self.rect.centery - 40, 0, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)

    def n_light_left(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if not keys[pg.K_a] and not keys[pg.K_d] and not keys[pg.K_s]:
            if now - self.attack_delay_n_light_left > self.last_attack_n_light_left:
                self.attack_delay_n_light_left = now
                bullet = Bullet(self.rect.centerx - 60, self.rect.centery - 40, 0, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)

    def d_light_right(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_s]:
            if not keys[pg.K_a] and not keys[pg.K_d]:
                if now - self.attack_delay_d_light_right > self.last_attack_d_light_right:
                    self.attack_delay_d_light_right = now
                    bullet = Bullet(self.rect.centerx + 40, self.rect.centery + 3.5, 0, 0, 30, 15, self.dt)
                    self.game.all_sprites.add(bullet)
                    self.game.bullets.add(bullet)

    def d_light_left(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_s]:
            if not keys[pg.K_a] and not keys[pg.K_d]:
                if now - self.attack_delay_d_light_left > self.last_attack_d_light_left:
                    self.attack_delay_d_light_left = now
                    bullet = Bullet(self.rect.centerx - 60, self.rect.centery + 3.5, 0, 0, 30, 15, self.dt)
                    self.game.all_sprites.add(bullet)
                    self.game.bullets.add(bullet)

    def s_air_right(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_d]:
            if now - self.attack_delay_s_air_right > self.last_attack_s_air_right:
                self.attack_delay_s_air_right = now
                bullet = Bullet(self.rect.centerx + 20, self.rect.centery - 10, self.vel.x + 10, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)

    def s_air_left(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_a]:
            if now - self.attack_delay_s_air_left > self.last_attack_s_air_left:
                self.attack_delay_s_air_left = now
                bullet = Bullet(self.rect.centerx - 20, self.rect.centery - 10, self.vel.x - 10, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)

    def n_air(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if not keys[pg.K_a] and not keys[pg.K_d] and not keys[pg.K_s]:
            if now - self.attack_delay_n_air > self.last_attack_n_air:
                self.attack_delay_n_air = now
                bullet = Bullet(self.rect.centerx + 5, self.rect.centery - 60, 0, 0, 20, 20, self.dt)
                bullet2 = Bullet(self.rect.centerx - 25, self.rect.centery - 60, 0, 0, 20, 20, self.dt)
                self.game.all_sprites.add(bullet)
                self.game.bullets.add(bullet)
                self.game.all_sprites.add(bullet2)
                self.game.bullets.add(bullet2)

    def d_air(self):
        keys = pg.key.get_pressed()
        now = pg.time.get_ticks()
        if keys[pg.K_s]:
            if not keys[pg.K_a] and not keys[pg.K_d]:
                if now - self.attack_delay_d_air > self.last_attack_d_air:
                    self.attack_delay_d_air = now
                    bullet = Bullet(self.rect.centerx + 5, self.rect.centery + 60, 0, 12, 20, 20, self.dt)
                    bullet2 = Bullet(self.rect.centerx - 25, self.rect.centery + 60, 0, 12, 20, 20, self.dt)
                    self.game.all_sprites.add(bullet)
                    self.game.bullets.add(bullet)
                    self.game.all_sprites.add(bullet2)
                    self.game.bullets.add(bullet2)

    def update(self):
        self.acc = vec(0, PLAYER_GRAV)
        keys = pg.key.get_pressed()
        if keys[pg.K_a]:
            self.acc.x = -PLAYER_ACC
        if keys[pg.K_d]:
            self.acc.x = PLAYER_ACC
        # apply friction
        self.acc.x += self.vel.x * PLAYER_FRICTION
        # equations of motion
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc

        self.rect.midbottom = self.pos

Solution

  • It sounds like what you want is a object level variable: one that is common to and instance of the class and shared by all the member methods of that instance. That can be declared and used like this:

    class foo:
        def __init__(self, val):
            self.now = val
        def double_it(self):
            return self.now * 2
    
    >>> f = foo(10)
    >>> f.double_it()
    20
    




    Edit to add:
    It's not really clear from the question why you'd need this, but you do specify that the variable should not be included in __init__(). If that is the case, it is possible to create instance attributes "on the fly" although it is usually not very good practice and leads to some rather unreadable code:

    class bar:
        def double_it(self, val):
            self.now = val * 2
    
    >>> b = bar()
    >>> b.now  <----- we expect this to fail; nothing has set `b.val` yet.
    
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'bar' object has no attribute 'now'
    
    >>> b.double_it(10)
    >>> b.now
    20
    

    There is really no good reason for doing that though, and most linters will flag it (for example, pylint):

    :attribute-defined-outside-init (W0201): *Attribute %r defined outside __init__*
      Used when an instance attribute is defined outside the __init__ method. This
      message belongs to the classes checker.
    

    It would be better to set the value to None in the constructor and then change it later.