1

I'm currently making a plane shooting game in Pygame. I have created plane class and now started to create the bullet class. However, i'm experiencing an issue that i couldn't solve : i really need to use pygame.event.get() loops both inside the class and in the while loop (the main while loop of the game) . So when i used 2 loops like that, the game controls' became so weird and messy and after i searched for this problem, i found that normally it must be only one loop on pygame.event.get(). I don't know what to do because if i combine the two loops into the main one situated in the while loop, then i wouldn't be able to refer to the bullet object because it will be outside of the class. This is my code :

class Singlebullet(object):

    def __init__(self):
        self.img = pygame.image.load(r'C:\Users\me\Documents\MyGame\bullet.png')
        self.x = planeX + 71 
        self.y = planeY 
        self.state = 'ready'
        self.speed = 0
        self.firerate = 1000
    
    def shoot(self):
        if current_bullet_type == 'single':
            if self.state == 'ready':
                MyScrollingScreen.blit(self.img, (self.x, self.y))
            if self.state == 'fire':
                self.speed = -8
                self.y += bullet_speed
                MyScrollingScreen.blit(self.img, (self.x, self.y))
            if self.y < -20:
                self.state = 'ready'
            for e in pygame.event.get():
                if e.type == pygame.KEYDOWN or e.type == pygame.KEYUP:
                    if e.key == pygame.K_SPACE:
                        self.state = 'fire'
                        pygame.time.set_timer(pygame.USEREVENT+1, self.firerate)
                if e.type == pygame.USEREVENT+1:
                    self.state = 'ready'
s1 = Singlebullet()

while running:
    #[...]
    for event in pygame.event.get():
        #[...]
Developeeer
  • 110
  • 1
  • 1
  • 10

2 Answers2

1

pygame.event.get() removes the events from the queue (see pygame documentation).

So, some of the events are gotten in the main loop, and the others in the class, and you don't know where a specific event is gotten py pygame. So you miss some of the events.

To avoid that, call pygame.event.get() one time per frame, and use the same list for both main loop and SingleBullet:

class Singlebullet(object):
    
    def shoot(self):
        ...
        for e in events: # same events as in main loop

while running:
    events = pygame.event.get()
    for event in events:
        ...

Don't forget to set the list to [] the first frame if you get the events after calling Singlebullet.shoot() (the class will ask for an unexisting list).

D_00
  • 1,440
  • 2
  • 13
  • 32
  • thank you, it's working! but what do you mean by the last remark ? i didn't get it – Developeeer May 01 '21 at 13:28
  • 1
    @Developeeer *"... what do you mean by the last remark ..."* - In this approach `events` is a global variable. You have to ensure that `events` is set, before `shoot()` is called for the first time. – Rabbid76 May 01 '21 at 13:48
  • 1
    `events` is a variable defined in the main loop. If you call `SingleBullet.shoot()` at the beginning of the program, without starting the main loop, then `events` is not defined. A simple way to avoid that is by defining `events = []` before calling `shoot()` (used in the first frame in this case) – D_00 May 01 '21 at 13:50
1

pygame.event.get() get all the messages and remove them from the queue. See the documentation:

This will get all the messages and remove them from the queue. [...]

If pygame.event.get() is called in multiple event loops, only one loop receives the events, but never all loops receive all events. As a result, some events appear to be missed.

Get the events once per frame and use them in multiple loops or pass the list of events to functions and methods where they are handled:

class Singlebullet(object):

    def __init__(self):
        # [...]
    
    def shoot(self, event_list):
        if current_bullet_type == 'single':
            # [...]

            for e in event_list:
                if e.type == pygame.KEYDOWN or e.type == pygame.KEYUP:
                    if e.key == pygame.K_SPACE:
                        self.state = 'fire'
                        pygame.time.set_timer(pygame.USEREVENT+1, self.firerate)
                if e.type == pygame.USEREVENT+1:
                    self.state = 'ready'
                    
s1 = Singlebullet()

while running:
    #[...]

    event_list = pygame.event.get()
    for event in event_list:
        #[...]

    s1.shoot(event_list)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thanks! but i didn't get the last line – Developeeer May 01 '21 at 13:32
  • 1
    @Developeeer Somewhere you call `shoot()`, isn't it? (I don't know where you are doing this because that part is missing from your question). You have to pass the `event_list` to `def shoot(self, event_list)`. The other solution is to use a global variable like in the other answer. However this is a very bad and ugly design. – Rabbid76 May 01 '21 at 13:33
  • 1
    okey that's clear now thank you! – Developeeer May 01 '21 at 13:36