-2

I have been trying to make the bullets come out from a specific point on my ship (a pixel ship, where I have drawn a "cannon"). It works when the ship is in a specific angle, but as soon as the player rotate the ship, the bullets start appearing further and further away from the cannon.

I have been trying to work out the math behind fixing this, but cannot for the life of me figure it out. I have tried playing with offset values within the Bullet class, adding it to the blit_rotate_center function as arguments for the x and y values. Everything I do seem to make it worse.

    new_image = pygame.transform.rotate(image, angle)
    rotated_image = pygame.mask.from_surface(new_image)
    new_rect = rotated_image.get_rect(center = image.get_rect(topleft = top_left).center)
    
    
    win.blit(rotated_image.to_surface(unsetcolor=(0, 0, 0, 0), setcolor= (255, 255, 255,)), new_rect.topleft)
    win.blit(new_image, new_rect.topleft)
    return new_rect

class PlayerShip:
    def __init__(self):
        self.img = assets.SPACE_SHIP_1
        self.max_vel = 3
        self.vel = 0
        self.acceleration = 0.01
        self.rotation_vel = 0.0
        self.rotation_max_vel = 0.6
        self.rotation_acc = 0.01
        self.angle = 270
        self.x = 400
        self.y = 400
        self.drag = 0.004
        self.rotation_drag = 0.0025
        self.vertical = 0
        self.horizontal = 0
        self.cooldown = 240
        self.max_cooldown = 240
        self.rect = self.img.get_rect()

        # Weapons data
        self.salvo = 3

        
    
    def rotate(self, left = False, right = False):
        if left:
            self.rotation_vel += self.rotation_acc
            if self.rotation_vel > self.rotation_max_vel:
                self.rotation_vel = self.rotation_max_vel
        elif right:
            self.rotation_vel -= self.rotation_acc
            if self.rotation_vel < self.rotation_max_vel - self.rotation_max_vel * 2:
                self.rotation_vel = self.rotation_max_vel - self.rotation_max_vel * 2
    
    def draw(self, win):
        self.rect = blit_rotate_center(win, self.img, (self.x, self.y), self.angle)
        self.angle += self.rotation_vel
        # Moving the player according to angle
        radians = math.radians(self.angle)
        self.vertical = math.cos(radians) * self.vel
        self.horizontal = math.sin(radians) * self.vel
        self.y -= self.vertical
        self.x -= self.horizontal
        self.reduce_speed()

        #Updating the cooldown
        self.cooldown -= 1
        if self.cooldown <= 0:
            self.cooldown = 0

    def key_input(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_a]:
            self.rotate(left = True)
        if keys[pygame.K_d]:
            self.rotate(right = True)
        if keys[pygame.K_w]:
            self.move_forward()
        if keys[pygame.K_s]:
            self.move_backward()
    
    def move_forward(self):
        self.img = assets.SPACE_SHIP_1_THRUST
        self.vel = min(self.vel + self.acceleration, self.max_vel)

    def move_backward(self):
        self.vel = min(self.vel - self.acceleration, self.max_vel)
        if self.vel < self.max_vel - self.max_vel * 2:
            self.vel = self.max_vel - self.max_vel * 2
    
    def reduce_speed(self):
        if self.vel < 0:
            self.vel += self.drag
        elif self.vel > 0:
            self.vel -= self.drag
        if self.rotation_vel < 0:
            self.rotation_vel += self.rotation_drag
        elif self.rotation_vel > 0:
            self.rotation_vel -= self.rotation_drag




class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, player_angle, player_rect):
        self.speed = 10
        self.angle = player_angle
        self.image = assets.BULLET_1

        ship_center_x = player_rect[0] + player_rect[2] // 2
        ship_center_y = player_rect[1] + player_rect[3] // 2

        self.x = ship_center_x
        self.y = ship_center_y

        self.rect = self.image.get_rect()
        radians = math.radians(self.angle - 90)
        self.vertical = math.cos(radians) * self.speed
        self.horizontal = math.sin(radians) * self.speed


        self.x_offset = self.vertical
        self.y_offset = self.horizontal
    
    
    def draw(self, win):
        blit_rotate_center(win, self.image, (self.x - self.x_offset, self.y - self.y_offset), self.angle)
        radians = math.radians(self.angle - 90)
        self.vertical = math.cos(radians) * self.speed
        self.horizontal = math.sin(radians) * self.speed
        self.y -= self.vertical
        self.x -= self.horizontal

1 Answers1

-2

The problem here is that you're not correctly calculating the offset from the center of the ship to the cannon for different angles. In a rotated coordinate system, the x and y offsets change depending on the angle. Therefore, you have to take the rotation of the ship into account when determining the position of the cannon.

To do this, you should:

  1. Determine the offset of the cannon from the center of the ship in the ship's local coordinate system. This will be a constant pair of x, y values.
  2. Rotate this offset by the same angle as the ship to get the offset in the global (screen) coordinate system. This can be done using a simple rotation matrix.
  3. Add this rotated offset to the center of the ship to get the global position of the cannon.

Here's how you can modify your Bullet class to implement this:

class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y, player_angle, player_rect):
        self.speed = 10
        self.angle = player_angle
        self.image = assets.BULLET_1

        ship_center_x = player_rect[0] + player_rect[2] // 2
        ship_center_y = player_rect[1] + player_rect[3] // 2

        # Step 1: Define the offset from the center of the ship to the cannon in the ship's local coordinates.
        # These values depend on how your ship is drawn.
        local_cannon_offset_x = 0
        local_cannon_offset_y = -20  # Assuming the cannon is 20 pixels above the center of the ship.

        # Step 2: Rotate the local offset by the same angle as the ship to get the offset in the global coordinate system.
        # We use the standard rotation matrix here.
        radians = math.radians(self.angle)
        global_cannon_offset_x = local_cannon_offset_x * math.cos(radians) - local_cannon_offset_y * math.sin(radians)
        global_cannon_offset_y = local_cannon_offset_x * math.sin(radians) + local_cannon_offset_y * math.cos(radians)

        # Step 3: Add the global offset to the center of the ship to get the position of the cannon.
        self.x = ship_center_x + global_cannon_offset_x
        self.y = ship_center_y + global_cannon_offset_y

        self.rect = self.image.get_rect()
        radians = math.radians(self.angle - 90)
        self.vertical = math.cos(radians) * self.speed
        self.horizontal = math.sin(radians) * self.speed

    def draw(self, win):
        blit_rotate_center(win, self.image, (self.x, self.y), self.angle)
        radians = math.radians(self.angle - 90)
        self.vertical = math.cos(radians) * self.speed
        self.horizontal = math.sin(radians) * self.speed
        self.y -= self.vertical
        self.x -= self.horizontal
Macto24
  • 79
  • 6