4

I'm trying to create a laser beam for my starship, this laser beam is a drawn rect and not an image, I move the starship with "w-a-s-d" and aim with the mouse, I created a class "Laser" and I've managed to make it aim in the same direction as the player. In addition to rotating, the beam should also move to stay attached to the ship, but I can not understand how to do it.

class Player:
#inizialize the player
def __init__(self, x, y, player):
    self.x = x
    self.y = y
    self.image = player
    self.is_shooting = False
    self.original_player_image = player
    self.angle = 0

#move the player
def movePlayer(self):
    if key[0]:
        self.x -= 10
    elif key[1]:
        self.x += 10
    if key[2]:
        self.y -= 10
    elif key[3]:
        self.y += 10

    #check borders
    if self.x <= 0:
        self.x = 0
    if self.x + rock_image.get_width() >= display_width:
        self.x = display_width - player_image.get_width()
    if self.y <= 0:
        self.y = 0
    if self.y + player_image.get_height() >= display_height:
        self.y = display_height - player_image.get_height()

#rotate the player where the mouse is aiming
def rotate(self):
    mouse_x, mouse_y = pygame.mouse.get_pos()
    rel_x, rel_y = mouse_x - self.x, mouse_y - self.y
    self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) - 90
    self.image = pygame.transform.rotate(self.original_player_image, int(self.angle))
    self.rect = self.image.get_rect()

#draw the player
def drawPlayer(self):
    screen.blit(self.image, (self.x, self.y))


class Laser:
def __init__(self, player_x, player_y):
    self.x = player_x
    self.y = player_y
    self.original_image = pygame.Surface((5, 150))
    self.original_image.set_colorkey( (0,0,0) )
    self.original_image.fill( (255,0,0) )
    self.copy_image = self.original_image.copy()
    self.copy_image.set_colorkey( (0,0,0) )
    self.rect = self.copy_image.get_rect()
    self.rect.center = self.x + player_image.get_width()//2, self.y - 75
    self.new_image = pygame.Surface((5, 150))

def continueDrawLaser(self):
    if laser_bool:
        screen.blit(self.new_image, self.rect)

def rotate(self):
    mouse_x, mouse_y = pygame.mouse.get_pos()
    rel_x, rel_y = mouse_x - player1.x, mouse_y - player1.y
    angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) - 85
    vel_x = math.cos(angle)
    vel_y = math.sin(angle)
    self.new_image = pygame.transform.rotate(self.original_image, angle)
    self.rect = self.new_image.get_rect()
    self.rect.center = player1.x + vel_x, player1.y + vel_y

First there is the Player class(starship) The second one is the Laser class(laser beam)

This is what I get:

enter image description here

If you need the whole code I posted it here:

https://pastebin.com/jnYifErX

To let you test the code I have eliminated all the useless things and left only the player with the laser beam, so you have to download only the spaceship to test the code,here the shortened code:

https://pastebin.com/VTbuCZY4

Link to download the spaceship:

https://www.flaticon.com/free-icon/spaceship_1114780?term=space%20ship&page=1&position=57

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
C-Gian
  • 62
  • 2
  • 19

1 Answers1

1

In the answer to How do I rotate an image around its center using Pygame? is presented an algorithm to rotate an image around a pivot.

Use the algorithm to implement the function rotate in the class Laser, dependent on the orientation and position of player1

class Laser:
    # [...]

    def rotate(self):

        # get rectangle of player and laser, as if the angle would be 0 
        player_rect = player1.original_player_image.get_rect(topleft = (player1.x, player1.y))
        laser_rect  = self.original_image.get_rect(midbottom = player_rect.midtop)
        self.angle  = player1.angle
        pos         = player_rect.center
        pivotPos    = [pos[0] - laser_rect.x, pos[1] - laser_rect.y]   

        # calcaulate the axis aligned bounding box of the rotated image
        w, h       = self.original_image.get_size()
        box        = [pygame.math.Vector2(p) for p in [(0, 0), (w, 0), (w, -h), (0, -h)]]
        box_rotate = [p.rotate(self.angle) for p in box]
        min_box    = (min(box_rotate, key=lambda p: p[0])[0], min(box_rotate, key=lambda p: p[1])[1])
        max_box    = (max(box_rotate, key=lambda p: p[0])[0], max(box_rotate, key=lambda p: p[1])[1])

        # calculate the translation of the pivot 
        pivot        = pygame.math.Vector2(pivotPos[0], -pivotPos[1])
        pivot_rotate = pivot.rotate(self.angle)
        pivot_move   = pivot_rotate - pivot

        # calculate the upper left origin of the rotated image
        origin = (pos[0] - pivot[0] + min_box[0] - pivot_move[0], pos[1] + pivot[1] - max_box[1] + pivot_move[1])

        # get a rotated image
        self.new_image = pygame.transform.rotate(self.original_image, self.angle)

        # get new rectangle
        self.rect = self.new_image.get_rect(topleft = (round(origin[0]), round(origin[1])))

Further mote, you've to ensure that the rotated spaceship is correctly placed. Calculate the center of the un-rotated spaceship and apply the center to the rotated spaceship. Use the .rect attribute of the spaceship to blit:

class Player:
    # [...]

    # rotate the player where the mouse is aiming
    def rotate(self):
        mouse_x, mouse_y = pygame.mouse.get_pos()
        rel_x, rel_y = mouse_x - self.x, mouse_y - self.y
        self.angle = (180 / math.pi) * -math.atan2(rel_y, rel_x) - 90
        self.image = pygame.transform.rotate(self.original_player_image, int(self.angle))
        orig_center = self.original_player_image.get_rect(topleft = (self.x, self.y)).center
        self.rect = self.image.get_rect(center = orig_center)

    # draw the player
    def drawPlayer(self):
        screen.blit(self.image, self.rect.topleft)     

Of course every time when the player is rotated, then the laser has to be rotated, too:

player1.movePlayer()
player1.rotate()
laser1.rotate()

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • It was more complicated than I thought. I don't know why but it was a bit inaccurate, I solved it by adding +15 to the height in the box variable. When I look up, down, right and left the laser is attached to the ship, when I'm in a corner instead it detaches a bit, how can I make it always attached? Here is what I mean: https://imgur.com/a/UcoEp1A Thanks a lot anyway, you're really good! – C-Gian Jan 01 '20 at 12:11
  • @C-Gian The issue is not the laser, it is the spaceship. You've to correct the location of the spaceship when it is rotated. See the answer. I've done some corrections in `Laser.rotate`, too. – Rabbid76 Jan 01 '20 at 12:56
  • Sorry I don't know why but I totally ignored the central part of the answer, now I modified the codd and everything works perfectly, however I can't understand why the collisions are inaccurate, I edited the post to show the problem. Anyway thanks again! @Rabbid76 – C-Gian Jan 01 '20 at 13:27
  • @C-Gian I added the middle part of the answer later. For the collision it would be better to create a new question. Note, a question should not be to broad. Each question should be about one specific topic. So 1 question for the rotation and a 2nd question for the collision. But note the rotated laser image still covers a axis aligned rectangle. If the laser is at 45° then the start of the laser is in on corner of the image and the end is at the diagonally opposite corner. – Rabbid76 Jan 01 '20 at 13:30