As each instance of LineSprite draw.line() is at different angles (depending on start & end mouse pos), and the image.get_rect() takes up more space than line drawn, I want to mask the drawn line to use for collisions between different sprite groups. I've tried various spritecollide's but it doesn't recognize the line as a sprite or having a rect. I can get a collision using a the rect.clipline() but I have to add each instance to a list and look through the list but I can't reverse engineer it to figure out which instance of LineSprite was hit to remove (kill()) from screen.
import sys
import pygame as pg
import math
WIDTH = 800
HEIGHT = 800
FPS = 60
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (0, 0, 255)
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.all_sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pg.Surface((20, 20))
self.image.fill(WHITE)
self.rect = self.image.get_rect()
self.x = x
self.y = y
def update(self):
self.rect.x = self.x
self.rect.y = self.y
class LineSprite(pg.sprite.Sprite):
def __init__(self, game, screen, start_pos, end_pos):
self.groups = game.all_sprites, game.lines
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.screen = screen
self.start_pos = start_pos
self.end_pos = end_pos
self.length = abs(((self.end_pos[0] - self.start_pos[0]) ** 2 + (self.end_pos[1] - self.start_pos[1]) ** 2) ** 0.5) - 1
self.image = pg.Surface((self.start_pos[0] + self.length, self.start_pos[1] + self.length))
self.image.set_colorkey((0, 0, 0))
self.rect = self.image.get_rect()
self.border = pg.Rect(self.start_pos[0], self.start_pos[1], self.end_pos[0]-self.start_pos[0], self.end_pos[1]-self.start_pos[1])
self.border.normalize()
pg.draw.line(self.image, GREEN, (self.start_pos), (self.end_pos), 5)
self.game.linelist.append(((self.start_pos), (self.end_pos)))
pg.draw.rect(self.image, RED, self.border, 5)
# self.screen.blit(self.image, self.rect)
def remove(self):
self.kill()
class BlockSprite(pg.sprite.Sprite):
def __init__(self, game, colour, x, y):
self.groups = game.all_sprites, game.blocks
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.colour = colour
self.x = x
self.y = y
self.image = pg.Surface((20, 20))
self.image.fill(self.colour)
self.rect = self.image.get_rect()
self.rect.x = self.x
self.rect.y = self.y
def remove(self):
self.kill()
class Game:
def __init__(self):
pg.init()
pg.display.set_caption("Wizard")
self.screen = pg.display.set_mode((HEIGHT, WIDTH))
self.clock = pg.time.Clock()
self.load_data()
self.linelist = []
def load_data(self):
pass
def new(self):
self.all_sprites = pg.sprite.Group()
self.player = Player(self, 20, 20)
self.lines = pg.sprite.Group()
self.blocks = pg.sprite.Group()
self.run()
def run(self):
self.playing = True
while self.playing:
self.mx, self.my = pg.mouse.get_pos()
self.dt = self.clock.tick(FPS) / 1000
self.events()
self.update()
self.draw()
def quit(self):
pg.quit()
sys.exit()
def update(self):
self.player.x = self.mx - 10
self.player.y = self.my - 10
self.all_sprites.update()
def draw_grid(self):
for x in range(0, int(WIDTH / 20)):
pg.draw.line(self.screen, WHITE, (x * 20, 0), (x * 20, HEIGHT))
for y in range(0, int(HEIGHT / 20)):
pg.draw.line(self.screen, WHITE, (0, y * 20), (WIDTH, y * 20))
def draw(self):
self.screen.fill(BLUE)
self.draw_grid()
self.all_sprites.draw(self.screen)
pg.display.flip()
def events(self):
for event in pg.event.get():
if event.type == pg.QUIT:
self.quit()
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.quit()
if event.key == pg.K_b:
self.x, self.y = pg.mouse.get_pos()
self.block = BlockSprite(self, GREEN, self.x - 10, self.y -10)
if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
self.start_pos = None
self.end_pos = None
self.start_pos = pg.mouse.get_pos()
if event.type == pg.MOUSEBUTTONUP and event.button == 1:
self.end_pos = pg.mouse.get_pos()
self.line = LineSprite(self, self.screen, self.start_pos, self.end_pos)
hits = pg.sprite.spritecollide(self.player, self.blocks, False)
for hit in hits:
if event.type == pg.MOUSEBUTTONDOWN and event.button == 3:
if hit.colour == GREEN:
col = RED
else:
col = GREEN
hit.remove()
self.block = BlockSprite(self, col, hit.x, hit.y)
if event.type == pg.KEYDOWN:
if event.key == pg.K_x:
hit.remove()
for block in self.blocks:
if any(block.rect.clipline(*line) for line in self.linelist):
print("hit")
# in self.lines - LineSprite.remove()
g=Game()
g.start_screen()
while True:
g.new()
Just use pygame.Rect.normalize
:
This will flip the width or height of a rectangle if it has a negative size. The rectangle will remain in the same place, with only the sides swapped.
rect = pygame.Rect(x1, y1, x2-x1, y2-y1)
rect.normalize()
If you want to draw a rotated rectangle, see rotating a rectangle in pygame
If you want to detect the collision between a rectangle and a line, see How do I check collision between a line and a rect in pygame?
If you want to detect the collision of an image and a line, see Make a line as a sprite with its own collision in Pygame