Pygame Rotation Tutorial

Hey there,

So what I was recently having some trouble with was with Python’s PyGame Rotate function. I was having trouble understanding what some of from the comments found here meant, or were trying to illustrate, let alone the documentation given, so I resolved to figure it out , once and for all, so hopefully this post will be enough to get someone else on track. I think I was getting caught up on the different ways to deal with blitting and surfaces and rects.

When you start off, it can get confusing with all the new terms, so here is a simple example of what you can do to create a rotating 2D polygon:

So right away, there’s just the import initialization you need to do with PyGame that you need, but I have no idea what it does, only that you need to include it in every PyGame application.

import pygame

#necessary pygame initializing
pygame.init()

This next line is the initialization of the surface that you will be blitting to, and the displaying to the user. In order for something to be seen by the player, it must be blitted to screen first. (400,400) is the Width and Height of the display, and screen surface.

#create a surface that will be seen by the user
screen = pygame.display.set_mode((400, 400))

This is just a variable that will be used and manipulated to determine the degrees that the image will be rotated

#create a variable for degrees pf rotation
degree = 0

This is the beginning of the main loop. It could probably be changed to something nicer, like a variable or something than changes when the pygame.quit function gets called, but for this example it will do just fine.

while True:

This fills the screen surface with the color with the RGB value of (40,40,40), which is an off black, if my color sense is good enough. Remember the values have to be between 0 – 255.

There are good enough RBG > Hex translators available, but I like to use the ever present YellowPipe one. And, while we’re at it, a great color chooser tool is the Color Scheme Designer 3.

#clear screen at the start of every frame
screen.fill((40, 40, 40))

Here a new surface is created, with the size of 100, 100. This surface will be used to draw the shapes onto, and then be rotated. The surface gets filled with (255,255,255) which is white, since it contains all the colors, so you can see what part of the surf-surface is filled or not.

#create new surface with white BG
surf = pygame.Surface((100, 100))
surf.fill((255, 255, 255))

The color key is the color that is treated as transparent when the surface gets blitted. An analogue to the color key is the blue or green screen in movies. The color of the background is ignored.

#set a color key for blitting
surf.set_colorkey((255, 0, 0))

Here two rect objects are created. All rects are is a collection of values which are used everywhere else in pygame. I think of them as specialized dictionaries.

Rects are created with a minimum of a pair values, called x and y,  or left and top, which is the value in pixels of how far along the x or y plane they are, or how far from the left or top side that its top left pixel is. This means that the ‘bigger’ rect here is made at the 0,0 position.

The second pair of vales represents the size of the rect, or width and height. So in the ‘bigger’ rect, the top of the square travels 100px right from 0 on the x plane, and 50px down the y plane.

#create shapes so you can tell rotation is happenning
bigger = pygame.Rect(0, 0, 100, 50)
smaller = pygame.Rect(25, 50, 50, 50)

Now here is the drawing of the above rects. The draw.rect calls work with a destination surface (here ‘surf’), an RGB color (100,0,0), and either the x,y pair or a rect (I used the ‘bigger’ and ‘smaller’ rects).

Remember that it doesn’t show up on the user’s display just yet, you need to flip the display, which comes later.

#draw those two shapes to that surface
pygame.draw.rect(surf, (100, 0, 0), bigger)
pygame.draw.rect(surf, (100, 0, 0), smaller)

This is just a x,y pair of co-ordinates where the surf surface will be blitted, or copied, to. Blitting is just the word for copying, or drawing something onto another surface. So it is 200 pixels away from the left side, and 200 pixels away from the top side.

##ORIGINAL UNCHANGED
#what coordinates will the static image be placed:
where = 200, 200

Here is the blitting of surf to the screen surface, but  blittedRect is created, which is simply the rect, which remember is essentially just a collection of coordinates, that represents the    coordinates that was blitted onto on the screen surface. That mean that even though the two rects were drawn at 0y and 50y respectively, the new rect represent where those y values are translated on the screen surface, instead of the surf surface.

If it helps, you can add the y co-ordinate from the where tuple to the y co-ordinate from the two rects from earlier.

#draw surf to screen and catch the rect that blit returns
blittedRect = screen.blit(surf, where)

Now the center of that newest rect, which was returned by the blit function is saved because you will eventually need to realign a surface that will be the result of rotating surf surface.

##ROTATED
#get center of surf for later
oldCenter = blittedRect.center

Here two things are happening. First PyGame’s rotate transformation function is being called. What this does is create a new surface that has the source surface (surf in our case) rotated by a given amount of degrees ( our variable is called degree, initally set to 0). This slightly distorts the resulting image due to the internal mechanics of the function.

Secondly, it returns that surface which you must assign to a variable, otherwise, there won’t be any visible rotation, since the rotation function does not alter the source image. This surface (rotatedSurf) is later blitted to the screen surface, and then made visible to the user.

#rotate surf by DEGREE amount degrees
rotatedSurf = pygame.transform.rotate(surf, degree)

This is where we correct the center of rect object returned by the get_rect function. You need to call get_rect on the surface, because otherwise you would not have any way of getting the positional co-ordinates of it, or it’s width and height.

