Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Bullets" shot from two Pygame sprites combine into a single long line

Tags:

python

pygame

I have a sprite which shoots bullets. Sometimes bullets also shoot out of invisible shooters.

The switch from opponent to shooter mid-program works but when I want the bullets to shoot in a certain way, with delays between each shot, the bullets seem to become a single line (the purple thing in the image is the bullets):

Screenshot with long purple line starting from circle

The mechanic works when it's the opponent shooting, but not when it's the invisible square.

Why is this happening? Is there a small bug I need to fix?

Here's my code:

import pygame
import time
import itertools
import os

pygame.init()
SCREENWIDTH = 1000
SCREENHEIGHT = 650
screen = pygame.display.set_mode([SCREENWIDTH, SCREENHEIGHT])
screen.fill((255, 123, 67))
pygame.draw.rect(screen, (230, 179, 204), (0, 50, 1000, 650), 0)

background = screen.copy()
clock = pygame.time.Clock()
stageon = True

class Spell:

    def __init__(self, bullet, pattern, speed, loop, tick_delay):
        self.bullet = bullet
        self.pattern = pattern
        self.speed = speed
        self.loop = loop
        self.tick_delay = tick_delay

class Shooter(pygame.sprite.Sprite):

    def __init__(self, spell, pos, *groups):
        super().__init__(*groups)
        self.image = pygame.image.load("Sprites/transparent.jpg")
        self.rect = self.image.get_rect(topleft=(pos))
        self.pos = pygame.Vector2(pos)
        self.start_time = pygame.time.get_ticks()
        self.currentspell = spell
        self.speed = 3
        self.ticks = 1000

    def update(self):
        time_gone = pygame.time.get_ticks() - self.start_time
        if self.currentspell is not None and time_gone > self.currentspell.tick_delay:
            self.start_time = pygame.time.get_ticks()
            for bullet in self.currentspell.pattern:
                if bullet[0] <= time_gone:
                    Bullet(self.rect.center, bullet[1], self.currentspell.bullet, sprites, bullets)

            self.currentspell.loop -= 1
            if self.currentspell.loop <= 0:
                    self.currentspell = None
                    self.kill()

class Opponent(pygame.sprite.Sprite):

    def __init__(self, sprite, sequence, *groups):
        super().__init__(*groups)
        self.image = sprite
        self.rect = self.image.get_rect(topleft=(425, 30))
        self.pos = pygame.Vector2(self.rect.topleft)
        self.start_time = pygame.time.get_ticks()
        self.sequence = sequence
        self.spellno = 0
        self.currentspell = sequence[self.spellno]
        self.speed = 3
        self.ticks = 1000
        self.shooters = 0
    def update(self):
        time_gone = pygame.time.get_ticks() - self.start_time
        if type(self.currentspell) != Spell:
           Shooter(self.currentspell[0], self.currentspell[1], sprites) 
           self.shooters += 1
        if self.shooters != 0:
           return
        else:
            if self.currentspell is not None and time_gone > self.currentspell.tick_delay:
                self.start_time = pygame.time.get_ticks()
                for bullet in self.currentspell.pattern:
                    if bullet[0] <= time_gone:
                        Bullet(self.rect.center, bullet[1], self.currentspell.bullet, sprites, bullets)

                self.currentspell.loop -= 1
                if self.currentspell.loop <= 0:
                    self.spellno += 1
                    if self.spellno >= len(self.sequence):
                        self.currentspell = None
                    else:
                        self.currentspell = self.sequence[self.spellno]

sprites = pygame.sprite.Group()

class Bullet(pygame.sprite.Sprite):

    def __init__(self, pos, direction, image, *groups):
        super().__init__(*groups)
        self.image = image
        self.rect = self.image.get_rect(topleft=pos)
        self.direction = direction
        self.pos = pygame.Vector2(self.rect.topleft)


    def update(self):
        self.pos += self.direction
        self.rect.topleft = (self.pos.x, self.pos.y)
        if not screen.get_rect().colliderect(self.rect):
            self.kill()

bullets = pygame.sprite.Group()

opponentgroup = pygame.sprite.Group()

img4 = pygame.image.load("Sprites/utd.png")

ut1 = Spell(pygame.image.load("Sprites/purple-glowey.png"),((0, pygame.Vector2(-1, 1) * 4),
                                                            (0, pygame.Vector2(-0.5, 1) * 4.5),
                                                            (0, pygame.Vector2(0, 1) * 5),
                                                            (0, pygame.Vector2(0.5, 1) * 4.5),
                                                            (0, pygame.Vector2(1, 1) * 4),),4, 1, 400)

ut2 = Spell(pygame.image.load("Sprites/purple-glowey.png"),((0, pygame.Vector2(1, 0) * 5),),4, 8, 400)

op_spells = [ut1, (ut2, (10, 395))]

OP = Opponent(img4, op_spells, opponentgroup)
sprites.add(OP)



def main():
    while stageon:
        for events in pygame.event.get():
            if events.type == pygame.QUIT or stageon == False:
                time.sleep(1)
                pygame.quit()
                return        
        sprites.update()
        screen.blit(background, (0, 0))
        sprites.draw(screen)
        pygame.display.update()

        clock.tick(100)
        if stageon == False:
            return
if __name__ == '__main__':
    main()
like image 519
Eleeza the Other World Wizard Avatar asked Feb 17 '19 18:02

Eleeza the Other World Wizard


1 Answers

You problem is this the update function of the Opponent class:

def update(self):
    time_gone = pygame.time.get_ticks() - self.start_time
    if type(self.currentspell) != Spell:
       Shooter(self.currentspell[0], self.currentspell[1], sprites) 
       self.shooters += 1
    ...

You use the list op_spells = [ut1, (ut2, (10, 395))], and as you can see, the second element is not an instance of Spell. So the if condition if type(self.currentspell) != Spell will be True and a new Shooter instance will be created.

But the creation of the Shooter instace happens every frame, so you create an infinite number of Shooter instances, which in turn create an infinite number of Bullet instances.

You could change your update function to this:

def update(self):
    time_gone = pygame.time.get_ticks() - self.start_time

    if self.currentspell is not None:
        spell = self.currentspell if type(self.currentspell) == Spell else self.currentspell[0]
        shooterpos = None if type(self.currentspell) == Spell else self.currentspell[1]
        if time_gone > spell.tick_delay:
            if shooterpos:
                # we have a position, so create a shooter
                Shooter(spell, shooterpos, sprites) 
            else:
                # we don't have a position, so create the Bullets ourself
                self.start_time = pygame.time.get_ticks()
                for bullet in spell.pattern:
                    if bullet[0] <= time_gone:
                        Bullet(self.rect.center, bullet[1], spell.bullet, sprites, bullets)
                spell.loop -= 1

            # if we have a position or the spell loop is done, go to the next spell
            if shooterpos or spell.loop <= 0:
                self.spellno += 1
                if self.spellno >= len(self.sequence):
                    self.currentspell = None
                else:
                    self.currentspell = self.sequence[self.spellno]
like image 113
sloth Avatar answered Oct 27 '22 00:10

sloth