1

In pygame I have an image object of a frigate at 0 degrees of rotation. On it i have turrets, i need to calculate their new position if the frigate rotates by say 90 degrees.

After rotating the image like so,

rotatedFrigate  = pygame.transform.rotate(gui.frigate[0], facingAngle)

I have tried various ways such as rotating the point,

point         = pygame.math.Vector2(turretx, turretY)
rotated_point = point.rotate(facingAngle)

Even adding on the original x,y coords still has it far off

t1x,t1y = rotated_point[0]+point[0], rotated_point[1]+point[1]

I have also tried rotation matrix approach using midpoint and adding new adjusted dims.

xm,ym = self.x + 0.5*self.w,self.y + 0.5*self.h
a = math.radians(facingAngle)  # Convert to radians
xr = (x - xm) * math.cos(a) - (y - ym) * math.sin(a) + xm
yr = (x - xm) * math.sin(a) + (y - ym) * math.cos(a) + ym
rotatedFrigate       = pygame.transform.rotate(gui.frigate[0], facingAngle)
t1x,t1y             = xr + 0.5*rotatedFrigate.get_width(),yr+ 0.5*rotatedFrigate.get_height()

For the turret :

turretx, turretY               = self.x,self.y+0.05*self.h

Self refers to the frigate coords prior to rotation

Frigate image center coordinates are calculated using

xm,ym = self.x + 0.5*self.w,self.y + 0.5*self.h

Where w & h are used on the frigate image get_width() get_height() methods.

Again prior to rotation.

Both approaches don't seem to work, sometimes they are close but most of the times they are far out.

Additional Info

Picture is if i use rotated_point = (point - pivot).rotate(-facingAngle) + pivot

enter image description here

