Search code examples
pythonpygame

Why does a Python class need to rely on a function defined outside of its scope?


I came across the following code that draws a health bar above a sprite

import pygame
import math

def draw_health_bar(surf, pos, size, borderC, backC, healthC, progress):
    pygame.draw.rect(surf, backC, (*pos, *size))
    pygame.draw.rect(surf, borderC, (*pos, *size), 1)
    innerPos  = (pos[0]+1, pos[1]+1)
    innerSize = ((size[0]-2) * progress, size[1]-2)
    rect = (round(innerPos[0]), round(innerPos[1]), round(innerSize[0]), round(innerSize[1]))
    pygame.draw.rect(surf, healthC, rect)
    
class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.original_image = pygame.image.load('CarBlue64.png')
        self.original_image = pygame.transform.rotate(self.original_image, 90)
        self.image = self.original_image
        self.rect = self.image.get_rect(center=(x, y))
        self.direction = pygame.math.Vector2((0, -1))
        self.velocity = 5
        self.position = pygame.math.Vector2(x, y)
        self.health = 10

...

    def draw_health(self, surf):
        health_rect = pygame.Rect(0, 0, self.original_image.get_width(), 7)
        health_rect.midbottom = self.rect.centerx, self.rect.top
        max_health = 10
        draw_health_bar(surf, health_rect.topleft, health_rect.size, 
                (0, 0, 0), (255, 0, 0), (0, 255, 0), self.health/max_health) 

Can someone please help me understand the following questions:

  1. Why was the function draw_health_bar declared outside of the class Player?
  2. Can I move the function draw_health_bar inside the class Player, then define the function draw_health inside the Player's child class? For example:
class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)

...
    def draw_health_bar(surf, pos, size, borderC, backC, healthC, progress):
        pygame.draw.rect(surf, backC, (*pos, *size))
        pygame.draw.rect(surf, borderC, (*pos, *size), 1)
        innerPos  = (pos[0]+1, pos[1]+1)
        innerSize = ((size[0]-2) * progress, size[1]-2)
        rect = (round(innerPos[0]), round(innerPos[1]), round(innerSize[0]), round(innerSize[1]))
        pygame.draw.rect(surf, healthC, rect)

...

class A(Player):
    def __init__(self, x, y):
        super().__init__(x, y)

...

    def draw_health(self, surf):
        health_rect = pygame.Rect(0, 0, self.original_image.get_width(), 7)
        health_rect.midbottom = self.rect.centerx, self.rect.top
        max_health = 10
        super().draw_health_bar(surf, health_rect.topleft, health_rect.size, 
                (0, 0, 0), (255, 0, 0), (0, 255, 0), self.health/max_health) 

Solution

  • Why was the function draw_health_bar declared outside of the class Player?

    Because it doesn't do anything that's Player-specific. It doesn't refer to any instance variables of the Player object, or call any Player methods.

    Can I move the function draw_health_bar inside the class Player, then define the function draw_health inside the Player's child class?

    You could. But if you do this, you should add the @staticmethod decorator, since it doesn't have a self parameter. And then you should call it using Player.draw_health_bar(...) syntax -- the class hierarchy doesn't establish a scope for names.