1

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?

Community
  • 1
  • 1
emorphus
  • 550
  • 9
  • 20
  • Are you converting your angle to radians? – Nick is tired Feb 16 '17 at 17:34
  • I changed degrees into radians and then the bullets fire in the correct direction but still from the same place of the gun. The rect.midright. The modified code 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) – emorphus Feb 16 '17 at 17:48
  • Please edit your post and show us a minimal, complete example that we can run. – skrx Feb 16 '17 at 18:05
  • @emorphus you need to calculate the position of the muzzle of the gun using the center of the rect, the length of thr barrel and the angle then set the starting position as there – Nick is tired Feb 16 '17 at 18:16
  • I edited the code to use the center of the gun as the starting point. Then which ever way the gun rurned the bullet always came from the center and shot out of the barrel. I edited the code above to show this. How do i get the bullet to shoot from the muzzle point at the end of the gun? – emorphus Feb 16 '17 at 18:26
  • The above code I posted is from a large project which is broken into several files. To make a singe file working code will take me a few hours so I have posted it to my bitbucket account. Please look at the code if time permits.link is --> https://bitbucket.org/diliupg/platform_survivor. – emorphus Feb 16 '17 at 18:47

2 Answers2

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
skrx
  • 19,980
  • 5
  • 34
  • 48
  • I've edited the answer to demonstrate how you can offset the start position with another vector. – skrx Feb 16 '17 at 18:29
1

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
emorphus
  • 550
  • 9
  • 20