Pygame Rotated Shooting

2019-01-15 11:37发布

问题:

Me and a few friends have been coding an intresting new shooting mechanic. For It to work, we need to shoot in the direction the player is facing. The Sprite is being rotated with Pygame.Transform.Rotate. How can we get an angle, and shoot our bullet in that direction?

Here is Our Code for rotation of the Sprite

 char_image_number = pygame.mouse.get_pressed()

    if char_image_number == (0, 0, 0):
            char_image = pygame.image.load(player_none).convert_alpha()
            char_image = pygame.transform.rotate(char_image, angle)
            screen.blit(background, (0,0))
            screen.blit(char_image,(540, 260))
            pygame.display.update()
    elif char_image_number == (1, 0, 0):
            print (angle)
            char_image = pygame.image.load(player_left).convert_alpha()
            angle+=1
            char_image = pygame.transform.rotate(char_image, angle)
            screen.blit(background, (0,0))
            screen.blit(char_image,(540, 260))
            pygame.display.update()
    elif char_image_number == (0, 0, 1):
            print (angle)
            angle-=1
            char_image = pygame.image.load(player_right).convert_alpha()

            char_image=pygame.transform.rotate(char_image, angle)
            screen.blit(background, (0,0))
            screen.blit(char_image,(540, 260))

            pygame.display.update()

    elif char_image_number == (1, 0, 1):
            char_image = pygame.image.load(player_both).convert_alpha()
            char_image = pygame.transform.rotate(char_image, angle)
            screen.blit(background, (0,0))
            screen.blit(char_image,(540, 260))
            pygame.display.update()

How can we shoot at the angle the player is facing?

回答1:

Here's a quick'n'dirty executable example of how to use vector math to do what you want.

import pygame

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((32, 32))
        self.image.fill((0, 0, 0))
        self.image.set_colorkey((0, 0, 0))
        pygame.draw.polygon(self.image, pygame.Color('dodgerblue'), ((0, 0), (32, 16), (0, 32)))
        self.org_image = self.image.copy()
        self.angle = 0
        self.direction = pygame.Vector2(1, 0)
        self.rect = self.image.get_rect(center=(200, 200))
        self.pos = pygame.Vector2(self.rect.center)

    def update(self, events, dt):
        for e in events:
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE:
                    self.groups()[0].add(Projectile(self.rect.center, self.direction.normalize()))
        pressed = pygame.key.get_pressed()
        if pressed[pygame.K_a]:
            self.angle += 3
        if pressed[pygame.K_d]:
            self.angle -= 3

        self.direction = pygame.Vector2(1, 0).rotate(-self.angle)
        self.image = pygame.transform.rotate(self.org_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)

class Projectile(pygame.sprite.Sprite):
    def __init__(self, pos, direction):
        super().__init__()
        self.image = pygame.Surface((8, 8))
        self.image.fill((0, 0, 0))
        self.image.set_colorkey((0, 0, 0))
        pygame.draw.circle(self.image, pygame.Color('orange'), (4, 4), 4)
        self.rect = self.image.get_rect(center=pos)
        self.direction = direction
        self.pos = pygame.Vector2(self.rect.center)

    def update(self, events, dt):
        self.pos += self.direction * dt
        self.rect.center = self.pos
        if not pygame.display.get_surface().get_rect().contains(self.rect):
            self.kill()

def main():
    pygame.init()
    screen = pygame.display.set_mode((500, 500))
    sprites = pygame.sprite.Group(Player())
    clock = pygame.time.Clock()
    dt = 0

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        sprites.update(events, dt)
        screen.fill((30, 30, 30))
        sprites.draw(screen)
        pygame.display.update()
        dt = clock.tick(60)

if __name__ == '__main__':
    main()

Note: it seems that you're loading the image of your player every frame. You should not do that, as this is very slow. Just load each image once. Also, it's better to have only one spot in your code that calls pygame.display.update(), as you should make sure it's called only once each frame.



