1

I've been banging my head on this for a couple days now. I'm trying to rotate a sprite on its' center axis and have been referencing this previous post about the same issue:

How do I rotate an image around its center using Pygame?

I'm using the exact same code as described in the previous post and also utilizing a image_clean attribute as to not distort the image on successive rotations. When I try and rotate my sprite the center of the image oscillates back and forth between two points.

def rotate_center(image, angle):
    """rotate a Surface, maintaining position."""
    loc = image.get_rect().center  #rot_image is not defined 
    rot_img = pg.transform.rotate(image, angle)
    print('')
    print('>>> before: ' + str(rot_img.get_rect().center))
    rot_img.get_rect().center = loc
    print('>>> after: ' + str(rot_img.get_rect().center))
    return rot_img

print statements verify that the line:

rot_sprite.get_rect().center = loc

Doesn't seem to assign the loc value to the image center. The output is always like this:

>>> before: (32, 32)
>>> after: (32, 32)

>>> before: (37, 37)
>>> after: (37, 37)

>>> before: (41, 41)
>>> after: (41, 41)

>>> before: (43, 43)
>>> after: (43, 43)

>>> before: (45, 45)
>>> after: (45, 45)

>>> before: (45, 45)
>>> after: (45, 45)

>>> before: (43, 43)
>>> after: (43, 43)

>>> before: (41, 41)
>>> after: (41, 41)

>>> before: (37, 37)
>>> after: (37, 37)

I always want the center to be at (32, 32) (I believe). Any help would be greatly appreciated.

Code:

def main():
    pg.init()

# Screen Setup
size = (SCREEN_WIDTH, SCREEN_HEIGHT)
screen = pg.display.set_mode(size)
screen_flags = screen.get_flags()
pg.display.set_caption(GAME_NAME)
done = False
clock = pg.time.Clock()

# SPRITE GROUPS
all_sprites = pg.sprite.Group()
turrets = pg.sprite.Group()

turret = Tower2((SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
turrets.add(turret)
all_sprites.add(turret)

# -------- Main Program Loop -----------
while not done:
    # Process Incoming Events
    for event in pg.event.get():
        if event.type == pg.QUIT:
            done = True
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_q:
                done = True
            if event.key == pg.K_f:
                if screen_flags & pg.FULLSCREEN == False:
                    screen_flags |= pg.FULLSCREEN
                    pg.display.set_mode(size, screen_flags)
                else:
                    screen_flags ^= pg.FULLSCREEN
                    pg.display.set_mode(size, screen_flags)


    turret.angle += 10
    turret.image = rotate_center(turret.image_clean, turret.angle)


    screen.fill(WHITE)
    all_sprites.draw(screen)
    pg.display.flip()
    clock.tick(15)

pg.quit()


class Tower2(pg.sprite.Sprite):
    def __init__(self, pos, image, tRange, attackSpeed=1):
        super().__init__()
        self.image = image
        self.image_clean = image
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.range = tRange
        self.attackSpeed = attackSpeed
        self.lastAttack = time.time()
        self.angle = 0

    def __iter__(self):
        return self

    def canAttack(self):
        if time.time() - self.lastAttack > self.attackSpeed:
            return True
        else:
            return False

    def rotate(self):
        pass

    def update(self):
        self.angle += 10
        self.image = pg.transform.rotate(self.image_clean, self.angle)
        self.image.get_rect().center = self.image_clean.get_rect().center



class Turret1(Tower2):
    def __init__(self, pos):    
        TOWER_WIDTH = 64
        TOWER_HEIGHT = 64
        INIT_RANGE = 200
        INIT_RATE = 1
        SPEED = 1
        tower_img = pg.image.load('imgs/test_rotate.png')
        tower_img = pg.transform.scale(tower_img, (TOWER_WIDTH, TOWER_HEIGHT))
        super().__init__(pos, tower_img, INIT_RANGE, SPEED)
Brand0R
  • 1,413
  • 11
  • 17
  • 1
    don't use `get_rect()` all the time because it always creates new rectange - and you change value in new rectangel not in original one. – furas Dec 05 '17 at 02:40

2 Answers2

2

Don't use get_rect() all the time - it always creates new rectangle and you change position in this new rectangle, not in original one. And later you get again new rectangle to display position.

Besides image doesn't keep position you have to get rectangle once and use it to keep position.

loc = image.get_rect().center

rot_img = pg.transform.rotate(image, angle)

# get rectangle and keep it
rot_img_rect = rot_img.get_rect()

# change position in rectangle
rot_img_rect.center = loc

# display position 
print('>>> after: ', rot_img_rect.center)

# return image and position in `rot_img_rect`
return rot_img, rot_img_rect
furas
  • 134,197
  • 12
  • 106
  • 148
  • Thanks a ton! Makes sense now that you pointed out get_rect() is making new rectangles all over the place. Do you know what catching the return value would look like for the sprite? is rot_img_rect getting assigned to sprite.rect? Doing: sprite.image, sprite.rect = rotate_center(sprite.image_clean, sprite.angle) just puts the whole sprite at (0,0) – Brand0R Dec 05 '17 at 03:00
  • Guess my question is where does rot_img_rect get assigned to on the sprite? – Brand0R Dec 05 '17 at 03:06
1

You have to pass the rect of the sprite as well, because it holds the actual position of the sprite and then pass the center coords to .get_rect or assign them to the new rect's center. If you just call .get_rect(), you get a rect with the topleft coords (0, 0).

def rotate_center(image, rect, angle):
    """Rotate a Surface, maintaining position."""
    rot_img = pg.transform.rotate(image, angle)
    # Get a new rect and pass the center position of the old
    # rect, so that it rotates around the center.
    rect = rot_img.get_rect(center=rect.center)
    return rot_img, rect  # Return the new rect as well.


# In the main while loop.
turret.angle += 10
# Also pass the turret's rect which holds the position of the sprite.
image, rect = rotate_center(turret.image_clean, turret.rect, turret.angle)
turret.image = image  # Assign the new image.
turret.rect = rect  # Assign the new rect.

You could also do this in the update method of the sprite:

import pygame as pg


WHITE = pg.Color('white')

class Tower2(pg.sprite.Sprite):
    def __init__(self, pos, tRange=1, attackSpeed=1):
        super().__init__()
        self.image = your_image
        self.image_clean = self.image
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.angle = 0

    def update(self):
        self.angle += 10
        self.image = pg.transform.rotate(self.image_clean, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)


def main():
    pg.init()

    SCREEN_WIDTH = 640
    SCREEN_HEIGHT = 480
    size = (SCREEN_WIDTH, SCREEN_HEIGHT)
    screen = pg.display.set_mode(size)
    done = False
    clock = pg.time.Clock()

    all_sprites = pg.sprite.Group()

    turret = Tower2((SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
    all_sprites.add(turret)

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        all_sprites.update()

        screen.fill(WHITE)
        all_sprites.draw(screen)
        pg.display.flip()
        clock.tick(15)

main()
pg.quit()
skrx
  • 19,980
  • 5
  • 34
  • 48