Firing bullets from a rotating gun

2020-01-29 20:54发布

问题:

My question is connected to this question: rotating gun with restricted movement

What calculations do you need to do to get bullets firing from the gun muzzle?

my code is given below

def gun_radar(self):

    for p in self.gameobjects:

        if "R" in p.name or "L" in p.name:

            if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar:  # if hero within radar

                p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
                p.direction = p.orig_direction.rotate(p.current_angle)
                p.orientation = p.vec_to_target.dot(p.direction)

                if p.orientation > 2:
                    p.current_angle += 1
                elif p.orientation < -2:
                    p.current_angle -= 1

                p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)

                p.gun_rotate(-p.current_angle)

                self.blt_timer -= 1  #count down the timer. when zero calculate vector and add bullet to fired_blts

                if self.blt_timer<= 0:
                    w, h = p.rect.center
                    angle_in_rad = p.current_angle * (math.pi) / 180
                    w = w + math.cos(angle_in_rad)
                    h = h + math.sin(-angle_in_rad)
                    bullet = Bombullet(bulletimage, w, h)


                    bullet.xvel = bullet.speed * math.cos(angle_in_rad)
                    bullet.yvel = bullet.speed * math.sin(angle_in_rad)

                    bullet.rect.x += bullet.xvel
                    bullet.rect.y += bullet.yvel

                    self.fired_blts.add(bullet)
                    self.blt_timer = 100

when the hero comes within a circular area of the gun it is activated and a bullet shoots from the center of the gun.

I move the bullet with

def move(self):

        self.rect.x += self.xvel

        self.rect.y += self.yvel
        print self.rect.x, self.rect.y, self.life
        self.life -= 1

the bullet updates and shoots in the correct direction but shoots from the center of the gun. How do I move the shooting point to the muzzle?

回答1:

Here's an example of a firing cannon. You just have to pass the current angle and the rect.center of the cannon to the newly created bullet and then rotate its image and velocity vector in the __init__ method.

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')
BG_COLOR = pg.Color('darkseagreen4')


class Bullet(pg.sprite.Sprite):

    def __init__(self, pos, angle):
        super(Bullet, self).__init__()
        self.image = pg.Surface((20, 11), pg.SRCALPHA)
        pg.draw.rect(self.image, pg.Color('grey11'), [0, 0, 13, 11])
        pg.draw.polygon(
            self.image, pg.Color('grey11'), [(13, 0), (20, 5), (13, 10)])
        self.image = pg.transform.rotate(self.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(80, 0).rotate(angle)
        # Use the offset to change the starting position.
        self.pos = Vector2(pos) + offset
        self.velocity = Vector2(5, 0)
        self.velocity.rotate_ip(angle)

    def update(self):
        self.pos += self.velocity
        self.rect.center = self.pos


def main():
    clock = pg.time.Clock()
    # The cannon image and rect.
    surf = pg.Surface((40, 22), pg.SRCALPHA)
    surf.fill(pg.Color('grey27'))
    pg.draw.rect(surf, pg.Color('grey11'), [30, 6, 10, 10])
    orig_surf = surf
    rect = surf.get_rect(center=(320, 240))
    angle = 0  # Angle of the cannon.
    # 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
            if event.type == pg.MOUSEBUTTONDOWN:
                if event.button == 1:  # Left button fires bullet.
                    # Fire a bullet from cannon center with current angle.
                    bullet_group.add(Bullet(rect.center, angle))

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

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

        clock.tick(30)

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

You can also use math.cos and sin to calculate the offset.

run = math.cos(math.radians(angle)) * 80
rise = math.sin(math.radians(angle)) * 80
offset = run, rise


回答2:

I achieved what I needed to do with thefollowing code which is the same as above with a very slight modification which is explained in the code. @skrx answer and the comment by Nick A. aided me to achieve this.

def gun_radar(self):

    for p in self.gameobjects:

        if "R" in p.name or "L" in p.name:

            if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar:  # if hero within radar

                p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
                p.direction = p.orig_direction.rotate(p.current_angle)
                p.orientation = p.vec_to_target.dot(p.direction)

                if p.orientation > 2:
                    p.current_angle += 1
                elif p.orientation < -2:
                    p.current_angle -= 1

                p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)

                p.gun_rotate(-p.current_angle)

                p.timer -= 1  #count down the timer. when zero calculate vector and add bullet to fired_blts

                if p.timer<= 0:

                    w, h = p.rect.center

                    # adjust for the distance fromm the gun center to the gun muzzle

                    w = w + math.cos(math.radians(p.current_angle)) * 28
                    h = h + math.sin(math.radians(p.current_angle)) * 28
                    bullet = Bombullet(bulletimage, w, h)  # create the bullet

                    # calculate the velocity

                    bullet.xvel = bullet.speed * math.cos(math.radians(p.current_angle))
                    bullet.yvel = bullet.speed * math.sin(math.radians(p.current_angle))

                    self.fired_blts.add(bullet)

                    p.timer = 100