Search code examples
pythonrotationpygamesurfacerect

Pygame Rotate Rectangles within Surface


I'm having trouble keeping track of two rectangles that I blit onto a surface after I then rotate that surface. There are three rectangles draw on to the surface, representing the player. As the left and right keys are pressed, the rectangles all rotate around the centre point correctly. When the space bar is pressed, the "lights" are supposed to toggle on and off, however they are always redrawn at the bottom of the surface. Can you advise what I'm doing wrong?

import pygame
from pygame.locals import *


class Player(pygame.sprite.Sprite):
    height = 48
    width = 48
    colour = (30,144,255)

    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.Surface((self.width, self.height))
        self.surf.fill((0, 0, 0))
        self.rect = self.surf.get_rect(center = (screen_width / 2, screen_height / 2))
        self.rotation = 0
        self.PlayerBody = pygame.draw.rect(self.surf, self.colour, Rect(15, 15, 20, 35))
        self.PlayerLightLeft = pygame.draw.rect(self.surf, (125, 0, 0), Rect(19, 45, 4, 4))
        self.PlayerLightRight = pygame.draw.rect(self.surf, (125, 0, 0), Rect(27, 45, 4, 4))
        self.lights = False
        self.lightsOnColour = (255, 0, 0)
        self.lightsOffColour = (125, 0, 0)
        self.lightsColour = self.lightsOffColour

    def update(self, pressedKey):
        if pressedKey == pygame.K_RIGHT:
            self.surf = pygame.transform.rotate(self.surf, -90)
        if pressedKey == pygame.K_LEFT:
            self.surf = pygame.transform.rotate(self.surf, 90)
        if pressedKey == pygame.K_SPACE:
            if self.lights:
                self.lightsColour = self.lightsOffColour
                self.lights = False
            else:
                self.lightsColour = self.lightsOnColour
                self.lights = True
        # always draws rectangles at the bottom of the surface
            self.PlayerLightLeft = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightLeft)
            self.PlayerLightRight = pygame.draw.rect(self.surf, self.lightsColour, self.PlayerLightRight)


# initialize pygame
pygame.init()

screen_width = 800
screen_height = 600

screen = pygame.display.set_mode((screen_width, screen_height))
background = pygame.Surface(screen.get_size())
background.fill((255, 255, 255))

Player = Player()

running = True

while running:
    pressedKey = pygame.K_0
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
            pressedKey = event.key
        elif event.type == QUIT:
            running = False
    screen.blit(background, (0, 0))
    Player.update(pressedKey)
    screen.blit(Player.surf, Player.rect)

    pygame.display.flip()

# end of game, quit
pygame.quit()

Solution

  • Don't rotate the player surface when a key is presse, but add an attribute angle to the class Player, which stores the current angle of the surface.
    Change the angle when the K_RIGHT or K_LEFT key is pressed.

    class Player(pygame.sprite.Sprite):
        height = 48
        width = 48
        colour = (30,144,255)
    
        def __init__(self):
            super(Player, self).__init__()
    
            # [...]
    
            self.angle = 0
    
        def update(self, pressedKey):
            if pressedKey == pygame.K_RIGHT:
                self.angle = (self.angle - 90) % 360
            if pressedKey == pygame.K_LEFT:
                self.angle = (self.angle + 90) % 360
            # [...] 
    

    this causes that the original surface is never changed and changing the "lights" will always work.

    Create a rotated surface, which is rotated by Player.angle and blit the rotated surface:

    rotSurf = pygame.transform.rotate(Player.surf, Player.angle)
    screen.blit(rotSurf, Player.rect)