回答2:

You have angle in degrees so try this:

import math

# angle examples 
for angle in [0, 30, 45, 60, 0+90, 30+90, 45+90, 60+90, 0+180, 30+180, 45+180, 60+180, 0+270, 30+270, 45+270, 60+270]: 

    bullet_speed = 10

    bullet_move_x = bullet_speed * math.cos( math.radians(angle) )
    bullet_move_y = -bullet_speed * math.sin( math.radians(angle) )

    print angle, bullet_move_x, bullet_move_y

Now you can add bullet_move_x, bullet_move_y to bullet positon.

For better results keep bullet position as float and convert to int when you blit bullet



回答3:

This example demonstrates how vectors can be utilized to control the position, velocity and also the start offset of the bullets.

In the while loop we use math.atan2 to calculate the angle to the target which is then used to rotate the cannon image and passed to the Bullet instances. In the __init__ method of the Bullets we rotate the offset vector, velocity vector and the sprite image by the angle.

To move the bullet, we add the (rotated) velocity vector to the position vector and set the rect position to the pos vector. The rect has to be moved, because it holds the blit position and is used for collision detection.

import math
import pygame as pg
from pygame.math import Vector2


pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BULLET_IMAGE = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.polygon(BULLET_IMAGE, pg.Color('grey11'), [(0, 0), (20, 5), (0, 11)])


class Bullet(pg.sprite.Sprite):

    def __init__(self, pos, angle):
        super(Bullet, self).__init__()
        self.image = pg.transform.rotate(BULLET_IMAGE, -angle)
        self.rect = self.image.get_rect(center=pos)
        # To apply an offset to the start position,
        # create another vector and rotate it as well.
        offset = Vector2(40, 0).rotate(angle)
        # Add the offset vector to the position vector.
        self.pos = Vector2(pos) + offset  # Center of the sprite.
        # Rotate the velocity vector (9, 0) by the angle.
        self.velocity = Vector2(9, 0).rotate(angle)

    def update(self):
        # Add velocity to pos to move the sprite.
        self.pos += self.velocity
        self.rect.center = self.pos


def main():
    clock = pg.time.Clock()
    # The cannon image and rect.
    cannon_img = pg.Surface((60, 22), pg.SRCALPHA)
    pg.draw.rect(cannon_img, pg.Color('grey19'), [0, 0, 35, 22])
    pg.draw.rect(cannon_img, pg.Color('grey19'), [35, 6, 35, 10])
    orig_cannon_img = cannon_img  # Store orig image to preserve quality.
    cannon = cannon_img.get_rect(center=(320, 240))
    angle = 0
    # Add bullets to this group.
    bullet_group = pg.sprite.Group()

    playing = True
    while playing:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                playing = False
            elif event.type == pg.MOUSEBUTTONDOWN:
                # Left button fires a bullet from cannon center with
                # current angle. Add the bullet to the bullet_group.
                if event.button == 1:
                    bullet_group.add(Bullet(cannon.center, angle))

        bullet_group.update()
        # Find angle to target (mouse pos).
        x, y = Vector2(pg.mouse.get_pos()) - cannon.center
        angle = math.degrees(math.atan2(y, x))
        # Rotate the cannon image.
        cannon_img = pg.transform.rotate(orig_cannon_img, -angle)
        cannon = cannon_img.get_rect(center=cannon.center)

        # Draw
        screen.fill(pg.Color('darkseagreen4'))
        bullet_group.draw(screen)
        screen.blit(cannon_img, cannon)
        txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
        screen.blit(txt, (10, 10))
        pg.draw.line(
            screen, pg.Color(150, 60, 20),
            cannon.center, pg.mouse.get_pos(), 2)
        pg.display.update()

        clock.tick(30)

if __name__ == '__main__':
    main()
    pg.quit()


标签: python pygame