Collision between masks in pygame - python

I have a problem with collisions in my game, spaceship's mask doesn't collide properly with a background and I believe that offset is a problem, however I'm not sure.
I've tried multiple collision techniques and checked a lot of answers to my problem, but none of them helped me.
import pygame as pg
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (2, 29)
pg.init()
def gameLoop():
display_width = 1800
display_height = 1000
pg.display.set_caption("Kerbal Space Landing Simulator")
clock = pg.time.Clock()
display = pg.display.set_mode((display_width, display_height))
sprsp = pg.image.load('C:/Users/PC/PycharmProjects/untitled/sprspaceship.png').convert_alpha()
cosbg = pg.image.load('C:/Users/PC/PycharmProjects/untitled/cosmos bg.png').convert_alpha()
done = False
class Spaceship:
def __init__(self, x, y, mass):
self.x = x
self.y = y
self.width = 139
self.height = 106
self.velx = 0
self.vely = 0
self.mass = mass
self.color = (255, 255, 255)
self.spr = sprsp
self.fuel = 500
self.mask = pg.mask.from_surface(self.spr)
self.angle = 0
self.changerot = 0
def check_controls(self):
if keys[pg.K_SPACE] and self.fuel > 0:
if self.angle > 0:
self.vely += 0.005 * (self.angle - 90)
self.velx += -0.005 * self.angle
else:
self.vely += -0.005 * (self.angle + 90)
self.velx += -0.005 * self.angle
self.fuel += -3
if keys[pg.K_LEFT] and self.angle < 90:
self.angle += 2
if keys[pg.K_RIGHT] and self.angle > -90:
self.angle += -2
def update_pos(self):
self.vely += 0.01
self.x += self.velx
self.y += self.vely
self.mask = pg.mask.from_surface(self.spr)
def update_rotation(self):
self.rspr = pg.transform.rotate(self.spr, self.angle)
self.changerot -= self.angle
def draw(self):
if self.fuel > 0:
pg.draw.rect(display, (255, 255, 255), (display_width - 100, 100 + 500 - self.fuel, 10, self.fuel), 0)
display.blit(self.rspr, (int(self.x), int(self.y)))
self.changerot = 0
class Terrain(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.mask = pg.mask.from_threshold(display, (160, 160, 160))
self.hitbox = pg.Rect(self.x, self.y, display_width, 500)
self.ox = display_width // 2 - self.x // 2
self.oy = display_height // 2 - self.y // 2
def draw(self):
pg.draw.rect(display, (160, 160, 160), (self.x, self.y, display_width, 500), 0)
spaceship = (Spaceship(500, 100, 1))
terrain = (Terrain(0, 800))
def redrawGameWindow():
display.blit(cosbg, (0, 0))
spaceship.draw()
terrain.draw()
pg.display.update()
def check_for_collisions():
offset = (int(spaceship.x - terrain.ox), int(spaceship.y - terrain.oy))
print(offset)
print(spaceship.mask.overlap(terrain.mask, offset))
return spaceship.mask.overlap(terrain.mask, offset)
# return spaceship.hitbox.colliderect(terrain.hitbox)
# return pg.sprite.spritecollide(spaceship.spr, terrain.mask, False, pg.sprite.collide_mask)
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
mouse_pressed = pg.mouse.get_pressed()
x, y = pg.mouse.get_pos()
spaceship.check_controls()
spaceship.update_pos()
spaceship.update_rotation()
if check_for_collisions() is not None:
print('Hit! You\'ve hit the ground with the speed:', spaceship.vely)
exit()
redrawGameWindow()
clock.tick(60)
gameLoop()
Spaceship doesn't collide with the surface. I know how to fix it using simpler code, but in future I want to use randomly generated terrain. Could you help me with these collisions?

The mask for the Terrain is never set. Crate a proper Terrain mask:
class Terrain(object):
def __init__(self, x, y):
self.x = x
self.y = y
maskSurf = pg.Surface((display_width, display_height)).convert_alpha()
maskSurf.fill(0)
pg.draw.rect(maskSurf, (160, 160, 160), (self.x, self.y, display_width, 500), 0)
self.mask = pg.mask.from_surface(maskSurf)
print(self.mask.count())
# [...]
When using pygame.mask.Mask.overlap(), then you've to check the overlapping of the Spaceship and the Terrain, rather than the Terrain and the Spaceship.
Since the Terrain mask is a mask of the entire screen, the offset for the overlap() test is the position of the Spaceship:
def check_for_collisions():
offset = (int(spaceship.x), int(spaceship.y))
collide = terrain.mask.overlap(spaceship.mask, offset)
print(offset, collide)
return collide
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask

Related

Creating HealthBar using pygame [duplicate]

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.

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()
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.

How can I make objects permanently stick in window without being refreshed in Pygame?

I am trying to recreate Atari Breakout using Pygame. I met with a problem, I put all three rows of the tiles in three lists and I want to print them and make them stay at the location before the ball hits them.
This is the code:
import pygame
import random
pygame.init()
screenWidth = 1200
screenHeight = 700
window = pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption('Atari Breakout')
pygame.mouse.set_pos(-500,650)
class Plate():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 5
def draw_plate(self):
pygame.mouse.set_visible(True)
pos = pygame.mouse.get_pos()
self.x = pos[0]-100
pygame.draw.rect(window, (00,00,255), (self.x, self.y ,self.width, self.height))
class Circle():
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
self.vel_x = 5
self.vel_y = 5
class Tiles():
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
def draw(self):
if self.color == 'red':
pygame.draw.rect(window, (255,0,0), (self.x, self.y, self.width, self.height))
elif self.color == 'green':
pygame.draw.rect(window, (44,176,55), (self.x, self.y, self.width, self.height))
elif self.color == 'blue':
pygame.draw.rect(window, (0,191,255), (self.x, self.y, self.width, self.height))
pygame.display.update()
def draw_titles():
first_row = []
second_row = []
third_row = []
preset_width1 = [70, 120, 200, 30, 240, 140, 130, 120, 80] # nine elements
preset_width2 = [70, 120, 200, 30, 240, 140, 130, 120, 80]
preset_width3 = [70, 120, 200, 30, 240, 140, 130, 120, 80]
random.shuffle(preset_width1)
random.shuffle(preset_width2)
random.shuffle(preset_width3)
#print(f'preset_width1 is: {preset_width1}')
put_width1 = []
put_width2 = []
put_width3 = []
for t in range(1,10):
if t==1:
width = preset_width1.pop(0)
put_width1.append(width)
#print(f'put_width1 is: {put_width1}')
if t==1:
x = 0 + 5
else:
add = sum(put_width1)
#print(f'add is: {add}')
x = t*5 + add
#print(f'x is: {x}')
if t>1:
width = preset_width1.pop(0)
put_width1.append(width)
#print(f'put_width1 is: {put_width1}')
y = 125
height = 35
first_row.append(Tiles(x,y,width,height,'red'))
if t == 9:
break
for t in range(1,10):
if t==1:
width = preset_width2.pop(0)
put_width2.append(width)
if t==1:
x = 0 + 5
else:
add = sum(put_width2)
x = t*5 + add
if t>1:
width = preset_width2.pop(0)
put_width2.append(width)
y = 170
height = 35
second_row.append(Tiles(x,y,width,height,'green'))
if t == 9:
break
for t in range(1,10):
if t==1:
width = preset_width3.pop(0)
put_width3.append(width)
if t==1:
x = 0 + 5
else:
add = sum(put_width3)
x = t*5 + add
if t>1:
width = preset_width3.pop(0)
put_width3.append(width)
y = 215
height = 35
third_row.append(Tiles(x,y,width,height,'blue'))
if t == 9:
break
for num in range(0,9):
first_row[num].draw()
for num in range(0,9):
second_row[num].draw()
for num in range(0,9):
third_row[num].draw()
keys = pygame.key.get_pressed()
if keys[pygame.K_BACKSPACE]:
run = False
# main loop
plate = Plate(10,650,200,40)
ball = Circle(600,300,10)
run = True
start = False
bounds = pygame.Rect(0, 0, 1200, 700)
while run:
pygame.time.Clock().tick(120)
for event in pygame.event.get():
if event == pygame.QUIT:
run = False
plate.draw_plate()
keys = pygame.key.get_pressed()
# bounce algorithem
if keys[pygame.K_SPACE]:
start = True
if start:
ball.y -= ball.vel_y
ball.x += ball.vel_x
if ball.x - ball.radius < bounds.left or ball.x + ball.radius > bounds.right:
ball.vel_x *= -1
if ball.y - ball.radius < bounds.top or ball.y + ball.radius > bounds.bottom:
ball.vel_y *= -1
pygame.draw.rect(window, (0, 0, 0), bounds, 1)
pygame.draw.circle(window, (44,176,55), (ball.x, ball.y), ball.radius)
#pygame.display.update()
draw_titles()
# close call
if keys[pygame.K_BACKSPACE]:
run = False
break
window.fill((0,0,0))
pygame.display.update()
pygame.quit()
This is the ideal situation:
But instead, it refreshes like crazy. I know the problem is I put the draw_titles() function inside of the main While loop. But I believe is the way I code the draw_tiles() function that made it not work. If I place draw_titles() before the loop, the tiles will appear and instantly disappear and both the ball and the plate will not display.
I did some research online and I see tutorial for text and images. For images, they use .blit() but I believe it is only for images.
I had tried many variations to fix this but to no avail. Please help.
Thank you.
Here's a quick fix:
import pygame
import random
pygame.init()
screenWidth = 1200
screenHeight = 700
window = pygame.display.set_mode((screenWidth,screenHeight))
pygame.display.set_caption('Atari Breakout')
pygame.mouse.set_pos(-500,650)
class Plate():
def __init__(self, x, y, width, height):
self.x = x
self.y = y
self.width = width
self.height = height
self.vel = 5
def draw_plate(self):
pygame.mouse.set_visible(True)
pos = pygame.mouse.get_pos()
self.x = pos[0]-100
pygame.draw.rect(window, (00,00,255), (self.x, self.y ,self.width, self.height))
class Circle():
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
self.vel_x = 5
self.vel_y = 5
class Tiles():
def __init__(self, x, y, width, height, color):
self.x = x
self.y = y
self.width = width
self.height = height
self.color = color
def draw(self):
if self.color == 'red':
pygame.draw.rect(window, (255,0,0), (self.x, self.y, self.width, self.height))
elif self.color == 'green':
pygame.draw.rect(window, (44,176,55), (self.x, self.y, self.width, self.height))
elif self.color == 'blue':
pygame.draw.rect(window, (0,191,255), (self.x, self.y, self.width, self.height))
first_row = []
second_row = []
third_row = []
def create_titles():
preset_width1 = [70, 120, 200, 30, 240, 140, 130, 120, 80] # nine elements
preset_width2 = [70, 120, 200, 30, 240, 140, 130, 120, 80]
preset_width3 = [70, 120, 200, 30, 240, 140, 130, 120, 80]
random.shuffle(preset_width1)
random.shuffle(preset_width2)
random.shuffle(preset_width3)
#print(f'preset_width1 is: {preset_width1}')
put_width1 = []
put_width2 = []
put_width3 = []
for t in range(1,10):
if t==1:
width = preset_width1.pop(0)
put_width1.append(width)
#print(f'put_width1 is: {put_width1}')
if t==1:
x = 0 + 5
else:
add = sum(put_width1)
#print(f'add is: {add}')
x = t*5 + add
#print(f'x is: {x}')
if t>1:
width = preset_width1.pop(0)
put_width1.append(width)
#print(f'put_width1 is: {put_width1}')
y = 125
height = 35
first_row.append(Tiles(x,y,width,height,'red'))
if t == 9:
break
for t in range(1,10):
if t==1:
width = preset_width2.pop(0)
put_width2.append(width)
if t==1:
x = 0 + 5
else:
add = sum(put_width2)
x = t*5 + add
if t>1:
width = preset_width2.pop(0)
put_width2.append(width)
y = 170
height = 35
second_row.append(Tiles(x,y,width,height,'green'))
if t == 9:
break
for t in range(1,10):
if t==1:
width = preset_width3.pop(0)
put_width3.append(width)
if t==1:
x = 0 + 5
else:
add = sum(put_width3)
x = t*5 + add
if t>1:
width = preset_width3.pop(0)
put_width3.append(width)
y = 215
height = 35
third_row.append(Tiles(x,y,width,height,'blue'))
if t == 9:
break
# main loop
plate = Plate(10,650,200,40)
ball = Circle(600,300,10)
run = True
start = False
bounds = pygame.Rect(0, 0, 1200, 700)
create_titles()
while run:
pygame.time.Clock().tick(120)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
# bounce algorithem
if keys[pygame.K_SPACE]:
start = True
# close call
if keys[pygame.K_BACKSPACE]:
run = False
break
if start:
ball.y -= ball.vel_y
ball.x += ball.vel_x
if ball.x - ball.radius < bounds.left or ball.x + ball.radius > bounds.right:
ball.vel_x *= -1
if ball.y - ball.radius < bounds.top or ball.y + ball.radius > bounds.bottom:
ball.vel_y *= -1
window.fill((0,0,0))
plate.draw_plate()
pygame.draw.rect(window, (0, 0, 0), bounds, 1)
pygame.draw.circle(window, (44,176,55), (ball.x, ball.y), ball.radius)
for tile in first_row:
tile.draw()
for tile in second_row:
tile.draw()
for tile in third_row:
tile.draw()
pygame.display.update()
pygame.quit()
When drawing to the screen, clear the screen surface first, then draw all objects, like this:
...
window.fill((0,0,0))
plate.draw_plate()
pygame.draw.rect(window, (0, 0, 0), bounds, 1)
pygame.draw.circle(window, (44,176,55), (ball.x, ball.y), ball.radius)
for tile in first_row:
tile.draw()
for tile in second_row:
tile.draw()
for tile in third_row:
tile.draw()
...
Note how all drawing related stuff is in one place. This way, it's clearer and less confusing.
You should make sure to only call pygame.display.flip() (or .update()) only once per frame (as Rabbid76 already said in a comment).
I also moved the lists outside the draw_titles function, which I renamed to create_titles. The function creates the tiles, so it should only be done once, not every frame.
You should also probably look into pygame's Sprite and Group class. Here's an example I hacked together which makes use of some pygame features:
import pygame
import random
class Paddle(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, bounds, *grps):
super().__init__(*grps)
self.image = pygame.Surface((width, height))
self.image.fill((0,0,255))
self.rect = self.image.get_rect(topleft=(x, y))
self.bounds = bounds
def update(self, dt):
pos = pygame.mouse.get_pos()
self.rect.centerx = pos[0]
self.rect.clamp_ip(self.bounds)
class Circle(pygame.sprite.Sprite):
def __init__(self, x, y, radius, bounds, *grps):
super().__init__(*grps)
self.image = pygame.Surface((radius, radius))
self.image.set_colorkey((1, 2, 3))
self.image.fill((1, 2, 3))
self.rect = self.image.get_rect(topleft=(x, y))
pygame.draw.circle(self.image, (44,176,55), (radius//2, radius//2), 5)
self.vel = pygame.Vector2((5, 5))
self.pos = self.rect.center
self.bounds = bounds
def update(self, dt):
self.pos += self.vel * min(dt/15, 10)
self.rect.center = self.pos
if self.rect.left < self.bounds.left or self.rect.right > self.bounds.right:
self.vel.x *= -1
if self.rect.top < self.bounds.top or self.rect.bottom > self.bounds.bottom:
self.vel.y *= -1
self.rect.clamp_ip(self.bounds)
def bounce(self, sprite):
if self.rect.top <= sprite.rect.top or sprite.rect.bottom >= sprite.rect.bottom:
self.vel.y *= -1
elif self.rect.left <= sprite.rect.left or sprite.rect.right >= sprite.rect.right:
self.vel.x *= -1
class Tiles(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color, *grps):
super().__init__(*grps)
self.image = pygame.Surface((width, height))
self.image.fill(color)
self.rect = self.image.get_rect(topleft=(x, y))
def hit(self):
self.kill()
def main():
pygame.init()
screen = pygame.display.set_mode((1200, 700))
screen_rect = screen.get_rect()
pygame.display.set_caption('Atari Breakout')
sprites = pygame.sprite.Group()
tiles = pygame.sprite.Group()
paddle = Paddle(10,650,200,40, screen_rect, sprites)
ball = Circle(600,300,10, screen_rect, sprites)
preset = [70, 120, 200, 30, 240, 140, 130, 120, 80]
y = 215
for color in ['blue', 'green', 'red']:
x = 5
line = preset[:]
random.shuffle(line)
for width in line:
Tiles(x, y, width, 35, pygame.Color(color), sprites, tiles)
x += width + 5
y -= 45
dt = 0
clock = pygame.time.Clock()
while True:
pygame.time.Clock().tick(120)
# events
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_BACKSPACE:
return
# game logic
tile = pygame.sprite.spritecollideany(ball, tiles)
if tile:
tile.hit()
ball.bounce(tile)
if pygame.sprite.collide_rect(paddle, ball):
ball.bounce(paddle)
sprites.update(dt)
# drawing
screen.fill((0,0,0))
sprites.draw(screen)
pygame.draw.rect(screen, (0, 0, 0), screen_rect, 1)
pygame.display.update()
dt = clock.tick(120)
pygame.quit()
if __name__ == '__main__':
main()

Using mask for collision detection pygame [duplicate]

This question already has an answer here:
Pygame collision with masks
(1 answer)
Closed 2 years ago.
I am trying to make a game where your supposed to land the player on a surface. I have named this surface as 'lander'. Here is my player class:
class player:
def __init__(self, x, y, height, width):
self.ufo = pygame.image.load(r"C:\Users\rahul\OneDrive\Documents\A level python codes\lander\ufo.png")
self.ufo = pygame.transform.scale(self.ufo, (height, width))
self.height = height
self.width = width
self.x = x
self.y = y
self.speed = 0
self.score = 0
def draw(self):
g.screen.blit(self.ufo, (self.x, self.y))
def fly_up(self):
self.speed = 5
p.y = p.y -4
self.y -= self.speed
def fly_right(self):
self.speed = 5
p.y = p.y -4
self.x += self.speed
def fly_left(self):
self.speed = 5
p.y = p.y -4
self.x -= self.speed
Here is my lander class:
class lander:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.color = (192, 192, 192)
def draw(self):
self.lander = pygame.draw.rect(g.screen, self.color, (self.x, self.y, self.width, self.height))
def move(self):
self.speed = 4
self.x -= self.speed
def lander_off_screen(self):
if self.x == 0 - p.height - 50:
self.x = 1200 + p.height + 50
self.y = random.randint(230, 590)
I want to detect if they are colliding using masks. However I am having a couple of problems.
Firstly I dont know how to convert self.lander to alpha. It gave me an error when I tried.
Even when I used an actual image instead of a rectange as lander, it didnt work like I
I wanted it to.
Thanks
My code so far for reference is here:
import pygame
import os
import random
import time
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()
start = time.time()
def main():
class game:
def __init__(self, *args):
self.win = pygame.display
self.SH = 600
self.SW = 1200
self.screen = self.win.set_mode((self.SW, self.SH))
self.win.set_caption("UFO Lander")
self.white = (255, 255, 255)
self.red = (255, 0, 0)
self.green = (0, 255, 0)
self.transparent = (0, 0, 0, 0)
self.background = pygame.image.load(r"C:\Users\rahul\OneDrive\Documents\A level python codes\lander\background.png")
self.background = pygame.transform.scale(self.background, (self.SW, self.SH))
self.transparent = (0, 0, 0, 0)
clock = pygame.time.Clock()
self.FPS = 30
self.FPS = clock.tick(self.FPS)
self.gravity = 4
self.time_passed = pygame.time.get_ticks()/1000
def write(self, text, color, x , y, size):
font = pygame.font.SysFont("Aerial", size)
text = font.render(text, True, color)
textRect = text.get_rect()
self.screen.blit(text,(x , y))
def player_off_screen(self):
if p.x < 0:
p.x = 0
g.write("YOU CRASHED!!", self.red, 50, 250, 200)
g.win.update()
pygame.time.delay(1000)
main()
if p.x + p.height > 1200:
p.x = 1200 - p.height
g.write("YOU CRASHED!!", self.red, 50, 250, 200)
g.win.update()
pygame.time.delay(1000)
main()
if p.y < 0:
p.y = 0
g.write("YOU CRASHED!!", self.red, 50, 250, 200)
g.win.update()
pygame.time.delay(1000)
main()
if p.y + p.width > 600:
p.y = 600 - p.width
g.write("YOU CRASHED!!", self.red, 50, 250, 200)
g.win.update()
pygame.time.delay(1000)
main()
class thrust:
def __init__(self, x, y):
self.thrust = pygame.image.load(r"C:\Users\rahul\OneDrive\Documents\A level python codes\lander\thrust.png")
self.thrust = pygame.transform.scale(self.thrust, (100, 150))
self.x = x
self.y = y
def draw(self):
g.screen.blit(self.thrust, (self.x, self.y))
def update_x(self, variable):
self.x = p.x + variable
def update_y(self, variable):
self.y = p.y + variable
class player:
def __init__(self, x, y, height, width):
self.ufo = pygame.image.load(r"C:\Users\rahul\OneDrive\Documents\A level python codes\lander\ufo.png")
self.ufo = pygame.transform.scale(self.ufo, (height, width))
self.height = height
self.width = width
self.x = x
self.y = y
self.speed = 0
self.score = 0
def draw(self):
g.screen.blit(self.ufo, (self.x, self.y))
def fly_up(self):
self.speed = 5
p.y = p.y -4
self.y -= self.speed
def fly_right(self):
self.speed = 5
p.y = p.y -4
self.x += self.speed
def fly_left(self):
self.speed = 5
p.y = p.y -4
self.x -= self.speed
p = player(100, 100, 350, 150)
t = thrust(p.x, p.y)
t2 = thrust(p.x, p.y)
class obstacles:pass
class lander:
def __init__(self, x, y, height, width):
self.x = x
self.y = y
self.height = height
self.width = width
self.color = (192, 192, 192)
def draw(self):
self.lander = pygame.draw.rect(g.screen, self.color, (self.x, self.y, self.width, self.height))
def move(self):
self.speed = 4
self.x -= self.speed
def lander_off_screen(self):
if self.x == 0 - p.height - 50:
self.x = 1200 + p.height + 50
self.y = random.randint(230, 590)
class fuel:
def __init__(self):
self.x = 100
self.y = 60
self.height = 30
self.width = 300
def draw(self):
pygame.draw.rect(g.screen, g.white, (self.x, self.y, self.width, self.height))
def less_fuel(self):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT or (pygame.K_UP) or (pygame.K_LEFT):
self.change = 0.7
self.width -= self.change
def run_out(self):
if self.width < 0:
g.write("FUEL EMPTY!!", g.red, 50, 250, 200)
g.win.update()
pygame.time.delay(1000)
main()
f = fuel()
class menu: pass
g = game()
run = True
t = thrust(p.x, p.y)
l = lander(1200 - p.height - 50, random.randint(230, 590), 20, p.height+ 50)
while run:
g.screen.blit(g.background, (0, 0))
l.draw()
l.move()
g.write("s", g.white, 350, 10, 50)
g.write("Fuel: ", g.white ,10, 60, 50)
g.write("Score: %d" %(p.score), g.white, 1000, 10, 50)
p.draw()
p.y += g.gravity
g.player_off_screen()
l.lander_off_screen()
t.update_x(50)
t.update_y(110)
t2.update_x(200)
t2.update_y(110)
f.draw()
f.run_out()
try:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
f.less_fuel()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RIGHT:
p.fly_right()
if event.key == pygame.K_LEFT:
p.fly_left()
if event.key == pygame.K_UP:
t.draw()
t2.draw()
p.fly_up()
end = time.time()
duration = end - start
g.write("Total game time: %d" %(duration), g.white ,10, 10, 50)
except NameError:
pass
pygame.display.flip()
if __name__ == "__main__":
main()
Create a pygame.Surface with the size of the rectangle rather than drawing a rectangle:
class lander:
def __init__(self, x, y, height, width):
# [...]
self.image = pygame.Surface((width, height))
self.image.fill((192, 192, 192))
def draw(self):
self.lander = self.image.get_rect(topleft = (self.x, self.y))
g.screen.blit(self.image, self.lander)
Use pygame.mask.from_surface() to create a pygame.mask.Mask object from a Surface:
class player:
def __init__(self, x, y, height, width):
# [...]
self.mask = pygame.mask.from_surface(self.ufo)
class lander:
def __init__(self, x, y, height, width):
# [...]
self.mask = pygame.mask.from_surface(self.image)
Use overlap() to detect the collision of masks. The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
In the following p is an instance of player and l is an instance of lander. So the offset is calculated by subtracting the coordinates of p from the coordinates of l:
offset = (l.x - p.x), (l.y - p.y)
if p.mask.overlap(l.mask, offset):
print("hit")
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask and Pygame collision with masks is not working

Object to shoot a projectile

I'm trying to make my tanks shoot, and I did all the code I think I should have done but I don't know why the tanks aren't shooting anything.
import pygame, assetloader
from pygame.locals import *
import random, time, math
import pygame
GRAD = math.pi/180
blue = (0, 0, 255)
wallRects = []
bullets = []
maze = [[] for i in range(25)]
assetloader.set_asset_path("assets/")
I defined the Bullet Class here:
def calculate_dir_with_angle(angle):
direction = [0, 0]
if (angle > 0 and angle < 180) or (angle > -360 and angle < -180):
direction[0] = -1
elif (angle > -180 and angle < 0) or (angle > 180 and angle < 360):
direction[0] = 1
elif (angle > -90 and angle < 90) or (angle > 270 and anlge < 360):
direction[1] = -1
elif (angle > 90 and angle < 270) or (angle > -270 and angle < -90):
direction[1] = 1
return direction
class Bullet:
def __init__(self, pos, r, angle):
self.x = pos[0]
self.y = pos[1]
self.r = r
self.counter = 50
direction = calculate_dir_with_angle(angle)
self.vel = [direction[0] * 2, direction[1] * 2]
def draw(self, screen):
self.x = int(self.x)
self.y = int(self.y)
pygame.draw.circle(screen, (25, 25, 25), (self.x, self.y), (self.r))
def move(self):
self.x += self.vel[0]
self.y += self.vel[1]
self.rect = pygame.Rect(self.x-self.r, self.y - self.r, 2 * self.r, 2 * self.r)
for wr in wallRects:
if self.rect.centery >= wr.top and self.rect.centery <= wr.bottom:
if self.rect.left <= wr.right and self.rect.left > wr.left:
self.vel[0] = -self.vel[0]
self.x = wr.right + self.r + 1
self.rect.x = wr.right + 1
elif self.rect.right >= wr.left and self.rect.right < wr.right:
self.vel[0] = -self.vel[0]
self.x = wr.left + self.r - 1
self.rect.x = wr.left - 2 * self.r - 1
if self.rect.centerx >= wr.left and self.rect.centerx <= wr.right:
if self.rect.top <= wr.bottom and self.rect.top > wr.top:
self.vel[1] = -self.vel[1]
self.y = wr.bottom + self.r + 1
self.rect.y = wr.bottom + 1
elif self.rect.bottom >= wr.top and self.rect.bottom < wr.bottom:
self.vel[1] = -self.vel[1]
self.y = wr.top - self.r - 1
self.rect.y = wr.top - 2 * self.r - 1
if self.counter > 0:
self.counter -= 1
def generateRandomPosition():
row = random.randint(1, 23)
col = random.randint(1, 23)
while maze[row][col-1] != 0 or maze[row][col] != 0 or maze[row][col+1] != 0:
row = random.randint(1, 23)
col = random.randint(1, 23)
return row, col
Player 1:
class Player(pygame.sprite.Sprite):
def __init__(self, x, y, pos):
pygame.sprite.Sprite.__init__(self)
self.image, self.rect = assetloader.load_image("Tank.png", -1)
self.rect.x = x
self.rect.y = y
self.rect.clamp_ip(screen.get_rect())
self.rows = pos[0]
self.cols = pos[1]
self.x = self.cols * gsize
self.y = self.rows * gsize
self.orig_image, self.orig_rect = assetloader.load_image("Tank.png", -1)
self.orig_rect.x = self.x
self.orig_rect.y = self.y
self.orig_gun_pos = self.orig_rect.midtop
self.ammo = 5
def checkCollisions(self):
for b in bullets:
if b.counter <= 0:
if b.rect.colliderect(self.orig_rect):
self.dead = True
def calculate_gun_pos(self):
self.orig_gun_pos = self.orig_rect.midtop
new_y = self.orig_gun_pos[1] - self.orig_rect.centery
new_x = self.orig_gun_pos[0] - self.orig_rect.centerx
rads = self.dir * GRAD
gun_x = (new_y * math.sin(rads)) + (new_x * math.cos(rads)) + (self.orig_rect.centerx)
gun_y = (new_y * math.cos(rads)) - (new_x * math.sin(rads)) + (self.orig_rect.centery)
self.gun_pos = (gun_x, gun_y)
def shoot(self):
if self.ammo > 0:
self.calculate_gun_pos()
b = Bullet(self.gun_pos, 3, self.dir)
bullets.append(b)
self.ammo -= 1
def draw(self, screen):
image = pygame.transform.rotate(self.image, self.dir)
screen.blit(image, self.rect)
def update(self):
oldCenter = self.rect.center
self.rect = self.image.get_rect()
self.rect.center = oldCenter
screen_rect = screen.get_rect()
keys = pygame.key.get_pressed()
if keys[K_m]:
p.shoot()
if not screen_rect.contains(self.rect):
self.rect.clamp_ip(screen_rect)
Calling the functions:
size = width, height = 500, 400
gsize = 25
start_x, start_y = 0, 0
bgColor = 255, 255, 255
pygame.init()
screen = pygame.display.set_mode(size)#, pygame.FULLSCREEN)
pygame.display.set_caption("Sample Sprite")
clock = pygame.time.Clock()
p = Player(width/2, height/4, (3,4))
coll_font = pygame.font.Font(None, 30)
going = True
while going:
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
going = False
elif event.type == KEYDOWN:
if event.key == K_m:
p.shoot()
for b in bullets:
b.move()
p.update()
screen.fill(bgColor)
p.draw(screen)
pygame.display.flip()
pygame.quit()
How would I call the bullet to actually appear and fire because I have the Bullet class which gets called within the Player class in def shoot(self) so does anyone have an idea why the bullets aren't appearing?
I usually add bullets in this way: I pass the group that contains all sprites and the bullet group to the player instance and add new bullets to these group in the player's handle_event method.
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
screen_rect = screen.get_rect()
FONT = pg.font.Font(None, 24)
BG_COLOR = pg.Color('gray12')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(
BULLET_IMAGE, pg.Color('aquamarine1'), [(0, 0), (20, 5), (0, 11)])
PLAYER_IMAGE = pg.Surface((50, 30), pg.SRCALPHA)
pg.draw.polygon(
PLAYER_IMAGE, pg.Color('dodgerblue1'), [(0, 0), (50, 15), (0, 30)])
class Player(pg.sprite.Sprite):
def __init__(self, pos, all_sprites, bullet_group):
super().__init__()
self.image = PLAYER_IMAGE
self.orig_image = self.image # Needed to preserve image quality.
self.rect = self.image.get_rect(center=(pos))
self.pos = Vector2(pos)
self.vel = Vector2(1, 0)
self.angle = 0
self.angle_speed = 0
self.all_sprites = all_sprites
self.bullet_group = bullet_group
def handle_event(self, event):
if event.type == pg.MOUSEBUTTONDOWN:
# Left button fires a bullet from cannon center with
# current angle. Add the bullet to the bullet_group.
if event.button == 1:
bullet = Bullet(self.pos, self.angle)
self.bullet_group.add(bullet)
self.all_sprites.add(bullet)
elif event.type == pg.KEYDOWN:
# Rotate self by setting the .angle_speed.
if event.key in (pg.K_a, pg.K_LEFT):
self.angle_speed = -3
elif event.key in (pg.K_d, pg.K_RIGHT):
self.angle_speed = 3
elif event.type == pg.KEYUP:
if event.key in (pg.K_a, pg.K_LEFT):
self.angle_speed = 0
elif event.key in (pg.K_d, pg.K_RIGHT):
self.angle_speed = 0
def update(self):
self.pos += self.vel
self.rect.center = self.pos
if self.angle_speed != 0:
self.rotate()
def rotate(self):
# Update the angle and the velocity vector.
self.angle += self.angle_speed
self.vel.rotate_ip(self.angle_speed)
# Rotate the image and get a new rect with the previous center.
self.image = pg.transform.rotozoom(self.orig_image, -self.angle, 1)
self.rect = self.image.get_rect(center=self.rect.center)
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, angle):
super().__init__()
self.image = pg.transform.rotate(BULLET_IMAGE, -angle)
self.rect = self.image.get_rect(center=pos)
# To apply an offset (40 pixels) to the start position,
# create another vector and rotate it as well.
offset = Vector2(40, 0).rotate(angle)
# Add the offset vector to the position vector (the center).
self.pos = Vector2(pos) + offset
# Rotate the start velocity vector (9, 0) by the angle.
self.vel = Vector2(9, 0).rotate(angle)
def update(self):
# Add the velocity to the pos to move the sprite.
self.pos += self.vel
self.rect.center = self.pos # Update the rect as well.
# Remove bullets outside of the screen area.
if not screen_rect.contains(self.rect):
self.kill()
def main():
clock = pg.time.Clock()
all_sprites = pg.sprite.Group()
# Bullets will be added to this group.
bullet_group = pg.sprite.Group()
# Pass the bullet group to the player.
player = Player((300, 200), all_sprites, bullet_group)
all_sprites.add(player)
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
# Pass events to the player instance.
player.handle_event(event)
all_sprites.update()
screen.fill(BG_COLOR)
all_sprites.draw(screen)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()

Categories