Murchie85
  • 815
  • 2
  • 12
  • 27
  • ... see [How to set the pivot point (center of rotation) for pygame.transform.rotate()?](https://stackoverflow.com/questions/15098900/how-to-set-the-pivot-point-center-of-rotation-for-pygame-transform-rotate/69312319#69312319) and [How to make my rectangle rotate with a rotating sprite](https://stackoverflow.com/questions/65622705/how-to-make-my-rectangle-rotate-with-a-rotating-sprite/65640631?noredirect=1) – Rabbid76 Jan 28 '23 at 14:12
  • In the second example, pivot point is the center of the image (original) using x,y, and img.get_width()...etc Also second example, i'm using new rotated dims to add on to image. First example, i'm simply adding on the original x,y coords (though I have a feeling this is wrong) – Murchie85 Jan 28 '23 at 14:17
  • For the turret : turretx, turretY = self.x,self.y+0.05*self.h Self refers to the frigate dimensions prior to rotation Frigate image center coordinates are calculated using xm,ym = self.x + 0.5*self.w,self.y + 0.5*self.h Where w & h are used on the frigate image `get_width() get_height()` methods. – Murchie85 Jan 28 '23 at 14:23

1 Answers1

1

I suggest to use pygame.math.Vector2.rotate(). The following works if the image is rotated around a pivot. See How do I rotate an image around its center using PyGame? and How to set the pivot point (center of rotation) for pygame.transform.rotate()? .

  • Calculate the vector from the pivot to the point
  • Rotate the vector with pygame.math.Vector2.rotate()
  • Add the pivot to the rotated vector
point = pygame.math.Vector2(turretx, turretY)
pivot = pygame.math.Vector2(self.x + 0.5*self.w, self.y + 0.5*self.h)

rotated_point = (point - pivot).rotate(-facingAngle) + pivot

t1x, t1y = rotated_point.x, rotated_point.y

Note that you need to rotate the vector by the negative angle. While pygame.transform.rotate works clockwise, pygame.math.Vector2.rotate() works counterclockwise.


In the following minimal example the pivot point is marked with the blue cross and the rotating point with the green cross. The rotated vector is the blue line between the blue cross and the green cross.

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

image_size = (160, 80)
point_on_image = (130, 40)
image = pygame.Surface(image_size, pygame.SRCALPHA)
pygame.draw.ellipse(image, "gray", (0, 0, *image_size)) 
pygame.draw.circle(image, "red", point_on_image, 10)
angle = 0

run = True
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 
    
    window_center = window.get_rect().center
    rotated_image = pygame.transform.rotate(image, angle)

    px = window_center[0] - image_size[0]//2 + point_on_image[0]
    py = window_center[1] - image_size[1]//2 + point_on_image[1]
    point = pygame.math.Vector2(px, py)
    pivot = pygame.math.Vector2(window_center)
    rotated_point = (point - pivot).rotate(-angle) + pivot

    window.fill("white")
    window.blit(rotated_image, rotated_image.get_rect(center = window_center))
    pygame.draw.line(window, "blue", (window_center[0]-15, window_center[1]), (window_center[0]+15, window_center[1]), 3)
    pygame.draw.line(window, "blue", (window_center[0], window_center[1]-15), (window_center[0], window_center[1]+15), 3)
    pygame.draw.line(window, "green", (rotated_point[0]-15, rotated_point[1]), (rotated_point[0]+15, rotated_point[1]), 3)
    pygame.draw.line(window, "green", (rotated_point[0], rotated_point[1]-15), (rotated_point[0], rotated_point[1]+15), 3)
    pygame.draw.line(window, "blue", window_center, rotated_point, 3)
    pygame.display.flip()

    angle +=1

pygame.quit()
exit()

If you do not rotate the image around its center, but only keep the position at the top left, you must:

  • Calculate the vector from the center of the original image to the point
  • Rotate the vector with pygame.math.Vector2.rotate()
  • Add the center of the rotated image to the rotated vector
point = pygame.math.Vector2(turretx, turretY)
pivot = pygame.math.Vector2(self.x + 0.5*self.w, self.y + 0.5*self.h)

rotatedFrigate = pygame.transform.rotate(gui.frigate[0], facingAngle)
new_pivot = pygame.math.Vector2(
    self.x + 0.5 * rotatedFrigate.get_width(),
    self.y + 0.5 * rotatedFrigate.get_height())

rotated_point = (point - pivot).rotate(-facingAngle) + new_pivot

t1x, t1y = rotated_point.x, rotated_point.y

Minimal example:

import pygame

pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

image_size = (160, 80)
point_on_image = (130, 40)
image = pygame.Surface(image_size, pygame.SRCALPHA)
pygame.draw.ellipse(image, "gray", (0, 0, *image_size)) 
pygame.draw.circle(image, "red", point_on_image, 10)
angle = 0

run = True
while run:
    clock.tick(100)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False 
    
    window_center = window.get_rect().center
    image_rect = image.get_rect(center = window_center)
    rotated_image = pygame.transform.rotate(image, angle)

    px = window_center[0] - image_size[0]//2 + point_on_image[0]
    py = window_center[1] - image_size[1]//2 + point_on_image[1]
    point = pygame.math.Vector2(px, py)
    pivot = pygame.math.Vector2(window_center)
    rotated_image_rect = rotated_image.get_rect(topleft = image_rect.topleft)
    rotated_image_center = rotated_image_rect.center
    rotated_point = (point - pivot).rotate(-angle) + rotated_image_center

    window.fill("white")
    window.blit(rotated_image, rotated_image_rect)
    pygame.draw.rect(window, "black", rotated_image_rect, 3)
    pygame.draw.line(window, "blue", (rotated_image_center[0]-15, rotated_image_center[1]), (rotated_image_center[0]+15, rotated_image_center[1]), 3)
    pygame.draw.line(window, "blue", (rotated_image_center[0], rotated_image_center[1]-15), (rotated_image_center[0], rotated_image_center[1]+15), 3)
    pygame.draw.line(window, "green", (rotated_point[0]-15, rotated_point[1]), (rotated_point[0]+15, rotated_point[1]), 3)
    pygame.draw.line(window, "green", (rotated_point[0], rotated_point[1]-15), (rotated_point[0], rotated_point[1]+15), 3)
    pygame.draw.line(window, "blue", rotated_image_center, rotated_point, 3)
    pygame.display.flip()

    angle +=1

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you, I know from looking at it, it seems the correct answer but for some reason my turret isn't playing ball, its not even going round in a predictable but offset trajectory. Trying to work out what it is i could be missing – Murchie85 Jan 28 '23 at 21:49
  • point = pygame.math.Vector2(self.turretOne.x,self.turretOne.y) pivot = pygame.math.Vector2(self.x + gui.frigate[0].get_width(), self.y + 0.5*gui.frigate[0].get_height()) rotatedFrigate = pygame.transform.rotate(gui.frigate[0], facingAngle) new_pivot = pygame.math.Vector2(self.x + 0.5 * rotatedFrigate.get_width(), self.y + 0.5 * rotatedFrigate.get_height()) rotated_point = (point - pivot).rotate(facingAngle) + new_pivot self.turretOne.x,self.turretOne.y = rotated_point.x, rotated_point.y – Murchie85 Jan 28 '23 at 21:50
  • @Murchie85 See the answer: ***"Note that you need to rotate the vector by the negative angle. While [`pygame.transform.rotate`](https://www.pygame.org/docs/ref/transform.html#pygame.transform.rotate) works clockwise, [`pygame.math.Vector2.rotate()`](https://www.pygame.org/docs/ref/math.html#pygame.math.Vector2.rotate) works counterclockwise."*** - so `rotated_point = (point - pivot).rotate(-facingAngle) + new_pivot` – Rabbid76 Jan 28 '23 at 21:56
  • Yes I tried, this, and whilst the turret remains mostly on the screen, it's position still dances about unpredictably if i rotate in 45 degree increments. Does it matter if the angles are in degrees/radians? Currently im using degrees and ensuring i wrap round if it exceeds 360. – Murchie85 Jan 28 '23 at 22:13
  • This is in fact the correct answer, sorry for not getting back around to it sooner - it took me time to validate what I was doing wrong. My problem is I was using the same x,y variables for referencing the original turret x,y coords and also udpating it. The original X,Y variable should always be in reference to the anchor point (in this case the frigate), because i kept referencing the same point over and over again, it kept it a moving target. – Murchie85 Feb 08 '23 at 11:42