2

I'm trying to make a white rectangle rotate like a the hands of a clock in pygame using this code,

import random, pygame, math, sys
from pygame.locals import *
 
Blue = (0,0,255)
Black = (0, 0, 0) 
Green = (0,255,0)
White = (255,255,255)
  
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
  
FPS = 30
fpsClock = pygame.time.Clock()
  
Sail = pygame.Surface([100,10])
Sail.set_colorkey (Black)
Sail.fill(White)
  
degrees = 0
hyp = 100
x = 200
y = 150


while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    adj = 10 * math.cos(degrees)
    opp = 10 * math.sin(degrees)
    dx = adj + 200
    dy = opp + 150
    
    rotatedSail = pygame.transform.rotate(Sail, degrees)
    Sail_rect = Sail.get_rect(topleft = (dx, dy))
    DISPLAYSURF.fill(Blue)
    DISPLAYSURF.blit(rotatedSail, Sail_rect)
    pygame.display.flip()
  
    fpsClock.tick(FPS)
  
    degrees += 1

but the rectangle rotates in a weird way. I would appreciate it if you could keep the suggestion as simple and as close to my code as possible, because I'm just starting to learn. Plus i know it's easier to do it using an image of a rectangle, but I'm trying to use a surface. can anyone help?

1 Answers1

2

You need to get the bounding rectangle of the rotated rectangle and set the center of the rectangle by (x, y) (see also How do I rotate an image around its center using PyGame?):

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
       
    rotatedSail = pygame.transform.rotate(Sail, degrees)
    rotatedSail_rect = rotatedSail.get_rect(center = (x, y))
    DISPLAYSURF.fill(Blue)
    DISPLAYSURF.blit(rotatedSail, rotatedSail_rect)
    pygame.display.flip()
  
    fpsClock.tick(FPS)
  
    degrees += 1

enter image description here


To rotate the object around another point than the center point is much more complicate. A general solution is described in the answer to How can you rotate an image around an off center pivot in PyGame.

Complete Example:

import pygame, math, sys
from pygame.locals import *
 
Blue = (0,0,255)
Black = (0, 0, 0) 
Green = (0,255,0)
White = (255,255,255)
  
pygame.init()
DISPLAYSURF = pygame.display.set_mode((400, 300))
pygame.display.set_caption('Sailing!')
  
FPS = 30
fpsClock = pygame.time.Clock()
  
Sail = pygame.Surface([100,10])
Sail.set_colorkey (Black)
Sail.fill(White)
  
degrees = 0
hyp = 100
x = 200
y = 150

def blitRotate(surf, image, pos, originPos, angle):

    # calcaulate the axis aligned bounding box of the rotated image
    w, h         = image.get_size()
    sin_a, cos_a = math.sin(math.radians(angle)), math.cos(math.radians(angle)) 
    min_x, min_y = min([0, sin_a*h, cos_a*w, sin_a*h + cos_a*w]), max([0, sin_a*w, -cos_a*h, sin_a*w - cos_a*h])

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

    # calculate the upper left origin of the rotated image
    origin = (pos[0] - originPos[0] + min_x - pivot_move[0], pos[1] - originPos[1] - min_y + pivot_move[1])

    # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)

    # rotate and blit the image
    surf.blit(rotated_image, origin)

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

    DISPLAYSURF.fill(Blue)
    blitRotate(DISPLAYSURF, Sail, (x, y), (0, 5), degrees)   
    pygame.display.flip()
    fpsClock.tick(FPS)
    degrees += 1

With this mechanism, a complete analog clock can be realized:

import pygame, datetime
  
pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
  
hour = pygame.Surface((60, 10), pygame.SRCALPHA)
hour.fill((255, 255, 255))
minute = pygame.Surface((80, 6), pygame.SRCALPHA)
minute.fill((255, 255, 0))
second = pygame.Surface((100, 2), pygame.SRCALPHA)
second.fill((255, 0, 0))

def blitRotate(surf, image, origin, pivot, angle):

    # offset from pivot to center
    image_rect = image.get_rect(topleft = (origin[0] - pivot[0], origin[1]-pivot[1]))
    offset_center_to_pivot = pygame.math.Vector2(origin) - image_rect.center
    
    # roatated offset from pivot to center
    rotated_offset = offset_center_to_pivot.rotate(-angle)

    # roatetd image center
    rotated_image_center = (origin[0] - rotated_offset.x, origin[1] - rotated_offset.y)

    # get a rotated image
    rotated_image = pygame.transform.rotate(image, angle)
    rotated_image_rect = rotated_image.get_rect(center = rotated_image_center)

    # rotate and blit the image
    surf.blit(rotated_image, rotated_image_rect)

start_time = pygame.time.get_ticks()
run = True
while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False

    now = datetime.datetime.now()
    angle_hour = 90 - 360 * (now.hour + now.minute/60) / 12
    angle_minute = 90 - 360 * now.minute / 60
    angle_second = 90 - 360 * now.second / 60
    center = window.get_rect().center
    
    window.fill(0)
    blitRotate(window, hour, center, (5, 5), angle_hour) 
    blitRotate(window, minute, center, (5, 4), angle_minute)  
    blitRotate(window, second, center, (5, 2), angle_second) 
    pygame.display.flip()
    clock.tick(100)

pygame.quit()
exit()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174