BetterNot
BetterNot

Reputation: 117

How to put a health bar over the sprite in pygame

By the title, I'm hoping that my the player would have a health bar attached to their head. If they move, the health bar also moves. Say sprite is my player. Hey sprite! He has a health bar on top of his head and yeah thats it. To be honest, I don't really know where to start, so help would be appreciated. Thanks!

P.S. A big thanks to Rabbid76 for his help! Also to Ann Zen! Code:

import pygame
import os
import random
import math
import winsound
# winsound.PlaySound("explosion.wav", winsound.SND_ALIAS)

os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0, 30)
wn = pygame.display.set_mode((1920, 1020))
clock = pygame.time.Clock()
icon = pygame.image.load('Icon.png')
pygame.image.load('Sprite0.png')
pygame.image.load('Sprite0.png')
pygame.display.set_icon(icon)
pygame.display.set_caption('DeMass.io')
vel = 5

class Player(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        z = random.randint(0, 101)
        if z <= 51:
            self.original_image = pygame.image.load('Sprite0.png')
        else:
            self.original_image = pygame.image.load('Sprite3.png')
        if z == 41:
            self.original_image = pygame.image.load('Sprite5.png')
        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.x = pygame.math.Vector2(x)
        self.y = pygame.math.Vector2(y)
        self.health = 10
        self.visible = True


    def point_at(self, x, y):
        self.direction = pygame.math.Vector2(x, y) - self.rect.center
        if self.direction.length() > 0:
            self.direction = self.direction.normalize()
        angle = self.direction.angle_to((0, -1))
        self.image = pygame.transform.rotate(self.original_image, angle)
        self.rect = self.image.get_rect(center=self.rect.center)

    def move(self, x, y):
        self.position -= self.direction * y * self.velocity
        self.position += pygame.math.Vector2(-self.direction.y, self.direction.x) * x * self.velocity
        self.rect.center = round(self.position.x), round(self.position.y)


    def reflect(self, NV):
        self.direction = self.direction.reflect(pygame.math.Vector2(NV))

    def update(self):
        self.position += self.direction * self.velocity
        self.rect.center = round(self.position.x), round(self.position.y)

    def hit(self, player):
        if self.health > 0:
            self.health -= 1
        else:
            self.visible = False
        distance = math.sqrt(math.pow(player.x - player.x(), 2) + math.pow(player.y - player.y(), 2))
        if distance < 20:
            return True

        else:
            return False



    def move(self, x, y, clamp_rect):
        self.position -= self.direction * y * self.velocity
        self.position += pygame.math.Vector2(-self.direction.y, self.direction.x) * x * self.velocity
        self.rect.center = round(self.position.x), round(self.position.y)

        if self.rect.left < clamp_rect.left:
            self.rect.left = clamp_rect.left
            self.position.x = self.rect.centerx
        if self.rect.right > clamp_rect.right:
            self.rect.right = clamp_rect.right
            self.position.x = self.rect.centerx
        if self.rect.top < clamp_rect.top:
            self.rect.top = clamp_rect.top
            self.position.y = self.rect.centery
        if self.rect.bottom > clamp_rect.bottom:
            self.rect.bottom = clamp_rect.bottom
            self.position.y = self.rect.centery

    class Projectile(object):
        def __init__(self, x, y, radius, color, facing):
            self.x = x
            self.y = y
            self.radius = radius
            self.color = color
            self.facing = facing
            self.vel = 8 * facing

        def draw(self, win):
            pygame.draw.circle(win, self.color, (self.x, self.y), self.radius)






player = Player(200, 200)
all_sprites = pygame.sprite.Group(player)


run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.MOUSEMOTION:
            player.point_at(*event.pos)

    pygame.draw.rect(wn, (0, 0, 0), (50, 50, 10000, 100000))



    keys = pygame.key.get_pressed()
    if keys[pygame.K_w] or keys[pygame.K_UP]:
        player.move(0, -1, wn.get_rect())


    wn.fill((255, 255, 255))
    all_sprites.draw(wn)
    pygame.display.update()

Upvotes: 3

Views: 2491

Answers (2)

Rabbid76
Rabbid76

Reputation: 210978

See haw to draw a bar in How can I display a smooth loading bar in pygame?. Create a function that draws a health bar:

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)

