rishi
rishi

Reputation: 640

Not able to make my cannon fire bullets in pygame

In my game I can place cannons anywhere on the screen. I want my cannons to be able to fire bullets and the bullets should be reset after they travel 100 pixels. Given below is my cannon class, I'm still new to OOP so I require some help however I wasn't able to accomplish this task using lists either. Thank you for your help.

class Cannon():
    global cannon_list
    global bullet_list
    def __init__(self, x, y, track, old_x):
        self.x = x
        self.y = y
        self.track = track
        self.old_x = old_x

    def spawnBullet(self):
        for j in bullet_list:
            self.old_x = self.x
            self.track = j[2]
            screen.blit(bullet, (j[0], j[1]))

    def moveBullet(self):
        if self.x <= self.track:
            self.x += 3

    def resetBullet(self):
        if self.x >= self.track:
            self.x = self.old_x

    def spawnCannon(self):
        for i in cannon_list:
            screen.blit(cannon, i)

Using the cannon class. this is under redrawGamewindow.

for j in bullet_list:
    cannonsAndBullets = Cannon(j[0], j[1], j[2], j[0])
    cannonsAndBullets.spawnCannon()
    cannonsAndBullets.spawnBullet()
    cannonsAndBullets.moveBullet()
    cannonsAndBullets.resetBullet()

Given below is what I have appended into bullet_list and cannon_list. x an y are the position of my player

            cannon_list.append([x,y])
            bullet_list.append([x,(y+25),100, x])

Edits in my class

class Cannon():
    global cannon_list
    global bullet_list
    def __init__(self, x, y, track, old_x):
        self.x = x
        self.y = y
        self.track = track
        self.old_x = old_x

    def spawnBullet(self):
        # for j in bullet_list:
        # self.x, self.y, self.track, self.old_x = j
        screen.blit(bullet, (self.x, self.y))

    def moveBullet(self):
        # for j in bullet_list:
        # self.x, self.y, self.track, self.old_x = j
        if self.track <= 100:
            print(self.track)
            self.track += 3
            self.x += 3

    def resetBullet(self):
        # for j in bullet_list:
        # self.x, self.y, self.track, self.old_x = j
        if self.track >= 100:
            print(self.track)
            self.x = self.old_x

    def spawnCannon(self):
        for i in cannon_list:
            screen.blit(cannon, i)

Upvotes: 2

Views: 126

Answers (1)

sloth
sloth

Reputation: 101072

Let's discard everything and start from scratch and make use of pygame features like sprites and vector math.

We begin with a basic skeleton of a pygame game, a simple window:

import pygame

def main():
    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill(pygame.Color('grey'))
        pygame.display.flip()
        clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Now, we want to place some Sprites. Let's create a Sprite class that represents the cannons, and let's place them with the mouse:

import pygame

class Cannon(pygame.sprite.Sprite):
    def __init__(self, pos, *grps):
        super().__init__(*grps)
        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('darkred'))
        self.rect = self.image.get_rect(center=pos)

def main():

    all_sprites = pygame.sprite.Group()

    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == pygame.MOUSEBUTTONDOWN:
                Cannon(e.pos, all_sprites)


        all_sprites.update()

        screen.fill(pygame.Color('grey'))
        all_sprites.draw(screen)
        pygame.display.flip()

        clock.tick(60)

if __name__ == '__main__':
    main()

enter code here

Now we want the cannons to shoot bullets. To do that, we're making use of some OOP features, like polymorphism. Bullets and cannons are different types, but they provide the same interface: update and draw. And that's it. Note how our mainloop does not need to know what types exactly our sprites are.

We also make sure that all the logic for bullets is in the Bullet class, and all the logic for the cannon is in the Cannon class:

import pygame

class Bullet(pygame.sprite.Sprite):
    def __init__(self, pos, *grps):
        super().__init__(*grps)
        self.image = pygame.Surface((4, 4))
        self.image.fill(pygame.Color('black'))
        self.rect = self.image.get_rect(center=pos)
        self.pos = pygame.Vector2(pos)
        self.travelled = pygame.Vector2(0, 0)
        direction = pygame.Vector2(pygame.mouse.get_pos()) - self.pos
        if direction.length() > 0:
            self.direction = direction.normalize()
        else:
            self.kill()

    def update(self, dt):
        v = self.direction * dt / 5
        self.pos += v
        self.travelled += v
        self.rect.center = self.pos
        if self.travelled.length() > 100:
            self.kill()

class Cannon(pygame.sprite.Sprite):
    def __init__(self, pos, *grps):
        super().__init__(*grps)
        self.image = pygame.Surface((32, 32))
        self.image.fill(pygame.Color('darkred'))
        self.rect = self.image.get_rect(center=pos)
        self.timer = 200

    def update(self, dt):
        self.timer = max(self.timer - dt, 0)
        if self.timer == 0:
            self.timer = 200
            Bullet(self.rect.center, self.groups()[0])

def main():

    all_sprites = pygame.sprite.Group()

    screen = pygame.display.set_mode((640, 480))
    clock = pygame.time.Clock()
    dt = 1
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
            if e.type == pygame.MOUSEBUTTONDOWN:
                Cannon(e.pos, all_sprites)


        all_sprites.update(dt)

        screen.fill(pygame.Color('grey'))
        all_sprites.draw(screen)
        pygame.display.flip()

        dt = clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Using a vector and simply adding the distance travelled each frame makes it easy to check for each Bullet if it already travelled 100 pixels. If true, simply calling kill will remove it.

Upvotes: 2

Related Questions