1

I am working on a game, where the player inputs a graph f(x) = ... and it draws the graph. All that works seamlessly. A Rocket should follow this path and rotate, so it stays on it. I am using math.degrees(math.atan2(-dy, dx)) to find the angle between 2 points which it should rotate to. I rotate the image with:

def blit_rotate_center(self, surf, topleft, rot_angle):
    rotated_image = pygame.transform.rotate(self.image, rot_angle)
    new_rect = rotated_image.get_rect(center=self.image.get_rect(topleft=topleft).center)
    surf.blit(rotated_image, new_rect)
    self.rect = new_rect

It does work somewhat, but there is a lot of weird and glitchy rotation.

thanks in advance! The Main loop:

while True:
for event in pygame.event.get():
    if event.type == pygame.QUIT:
        print(easy_high_score, med_high_score, hard_high_score)
        pygame.quit()
        sys.exit()

    if event.type == pygame.MOUSEBUTTONDOWN:
        if input_rect.collidepoint(event.pos):
            box_active = True
        else:
            box_active = False

        if launch_rect.collidepoint(event.pos) and not box_active:
            launch = True
            launch_active = True
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE:
            box_active = False
        if box_active:
            if event.key == pygame.K_BACKSPACE:
                if user_text == "Invalid Input":
                    user_text = ""
                else:
                    user_text = user_text[:-1]
                pt_list = []
                coord_list = []
                draw = False
            elif event.key == pygame.K_RETURN:
                draw = True
                if user_text == "Alim":
                    end_planet_pos = (1920, 540)
                    start_planet_pos = (0, 540)
                    cp_pos = (960, 540)
            else:
                user_text += event.unicode
                pt_list = []
                coord_list = []

screen.blit(bg_list[bg_index], (0, 0))
start.blit_start(start_planet_pos, (500, 500))
end.blit_start(end_planet_pos, (500, 500))

if difficulty == 0:
    enemy0.blit_start(enemy0_pos, (200, 200))
    if pygame.sprite.collide_mask(enemy0, checkpoint):
        enemy0_pos = (random.randint(200, 1720), random.randint(200, 880))

if difficulty == 1:
    enemy0.blit_start(enemy0_pos, (200, 200))
    enemy1.blit_start(enemy1_pos, (200, 200))
    if pygame.sprite.collide_mask(enemy0, checkpoint):
        enemy0_pos = (random.randint(200, 760), random.randint(200, 880))
    if pygame.sprite.collide_mask(enemy1, checkpoint):
        enemy1_pos = (random.randint(1160, 1720), random.randint(200, 880))
if difficulty == 2:
    enemy0.blit_start(enemy0_pos, (200, 200))
    enemy1.blit_start(enemy1_pos, (200, 200))
    enemy2.blit_start(enemy2_pos, (200, 200))
    if pygame.sprite.collide_mask(enemy0, checkpoint):
        enemy0_pos = (random.randint(200, 760), random.randint(200, 880))
    if pygame.sprite.collide_mask(enemy1, checkpoint):
        enemy1_pos = (random.randint(1160, 1720), random.randint(200, 880))
    if pygame.sprite.collide_mask(enemy2, checkpoint):
        enemy2_pos = (random.randint(200, 1720), random.randint(200, 880))

if not thru_cp:
    checkpoint.blit_cp(cp_pos)
if pygame.sprite.collide_mask(enemy0, checkpoint):
    enemy0_pos = (random.randint(200, 1720), random.randint(200, 880))

# draws the coordinate system
pygame.draw.line(screen, (255, 255, 255), (960, 0), (960, 1080), 2)
pygame.draw.line(screen, (255, 255, 255), (0, 540), (1920, 540), 2)

for numbers in range(0, 64):
    pygame.draw.aaline(screen, (255, 255, 255), (numbers * 30, 530), (numbers * 30, 550))

for numbers in range(0, 36):
    pygame.draw.aaline(screen, (255, 255, 255), (950, numbers * 30), (970, numbers * 30))

if box_active:
    inp_rect_color = color_active
else:
    inp_rect_color = color_passive

pygame.draw.rect(screen, inp_rect_color, input_rect, 2)
inp_text_surface = base_font.render(fixed_text_inp_box + user_text, False, (255, 255, 255))
input_rect.w = max(100, inp_text_surface.get_width() + 10)
screen.blit(inp_text_surface, (input_rect.x + 5, input_rect.centery - 5))

launch_button.blit_button((1820, 55), launch_active)

if len(coord_list) > 1:
    try:
        pygame.draw.aalines(screen, (255, 255, 255), False, coord_list, 3)
    except (ValueError, SyntaxError, NameError, TypeError):
        user_text = "Invalid Input"
if draw:
    try:
        coord_list = get_pt_list(user_text)
        prev_coord = coord_list[0]
        for coord in coord_list:
            pygame.draw.aaline(screen, (255, 255, 255), prev_coord, coord)
            pygame.display.flip()
            pygame.time.delay(1)
            prev_coord = coord
    except (ValueError, SyntaxError, NameError, TypeError):
        user_text = "Invalid Input"
    draw = False