Add a method draw_health to the class Player and use the function draw_health_bar:

class Player(pygame.sprite.Sprite):
    # [...]

    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)    

Call the method in the main application loop:

while run:
    # [...]

    wn.fill((255, 255, 255))
    all_sprites.draw(wn)
    player.draw_health(wn)    # <---
    pygame.display.update()

See also Sprite.


Minimal example:

repl.it/@Rabbid76/PyGame-HealthBar

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 point_at(self, x, y):
        self.direction = pygame.math.Vector2(x, y) - self.rect.center
        if self.direction.length() > 0:
            self.direction = self.direction.normalize()
        angle = self.direction.angle_to((0, -1))
        self.image = pygame.transform.rotate(self.original_image, angle)
        self.rect = self.image.get_rect(center=self.rect.center)

    def move(self, x, y, clamp_rect):
        self.position -= self.direction * y * self.velocity
        self.position += pygame.math.Vector2(-self.direction.y, self.direction.x) * x * self.velocity
        self.rect.center = round(self.position.x), round(self.position.y)

        test_rect = self.rect.clamp(clamp_rect)
        if test_rect.x != self.rect.x:
            self.rect.x = test_rect.x
            self.position.x = self.rect.centerx
            self.health = max(0, self.health - 1)
        if test_rect.y != self.rect.y:
            self.rect.y = test_rect.y
            self.position.y = self.rect.centery
            self.health = max(0, self.health - 1)

    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)    

window = pygame.display.set_mode((250, 250))
clock = pygame.time.Clock()

player = Player(200, 200)
all_sprites = pygame.sprite.Group(player)

run = True
while run:
    clock.tick(60)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.MOUSEMOTION:
            player.point_at(*event.pos)

    keys = pygame.key.get_pressed()
    mouse_buttons = pygame.mouse.get_pressed()
    if any(keys) or any(mouse_buttons):
        player.move(0, -1, window.get_rect())

    window.fill((127, 127, 127))
    pygame.draw.rect(window, (255, 0, 0), window.get_rect(), 3)
    all_sprites.draw(window)
    player.draw_health(window)
    pygame.display.update()

pygame.quit()
exit()

Upvotes: 2

Matthew Ogilvie
Matthew Ogilvie

Reputation: 31

Rabbid's answer worked for me, however I am using Python 3.8.2 and continually got deprication errors as well as compilation errors when trying to output an .exe. The progress variable is a float and results in this error:

    pygame.draw.rect(surf, healthC, (*innerPos, *innerSize))
DeprecationWarning: an integer is required (got type float).  Implicit conversion to integers using __int__ is deprecated, and may be removed in a future version of Python.

Multiplication by the variable progress in the function draw_health_bar needs to be raised to an int to get away from that. It doesn't seem to affect the visual appearance of the health bar at all. This is the culprit line updated with the int.

innerSize = (int((size[0]-2) * progress), size[1]-2)

Full updated code below. I prefer complete names for things so as not to confuse myself what variables mean, so I've done that, but everything else is the same except for the int I added.

def draw_health_bar(surface, position, size, color_border, color_background, color_health, progress):
     pygame.draw.rect(surface, color_background, (*position, *size))
     pygame.draw.rect(surface, color_border, (*position, *size), 1)
     innerPos  = (position[0]+1, position[1]+1)
     innerSize = (int((size[0]-2) * progress), size[1]-2)
     pygame.draw.rect(surface, color_health, (*innerPos, *innerSize))

Sorry if this posting this breaks StackOverflow's rules. First time posting and I thought this was helpful info to newbs like me who try this code out.

Upvotes: 3

Related Questions