How to create bullets in pygame?

2019-03-04 03:49发布

问题:

I know there have been several topics on this but I still can't figure out how to make my ship shoot bullets.. I want to add to my MOUSEBUTTONDOWN bullets shooting from the ship as the sound effect plays. thanks for the help!

import sys, pygame, pygame.mixer
from pygame.locals import *

pygame.init()

size = width, height = 800, 600
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

background = pygame.image.load("bg.png")
ship = pygame.image.load("ship.png")
ship = pygame.transform.scale(ship,(64,64))

shot = pygame.mixer.Sound("shot.wav")
soundin = pygame.mixer.Sound("sound.wav")

soundin.play()

while 1:
  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      sys.exit()

    elif event.type == MOUSEBUTTONDOWN:
      shot.play()

  clock.tick(60)

  mx,my = pygame.mouse.get_pos()

  screen.blit(background,(0,0))
  screen.blit(ship,(mx-32,500))
  pygame.display.flip()

回答1:

There are several steps you have to go through to do this. You will need a picture of the bullet, a way to store the locations of the bullets, a way to create the bullets, a way to render the bullets, and a way to update the bullets. You appear to know how to import pictures already, so I'll skip that part.

There are several ways that you can store pieces of information. I will be using a list of the top left corner of the bullets. Create the list anywhere before the final loop with bullets = [].

To create the bullets, you will want to use the location of the mouse. Add in bullets.append([event.pos[0]-32, 500]) after shot.play(), indented the same amount.

To render the bullets, you will be adding a for loop into your game loop. After the line screen.blit(background, (0, 0)), add the following code:

for bullet in bullets:
    screen.blit(bulletpicture, pygame.Rect(bullet[0], bullet[1], 0, 0)

To update the bullets, you need to put something somewhere in your game loop that looks like this:

for b in range(len(bullets)):
    bullets[b][0] -= 10

Finally, you need to remove the bullets when they reach the top of the screen. Add this after the for loop you just created (iterate over a slice copy, because lists shouldn't be modified during the iteration):

for bullet in bullets[:]:
    if bullet[0] < 0:
        bullets.remove(bullet)

After putting this all into your code, it should look something like this:

import sys, pygame, pygame.mixer
from pygame.locals import *

pygame.init()

size = width, height = 800, 600
screen = pygame.display.set_mode(size)

clock = pygame.time.Clock()

bullets = []

background = pygame.image.load("bg.png").convert()
ship = pygame.image.load("ship.png").convert_alpha()
ship = pygame.transform.scale(ship, (64, 64))
bulletpicture = pygame.image.load("You know what to do").convert_alpha()

shot = pygame.mixer.Sound("shot.wav")
soundin = pygame.mixer.Sound("sound.wav")

soundin.play()

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        elif event.type == MOUSEBUTTONDOWN:
            shot.play()
            bullets.append([event.pos[0]-32, 500])

    clock.tick(60)

    mx, my = pygame.mouse.get_pos()

    for b in range(len(bullets)):
        bullets[b][0] -= 10

    # Iterate over a slice copy if you want to mutate a list.
    for bullet in bullets[:]:
        if bullet[0] < 0:
            bullets.remove(bullet)

    screen.blit(background, (0, 0))

    for bullet in bullets:
        screen.blit(bulletpicture, pygame.Rect(bullet[0], bullet[1], 0, 0))

    screen.blit(ship, (mx-32, 500))
    pygame.display.flip()

If both you and I have done everything correctly, this should give you functioning bullets. Please, don't hesitate to ask me any questions if you don't understand what is going on or if something doesn't work.

Note that images/pygame.Surfaces should be converted with the convert or convert_alpha methods to improve the performance.



回答2:

Here's an example that demonstrates how pygame.sprite.Sprites and pygame.sprite.Groups can be used to create bullets. It also shows how to shoot continuously with the help of a timer variable and the dt (delta time) that clock.tick returns.

pygame.sprite.groupcollide is used for the collision detection between the bullets and the enemies sprite group. I iterate over the items in the hits dict to reduce the health points of the enemies.

import random
import pygame as pg


pg.init()

BG_COLOR = pg.Color('gray12')
PLAYER_IMG = pg.Surface((30, 50), pg.SRCALPHA)
pg.draw.polygon(PLAYER_IMG, pg.Color('dodgerblue'), [(0, 50), (15, 0), (30, 50)])
ENEMY_IMG = pg.Surface((50, 30))
ENEMY_IMG.fill(pg.Color('darkorange1'))
BULLET_IMG = pg.Surface((9, 15))
BULLET_IMG.fill(pg.Color('aquamarine2'))


class Player(pg.sprite.Sprite):

    def __init__(self, pos, all_sprites, bullets):
        super().__init__()
        self.image = PLAYER_IMG
        self.rect = self.image.get_rect(center=pos)
        self.all_sprites = all_sprites
        self.add(self.all_sprites)
        self.bullets = bullets
        self.bullet_timer = .1

    def update(self, dt):
        self.rect.center = pg.mouse.get_pos()

        mouse_pressed = pg.mouse.get_pressed()
        self.bullet_timer -= dt  # Subtract the time since the last tick.
        if self.bullet_timer <= 0:
            self.bullet_timer = 0  # Bullet ready.
            if mouse_pressed[0]:  # Left mouse button.
                # Create a new bullet instance and add it to the groups.
                Bullet(pg.mouse.get_pos(), self.all_sprites, self.bullets)
                self.bullet_timer = .1  # Reset the timer.


class Enemy(pg.sprite.Sprite):

    def __init__(self, pos, *sprite_groups):
        super().__init__(*sprite_groups)
        self.image = ENEMY_IMG
        self.rect = self.image.get_rect(center=pos)
        self.health = 30

    def update(self, dt):
        if self.health <= 0:
            self.kill()


class Bullet(pg.sprite.Sprite):

    def __init__(self, pos, *sprite_groups):
        super().__init__(*sprite_groups)
        self.image = BULLET_IMG
        self.rect = self.image.get_rect(center=pos)
        self.pos = pg.math.Vector2(pos)
        self.vel = pg.math.Vector2(0, -450)
        self.damage = 10

    def update(self, dt):
        # Add the velocity to the position vector to move the sprite.
        self.pos += self.vel * dt
        self.rect.center = self.pos  # Update the rect pos.
        if self.rect.bottom <= 0:
            self.kill()


class Game:

    def __init__(self):
        self.clock = pg.time.Clock()
        self.screen = pg.display.set_mode((800, 600))

        self.all_sprites = pg.sprite.Group()
        self.enemies = pg.sprite.Group()
        self.bullets = pg.sprite.Group()
        self.player = Player((0, 0), self.all_sprites, self.bullets)

        for i in range(15):
            pos = (random.randrange(30, 750), random.randrange(500))
            Enemy(pos, self.all_sprites, self.enemies)

        self.done = False

    def run(self):
        while not self.done:
            # dt = time since last tick in milliseconds.
            dt = self.clock.tick(60) / 1000
            self.handle_events()
            self.run_logic(dt)
            self.draw()

    def handle_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True

    def run_logic(self, dt):
        self.all_sprites.update(dt)

        # hits is a dict. The enemies are the keys and bullets the values.
        hits = pg.sprite.groupcollide(self.enemies, self.bullets, False, True)
        for enemy, bullet_list in hits.items():
            for bullet in bullet_list:
                enemy.health -= bullet.damage

    def draw(self):
        self.screen.fill(BG_COLOR)
        self.all_sprites.draw(self.screen)
        pg.display.flip()


if __name__ == '__main__':
    Game().run()
    pg.quit()


标签: python pygame