Once we assign that rect to a name (rotRect) we change its center attribute to the oldCenter we had set earlier.

This is also where it was the most confusing for me, so bear with me if this becomes unclear, feel free to ask for clarification, in the comments, on twitter (@TankorSmash) or on the reddit post that I will create and link.

#get the rect of the rotated surf and set it's center to the oldCenter
rotRect = rotatedSurf.get_rect()
rotRect.center = oldCenter

Now we blit to the screen surface the rotated surface (rotatedSurf) which came from the rotate function, at the x,y coordinates that the rotRect rect specifies. Remember that the rotRect uses the oldCenter variable as its center instead of the rotated surfaces center, because that center point is distorted.

#draw rotatedSurf with the corrected rect so it gets put in the proper spot
screen.blit(rotatedSurf, rotRect)

Here is simple math: add five to the degree variable, if the degree variable has a value greater than 360 (a full circle, for you brainiacs out there) reset it to 0, so as to avoid unnecessary calculations. Why rotate all the way around, then an extra 5 degrees, when just rotating 5 degrees would have the same effect?

#change the degree of rotation
degree += 5
if degree > 360:
degree = 0

The display.flip function takes the screen surface we created just after the pygame initialization and makes it visible to the user. That’s all it does here, simple enough

#show the screen surface
pygame.display.flip()

Finally, this is just a timer so the example  doesn’t run so fast that you can’t tell what’s going on. The value represents milliseconds, so here it is a wait of 60ms until the loop is restarted.

#wait 60 ms until loop restart
pygame.time.wait(60)

Here is the full example. I was running Python 2.6 on Vista with pyGame 1.9.1. There isn’t anything else imported so there isn’t any other dependancies.\

Again,  don’t be afraid to ask for help or clarification, here, on my twitter, or on reddit’s many subreddits.

import pygame

#necessary pygame initializing
pygame.init()

#create a surface that will be seen by the user
screen =  pygame.display.set_mode((400, 400))

#create a varible for degrees pf rotation
degree = 0
while True:
    #clear screen at the start of every frame
    screen.fill((40, 40, 40))

    #create new surface with white BG
    surf =  pygame.Surface((100, 100))
    surf.fill((255, 255, 255))
    #set a color key for blitting
    surf.set_colorkey((255, 0, 0))

    #create shapes so you can tell rotation is happenning
    bigger =  pygame.Rect(0, 0, 100, 50)
    smaller = pygame.Rect(25, 50, 50, 50)

    #draw those two shapes to that surface
    pygame.draw.rect(surf, (100, 0, 0), bigger)
    pygame.draw.rect(surf, (100, 0, 0), smaller)

    ##ORIGINAL UNCHANGED
    #what coordinates will the static image be placed:
    where = 200, 200

    #draw surf to screen and catch the rect that blit returns
    blittedRect = screen.blit(surf, where)

    ##ROTATED
    #get center of surf for later
    oldCenter = blittedRect.center

    #rotate surf by DEGREE amount degrees
    rotatedSurf =  pygame.transform.rotate(surf, degree)

    #get the rect of the rotated surf and set it's center to the oldCenter
    rotRect = rotatedSurf.get_rect()
    rotRect.center = oldCenter

    #draw rotatedSurf with the corrected rect so it gets put in the proper spot
    screen.blit(rotatedSurf, rotRect)

    #change the degree of rotation
    degree += 5
    if degree > 360:
        degree = 0

    #show the screen surface
    pygame.display.flip()

    #wait 60 ms until loop restart
    pygame.time.wait(60)

8 thoughts on “Pygame Rotation Tutorial”

  1. Hello,
    This tutorial was helpful, however, when I tried running the program, it came up as ” File “rotate.py”, line 1, in
    import pygame
    File “/Users/Victor/Desktop/pygame.py”, line 2, in
    NameError: name ‘create_screen’ is not defined

    How should I fix this???

    Thanks

    1. Are you able to run a simple instance of pygame? The error you are getting is originating from the pygame.py which suggests that PyGame isn’t properly installed. The specific error you’re getting though is telling you you don’t have a variable set for the name create_screen.

      Let me know if you sort it out. Basically, make sure PyGame is properly installed.

  2. This is really helpful. unfortunately the only problem that I have are joints. For example, I am trying to code a simple tank game where the tank rotates independently from the cannon. Since the cannon and the tank base are not perfect squares the sizes change when they rotate. I was wondering how I could set a point on the image to act as a rotate point. Thanks 🙂

    1. How do u rotate this image?

      mentx,menty=100,100
      ment=pygame.image.load(pol) .convert_alpha()
      screen.blit(ment,(mentx,menty))

      how can i rotate this img “ment” ?

  3. Just wanna say thank you, I couldn’t figure out what was happening with this rotation that was shifting the surface. Your article was really helpful to solve it, thanks again!

  4. Yep, this tutorial helped me out greatly.

    I was spoiled using things like Cocos2d and Sprite Kit, etc. You actually have to do the math, but I kinda like it that way.

    Cheers!

    P.S. for images, pygame.transform.rotozoom(img, angle, scale) works even better for anti-aliasing!

Leave a Reply to Yasir Cancel reply

Your email address will not be published.