if launch:
    try:
        if not thru_enemy:
            y1 = int(coord_list[coord_index][1])
            y2 = int(coord_list[coord_index + 1][1])
            x1 = int(coord_list[coord_index][0])
            x2 = int(coord_list[coord_index + 1][0])
            dy = y1 - y2
            dx = x2 - x1
            angle = math.degrees(math.atan2(dy, dx))
            custom_round(angle, 5)
            angle %= 360
            center = (round(int(coord_list[coord_index][0])), round(int(coord_list[coord_index][1])))
            rocket.blit_rotate_center(screen, center, angle)

            if pygame.sprite.collide_mask(start, rocket):
                thru_start = True
            if pygame.sprite.collide_mask(rocket, end):
                thru_end = True
            if pygame.sprite.collide_mask(checkpoint, rocket):
                thru_cp = True
            if pygame.sprite.collide_mask(rocket, enemy0):
                thru_enemy = True
                print("dead")
            if difficulty > 0:
                if pygame.sprite.collide_mask(rocket, enemy1):
                    thru_enemy = True
                    print("dead")
            if pygame.sprite.collide_mask(rocket, enemy2) and difficulty == 2:
                thru_enemy = True
                print("dead")

            pygame.display.flip()
            rocket.update()

            angle = 0
            coord_index += 20


    except IndexError:
        launch_complete = True
    if thru_enemy:
        launch_complete = True

    if launch_complete:
        coord_index = 0
        launch = False
        launch_active = False
        draw_rocket = False
        launch_complete = False
        if thru_start and thru_end and thru_cp and not thru_enemy:
            win = True
        else:
            thru_start = False
            thru_end = False
            thru_enemy = False
            thru_cp = False
        if win:
            thru_cp = False
            thru_enemy = False
            thru_end = False
            thru_start = False

            end_planet_pos = (1920, random.randint(0, 980))
            start_planet_pos = (0, random.randint(0, 980))
            cp_pos = (random.randint(300, 1620), random.randint(200, 880))
            bg_index = random.randint(0, 3)
            start_exo = end_exo
            end_exo = random.randint(0, 12)
            if start_exo == end_exo:
                if end_exo == 12:
                    end_exo -= 1
                else:
                    end_exo -= 1
            start = Obstacle(exo_list[start_exo], (500, 500))
            end = Obstacle(exo_list[end_exo], (500, 500))
            enemy_index1 = random.randint(0, 3)
            enemy_index2 = random.randint(0, 3)
            enemy_index3 = random.randint(0, 3)
            if enemy_index1 == enemy_index2 == enemy_index3:
                enemy_index2 += 1
            enemy0_pos = (random.randint(200, 1720), random.randint(200, 880))
            enemy1_pos = (random.randint(200, 1720), random.randint(200, 880))
            enemy2_pos = (random.randint(200, 1720), random.randint(200, 880))
            pt_list = []
            coord_list = []
            if difficulty == 0:
                easy_score += 1
                if easy_score > easy_high_score:
                    easy_high_score = easy_score
                    with open('easy_score.dat', 'wb') as file:
                        pickle.dump(easy_high_score, file)

            if difficulty == 1:
                med_score += 1
                if med_score > med_high_score:
                    med_high_score = med_score
                    with open("med_score.dat", "wb") as file:
                        pickle.dump(med_high_score, file)

            if difficulty == 2:
                hard_score += 1
                if hard_score > hard_high_score:
                    hard_high_score = hard_score
                    with open("hard_score.dat", "wb") as file:
                        pickle.dump(hard_high_score, file)

pygame.display.flip()
clock.tick(60)

Link to footage of the game: https://youtu.be/lkKxZicoOk8

  • 1
    See this [answer](https://stackoverflow.com/a/49413006/2280890) for an example of rotation. It's also beneficial to keep a copy of the original image and rotate _fresh_ to prevent accumulation of image artifacts. – import random May 08 '22 at 10:18
  • 1
    @importrandom See [How do I rotate an image around its center using PyGame?](https://stackoverflow.com/questions/4183208/how-do-i-rotate-an-image-around-its-center-using-pygame/54714144#54714144) – Rabbid76 May 08 '22 at 10:25
  • 1
    Thanks @Rabbid76 that's an awesome and comprehensively definitive answer on image rotation in pygame. – import random May 08 '22 at 11:05
  • I already use the function they use. @Rabbid76 – Christopher Löwenstein May 08 '22 at 13:10
  • This fuction works fine. If your code does not work it is not because of the function. Likely you usw it in a wrong way. – Rabbid76 May 08 '22 at 13:32
  • Likely `dx` and `dy` are incorrect. (`dx`, `dy`) is a Vector that points in the direction of movement. Unfortunately this part of the code is missing. You have to add the code to the question. Links to external resources tend to break or the content may change. – Rabbid76 May 08 '22 at 15:00
  • ... see [How to know the angle between two vectors?](https://stackoverflow.com/questions/42258637/how-to-know-the-angle-between-two-vectors/64563327#64563327). – Rabbid76 May 08 '22 at 16:45

1 Answers1

1

Likely the problem is that the rect attribute is modified in the blit_rotate_center method.

self.rect = new_rect

Since you're using the rect attribute to calculate the top-left corner of the image, this can result in a feedback loop:

rocket.blit_rotate_center(screen, 
   (int(coord_list[coord_index][0]) - rocket.rect.w / 2,
    int(coord_list[coord_index][1]) - rocket.rect.h / 2), angle)

Simplify your code. Use a rotation method that uses the center of the image instead of the top left:

def blit_rotate_center(self, surf, center, rot_angle):
    rotated_image = pygame.transform.rotate(self.image, rot_angle)
    new_rect = rotated_image.get_rect(center=center)
    surf.blit(rotated_image, new_rect)
    self.rect = new_rect

And pass the center of the object to the method:

center = (round(coord_list[coord_index][0]), round(coord_list[coord_index][1]))
rocket.blit_rotate_center(screen, center, angle)   

See also How to know the angle between two vectors? and *How do I rotate an image around its center using PyGame?.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • @ChristopherLöwenstein anyway, the Problem ist mit the `blit_rotate_center` function. Unfortunately all the other code is Missing. – Rabbid76 May 08 '22 at 14:47
  • @ChristopherLöwenstein I changed the answer. However, since you don't explain what exactly the problem is, it's very difficult to answer your question. – Rabbid76 May 08 '22 at 17:04