I am making a spaceship game where you control a spaceship and fire bullets at the enemies. I am now trying to make a game over background show when the player and any enemy collide, but when I ran the game, the game over background never showed!
This is my current code (Some parts omitted or replaced by --snip--):
class Spaceship(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("bullet.png")
self.image = pygame.transform.scale(self.image, (100, 100))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.y -= 5
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
class Enemy(pygame.sprite.Sprite):
def __init__(self, s, x, y, t):
pygame.sprite.Sprite.__init__(self)
self.type = t
self.screen, self.x, self.y = s, x, y
self.image = pygame.image.load("enemy.png")
self.image = pygame.transform.scale(self.image, (235, 215))
self.rect = self.image.get_rect()
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
self.score_given = get_enemy_given_score()[self.type]
def update(self):
if self.y < 0:
self.kill()
self.y += 3
self.rect.center = (self.x, self.y)
class GameOverBackground(pygame.sprite.Sprite):
def __init__(self, s, x, y, size=(100, 100)):
pygame.sprite.Sprite.__init__(self)
self.screen, self.x, self.y = s, x, y
self.size = size
self.image = pygame.image.load("Game_Over.jpg")
self.image = pygame.transform.scale(self.image, self.size)
self.rect = self.image.get_rect()
def blitme(self):
self.screen.blit(self.image, self.rect)
bg = GameOverBackground(screen, 0, 0)
spaceship = Spaceship(screen, 400, 400)
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
clock = pygame.time.Clock()
enemy_interval = 2000 # It's in milliseconds
enemy_event = pygame.USEREVENT + 1
pygame.time.set_timer(enemy_event, enemy_interval)
score = 0
font = pygame.font.SysFont("Arial", 30)
textsurface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
spaceship_collided = False
running = True
while running:
--snip--
screen.fill((255, 255, 255)) # DO NOT DRAW ANYTHING IN FRONT OF THIS LINE, I'M WARNING YOU
bullets.update()
key = pygame.key.get_pressed()
amount = 5
if key[pygame.K_a]:
spaceship.x -= amount
--snip--
spaceship.update()
if not spaceship_collided:
screen.blit(spaceship.image, spaceship.rect)
if spaceship_collided is False:
bullets.draw(screen)
enemies.draw(screen)
for i in enemies:
i.update()
if pygame.sprite.spritecollide(i, bullets, True):
score += i.score_given
i.kill()
if score >= 99999:
score = 99999
textsurface = font.render("Score: {:,}".format(score), True, (0, 0, 0))
screen.blit(textsurface, (590, 0))
if pygame.sprite.spritecollide(spaceship, enemies, dokill=True):
spaceship_collided = True
bg.blitme()
pygame.display.update()
clock.tick(60)
Can anybody help me?
The condition pygame.sprite.spritecollide(spaceship, enemies, dokill=True): os only fulfilled in a single frame. If you want to permanently display the game over screen, you need to draw it depending on the spaceship_collided :
running = True
while running:
# [...]
if pygame.sprite.spritecollide(spaceship, enemies, dokill=True):
spaceship_collided = True
if spaceship_collided:
bg.blitme()
pygame.display.update()
clock.tick(60)
Related
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()
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()
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.
I have tried searching for many solutions to do this but I cant seem to understand how to spawn the enemy block multiple times. In my program , the enemy block will only appear once. The game is incomplete, so please ignore empty functions.
I have created the enemy object using sprites. I want to create the program in such a way that , once the enemy object leaves the screen , another object(same size and colour) will appear.
import pygame as pg
from settings import *
from sprites import *
from os import path
class Game(object):
"""docstring for Game"""
def __init__(self):
# initialise game
global points , game_speed
pg.init()
self.screen = pg.display.set_mode((s_HEIGHT , s_WIDTH))
pg.display.set_caption(TITLE)
self.icon = pg.image.load("C:/Users/DELL/Documents/Jump Cube/Img/cube.png")
pg.display.set_icon(self.icon)
self.clock = pg.time.Clock()
self.running = True
self.game_speed = 14
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
self.load_data()
def load_data(self):
self.dir = path.dirname(__file__)
img_dir = path.join(self.dir, "Img")
#load spritesheet image
self.spritesheet = Spritesheet(path.join(img_dir, SPRITESHEET))
def new(self):
# Stars a new game
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.ene = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
pl = Platform(0, s_HEIGHT - 230, 800, 40)
en = enemies(50, 50)
self.all_sprites.add(pl)
self.platforms.add(pl)
self.all_sprites.add(en)
self.ene.add(en)
self.Run()
def Run(self):
# Game Loop
self.playing = True
while self.playing:
self.clock.tick(FPS)
self.Events()
self.Update()
self.Draw()
self.score()
def Update(self):
# Game Loop - Update
self.all_sprites.update()
hits = pg.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.vel.y = 0
pass
def score(self):
global points , game_speed
points += 1
if points % 100 == 0:
game_speed += 1
text = font.render("Points : " + str(points), True, WHITE)
textrec = text.get_rect()
textrec.center = (700, 50)
self.screen.blit(text, textrec)
def Events(self):
# Game Loop - Events
for event in pg.event.get():
if event.type == pg.QUIT:
if self.playing:
self.playing = False
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_SPACE:
self.player.jump()
def Draw(self):
# Game Loop - Draw
self.screen.fill(BLACK)
self.score()
self.all_sprites.draw(self.screen)
pg.display.update()
pass
def start_screen(self):
# shows the start screen
pass
def end_screen(self):
# shows the end screen
pass
g = Game()
g.start_screen()
while g.running:
g.new()
g.end_screen()
pg.quit()
sprites.py
import pygame as pg
from settings import *
vec = pg.math.Vector2
class Spritesheet():
# utility class for laoding and parsing spritesheets
def __init__(self, filename):
self.spritesheet = pg.image.load(filename).convert()
def get_image(self, x, y, width, height):
# grabs images from large spritesheets
image = pg.Surface((width, height))
image.blit(self.spritesheet, (0,0), (x, y, width, height))
image = pg.transform.scale(image , (50, 85))
return image
class Player(pg.sprite.Sprite):
def __init__(self, game):
pg.sprite.Sprite.__init__(self)
self.game = game
self.image = self.game.spritesheet.get_image(614, 1063, 120, 191)
self.image.set_colorkey(BLACK1)
self.rect = self.image.get_rect()
self.rect.center = (s_WIDTH / 2, s_HEIGHT / 2)
self.pos = vec(100, 600)
self.vel = (0, 0)
self.acc = (0, 0)
def jump(self):
#jump only if standing on plat
self.rect.y += 1
hits = pg.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = -8
def update(self):
self.acc = vec(0, PLAYER_GRAV)
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
class Platform(pg.sprite.Sprite):
def __init__(self, x, y, w, h):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w, h))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
class enemies(pg.sprite.Sprite):
def __init__(self, w1, h1):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w1, h1))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = 700
self.rect.y = 517
def update(self):
self.rect.x -= game_speed
settings.py
import pygame
pygame.init()
#game options
TITLE = "Ninja Jump"
s_WIDTH = 600
s_HEIGHT = 800
FPS = 60
game_speed = 14
points = 0
font = pygame.font.Font('freesansbold.ttf', 20)
SPRITESHEET = "spritesheet_jumper.png"
#player properties
PLAYER_ACC = 0.5
PLAYER_GRAV = 0.3
#colors
WHITE = (199, 198, 196)
BLACK = (23, 23, 23)
GRAY = (121, 121, 120)
GREEN = (72, 161, 77)
BLACK1 = (0,0,0)
GRAY1 = (162, 162, 162)
First of enemies should be name Enemy. The class represents 1 single enemy. The coordinates of the enemy are arguments of the constructor. The en pygame.sprite.Group object is used to manage multiple enemies:
class Enemies(pg.sprite.Sprite):
def __init__(self, x, y, w1, h1):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((w1, h1))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
def update(self):
self.rect.x -= game_speed
class Game(object):
# [...]
self.all_sprites = pg.sprite.Group()
self.platforms = pg.sprite.Group()
self.ene = pg.sprite.Group()
self.player = Player(self)
self.all_sprites.add(self.player)
pl = Platform(0, s_HEIGHT - 230, 800, 40)
en1 = Enemy(700, 517, 50, 50)
en2 = Enemy(600, 517, 50, 50)
en3 = Enemy(500, 517, 50, 50)
self.all_sprites.add(pl)
self.platforms.add(pl)
self.all_sprites.add([en1, en2, en3])
self.ene.add([en1, en2, en3])
self.Run()
I am making a pygame game where you control a spaceship and fire bullets to hit the enemies. As of right now, I am trying to make an enemy appear on the screen. Not making it move yet. However, when I ran my following code, Nothing but the spaceship appeared. The spaceship also was able to move and fire bullets.
This is my current code:
import pygame
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((800, 500))
screen.fill((255, 255, 255))
class Spaceship(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/spaceship.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.rect.center = (self.x, self.y)
class Bullet(pygame.sprite.Sprite):
def __init__(self, s, x, y):
pygame.sprite.Sprite.__init__(self)
self.screen = s
self.x, self.y = x, y
self.image = pygame.image.load("C:/eqodqfe/bullet.png")
self.image = pygame.transform.scale(self.image, (100, 100))
self.rect = self.image.get_rect()
self.rect.center = (self.x, self.y)
def update(self):
self.y -= 1
self.rect.center = (self.x, self.y)
if self.y < 0:
self.kill()
class Enemy(Spaceship):
def __init__(self, s, x, y):
Spaceship.__init__(self, s, x, y)
self.image = pygame.image.load("C:/eqodqfe/enemy.png")
self.image = pygame.transform.scale(self.image, (175, 175))
self.rect = self.image.get_rect()
spaceship = Spaceship(screen, 400, 400)
enemy = Enemy(screen, 100, 100)
bullets = pygame.sprite.Group()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == MOUSEBUTTONDOWN:
bullet = Bullet(screen, spaceship.x, spaceship.y - 20)
bullets.add(bullet)
bullets.update()
key = pygame.key.get_pressed()
if key[pygame.K_a]:
spaceship.x -= 0.5
elif key[pygame.K_d]:
spaceship.x += 0.5
elif key[pygame.K_w]:
spaceship.y -= 0.5
elif key[pygame.K_s]:
spaceship.y += 0.5
spaceship.update()
screen.blit(enemy.image, enemy.rect)
enemy.update()
screen.fill((255, 255, 255))
screen.blit(spaceship.image, spaceship.rect)
bullets.draw(screen)
pygame.display.update()
What is wrong?
You have to draw the enemy after drawing the background. If you draw the enemy before the background, the background will hide the enemy:
running = True
while running:
# [...]
# screen.blit(enemy.image, enemy.rect) <-- DELETE
enemy.update()
screen.fill((255, 255, 255))
screen.blit(enemy.image, enemy.rect) # <-- INSERT
screen.blit(spaceship.image, spaceship.rect)
bullets.draw(screen)
pygame.display.update()
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()
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()
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.
After about of hour of disaster and modification on my code, I can't seem to get my enemy sprite to move back and forth on a platform. So any help would be greatly appreciated :D
Test File, Crafted Super Fast....
import pygame, sys, os
GAME_TITLE = "GAME"
WINDOW_WIDTH, WINDOW_HEIGHT = 1280, 720
BLACK = (0, 0, 0)
BLUE = (0, 255, 0)
RED = (255, 0, 0)
FPS = 60
ENEMY_ACC = 0.3
ENEMY_FRICTION = -0.12
ENEMY_GRAVITY = 0.5
vec = pygame.math.Vector2
class Platform(pygame.sprite.Sprite):
def __init__(self, game, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.platform_list
self.image = pygame.Surface((1280, 100))
self.rect = self.image.get_rect()
pygame.draw.rect(self.image, RED, (x, y, w, h))
class Enemy(pygame.sprite.Sprite):
def __init__(self, game, pos):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.enemy_list
self.image = pygame.Surface((100, 100))
pygame.draw.rect(self.image, BLUE, (200, 200, 100, 100))
self.rect = self.image.get_rect()
self.pos = vec(pos)
self.rect.center = self.pos
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.direction = "R"
self.engage = False
def update(self):
hits = pygame.sprite.spritecollide(self, self.game.platform_list, False)
if hits:
plat_right = hits[0].rect.right
plat_left = hits[0].rect.left
self.pos.y = hits[0].rect.top
self.vel.y = 0
if self.direction == "R" and not self.engage:
if self.rect.right >= plat_right:
self.direction = "L"
self.acc.x = -ENEMY_ACC
if self.direction == "L" and not self.engage:
if self.rect.left <= plat_left:
self.direction = "R"
self.acc.x = -ENEMY_ACC
self.acc.x += self.vel.x * ENEMY_FRICTION
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
self.rect.midbottom = self.pos
self.acc = vec(0, ENEMY_GRAVITY)
class Game:
def __init__(self):
pygame.init()
pygame.mixer.init()
pygame.font.init()
os.environ['SDL_VIDEO_CENTERED'] = '1'
pygame.display.set_caption(GAME_TITLE)
self.window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
self.clock = pygame.time.Clock()
self.platform_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
def run(self):
enemy = Enemy(self, (200, 500))
self.enemy_list.add(enemy)
platform = Platform(self, 0, 620, 1280, 100)
self.platform_list.add(platform)
while True:
self.events()
self.update()
self.draw()
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def update(self):
self.enemy_list.update()
def draw(self):
self.window.fill(BLACK)
self.platform_list.draw(self.window)
self.enemy_list.draw(self.window)
pygame.display.flip()
def main():
g = Game()
g.run()
if __name__ == "__main__":
main()
Here's an example without the acc attribute. Just accelerate the self.vel by adding the ENEMY_GRAVITY, update the self.pos and self.rect, then if it collides with the platform see if the right or left edge is reached and then just invert the velocity.
I've set the ENEMY_GRAVITY to 3 and call clock.tick(30) to limit the frame rate. The direction attribute doesn't seem to be necessary anymore.
class Enemy(pygame.sprite.Sprite):
def __init__(self, game, pos):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.group = self.game.enemy_list
self.image = pygame.Surface((60, 60))
self.image.fill((30, 90, 200))
self.rect = self.image.get_rect(midbottom=pos)
self.pos = vec(pos)
self.vel = vec(3, 0)
self.engage = False
def update(self):
self.vel.y += ENEMY_GRAVITY
self.pos += self.vel
self.rect.midbottom = self.pos
hits = pygame.sprite.spritecollide(self, self.game.platform_list, False)
for plat in hits:
self.pos.y = plat.rect.top
self.rect.bottom = plat.rect.top
self.vel.y = 0
if self.vel.x > 0 and not self.engage:
if self.rect.right >= plat.rect.right:
self.vel = vec(-3, 0) # Reverse the horizontal velocity.
elif self.vel.x < 0 and not self.engage:
if self.rect.left <= plat.rect.left:
self.vel = vec(3, 0) # Reverse the horizontal velocity.