1

Python Version: 3.4

I've recently started using Pygame and I have then started to use the module pyganim to handle animation of sprites. http://inventwithpython.com/pyganim/

I have run into an issue when I try to rotate the animation. It has a strange effect, such as shown in this example https://i.stack.imgur.com/KOgGZ.gif, from this issue:

Weird shifting when using pygame.transform.rotate()

I have tried multiple fixes, such as finding the center of the image etc but as it is a pyganim object, I am blitting to my surface like: (where screen is my surface)

player.blit(screen, playerpos)

Changing the center of it seems to have no effect.

I have also tried swapping the image I'm using for a full black square, and I've still had no luck in debugging it.

I assume it is due to the fact I am blitting the pyganim object to my surface, and that I need to maybe blit it to another surface and rotate that? <- speculation!

The issue I believe lies here,

# Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]),position[0]-(playerpos[0]))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = (playerpos[0]-(player.getRect().width/2), playerpos[1]-(player.getRect().height/2))
player.blit(screen, playerpos1) 

As the actual rect of the Pyganim object doesn't change, no matter what rotation is put against it (I have some on screen logging that I verified this with). I think the standard methods for correction are void.

If anyone with some experience using Pyganim could shed some light it would be awesome!

Here is the code in full...

# Import modules
import pygame
from pygame.locals import *
import pyganim
import math

# Initialise
pygame.init()
width, height = 640, 480
screen=pygame.display.set_mode((width, height))
keys = [False, False, False, False]
playerpos=[100,100]

# Load images
player = pyganim.PygAnimation([('resources/images/tdt/p1.png', 0.3), ('resources/images/tdt/p2.png', 0.3), ('resources/images/tdt/p3.png', 0.3), ('resources/images/tdt/p2.png', 0.3)])
player.play()
grass = pygame.image.load("resources/images/tdt/bg.png")
grasswidth = grass.get_width()
grassheight = grass.get_height()
myfont = pygame.font.SysFont("monospace", 20)


# loop
while 1:
# clear the screen before re-drawing
screen.fill(0)

# draw the screen elements
for x in range(width//grasswidth+1):
    for y in range(height//grassheight+1):
        screen.blit(grass,(x*100,y*100))

# Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]),position[0]-(playerpos[0]))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = (playerpos[0]-(player.getRect().width/2), playerpos[1]-(player.getRect().height/2))
player.blit(screen, playerpos1) 


# render text
label = myfont.render("X pos: "+str(playerpos[0])+"| Y pos: "+str(playerpos[1]), 1, (255,255,0))
screen.blit(label, (20, 20))    

# Update the screen
pygame.display.flip()

# loop through the events
for event in pygame.event.get():
    #check if the event is the X button
    if event.type==pygame.QUIT:
        ##if it is, quit the game
        pygame.quit()
        exit(0)

    if event.type == pygame.KEYDOWN:
        if event.key==K_w:
            keys[0]=True
        elif event.key==K_a:
            keys[1]=True
        elif event.key==K_s:
            keys[2]=True
        elif event.key==K_d:
            keys[3]=True
    if event.type == pygame.KEYUP:
        if event.key==pygame.K_w:
            keys[0]=False
        elif event.key==pygame.K_a:
            keys[1]=False
        elif event.key==pygame.K_s:
            keys[2]=False
        elif event.key==pygame.K_d:
            keys[3]=False

# Move player
if keys[0]: # W
    playerpos[1]-=2
elif keys[2]: # S
    playerpos[1]+=2
if keys[1]: # A
    playerpos[0]-=2
elif keys[3]: # D
    playerpos[0]+=2

OK

I have made some progress on this... The code I have written right now is basic and works for the shape in question, as it is easily divisible by 4! :D However, I will re-write it to be able to handle all eventualities, however, it may be that the boxes have to be square: here is the code now:

playerpos1 = rMoveAmount(playerpos,angle, 44)
player.blit(screen, playerpos1) 

With rMoveAmount being:

def rMoveAmount(position, degrees, picsize):
#MOVE A BOX DEPENDING ON THE AMOUNT ROTATED
degTest = int(((360-degrees*57.29)-180)%90) 
#Has to be split up into two sections, before the half 90 degree rotation
if degTest < 45:
    t = int((((360-angle*57.29)-180)%45)//4) 
    nPos = (position[0] - t , position[1] - t )
#And after
if degTest > 44: 
    t = int((((360-angle*57.29)-180)%45)//4) 
    nPos = (position[0] + t - 11, position[1] + t - 11)

return nPos

So It takes 1/4 of the angle (which would be between 0 - 11) Then it uses that as the offset (as my pic is 44px and will always need to be shifted by 1/4)

As you can see, the pic size isn't currently used. And I think it was just luck that my picture size was 44 and fit so snugly into the middle of a half 90 degree rotation, and could also be divided by 11. However, I will work on being able to use this with more flexible sizes and update.

Edit 3:

Ok, this one should now work for all square images, it will also center the cursor correctly. I will just show the changes made, not the whole code again:

# 3 - Load images
player = pyganim.PygAnimation([('resources/images/tdt/p1.png', 0.3), ('resources/images/tdt/p2.png', 0.3), ('resources/images/tdt/p3.png', 0.3), ('resources/images/tdt/p2.png', 0.3)])
player.play()
playerSize = player.getRect().width


 # 6.1 - Set player position and rotation
position = pygame.mouse.get_pos()
angle = math.atan2(position[1]-(playerpos[1]+(playerSize/2)),position[0]-(playerpos[0]+(playerSize/2)))
player.clearTransforms()
player.rotate((360-angle*57.29)-90)
playerpos1 = rMoveAmount(playerpos,angle, playerSize)
player.blit(screen, playerpos1)

And the method rMoveAmount changes:

def rMoveAmount(position, degrees, picsize):
#MOVE A BOX DEPENDING ON THE AMOUNT ROTATED
degTest = int(((360-degrees*57.29)-180)%90)
quartsize = picsize / 4
#Has to be split up into two sections, before the half 90 degree rotation
if degTest < 45:
    #t = int((((360-angle*57.29)-180)%45)//4)
    jump = (quartsize / 44)
    nPos = (position[0] - (jump * degTest)   , position[1] - (jump * degTest)  )
#And after
if degTest > 44:
    jump = (quartsize / 44)
    nPos = (position[0] + (jump * degTest) - (picsize/2) , position[1] + (jump * degTest) - (picsize/2))
return nPos

Will now work with any square images.

Community
  • 1
  • 1
  • Problem lies here `player.getRect()` -> it returns the smallest possible rect that it can be fitted within. You should instead use the original rect of the non-rotated sprite in each step. – bendtherules Jun 04 '14 at 17:29
  • Do you mean like this? `playerpos1 = (playerpos[0]-(orig_rectH/2),playerpos[1]-(orig_rectW/2))` where ori_rectX is set at source by `getRect().Width/Height`? As that hasn't solved the issue. – user3707377 Jun 04 '14 at 20:08
  • Knowing the size of the image, it also doesn't solve it if I set the image size as constants such as `(playerpos[0]-(44/2),playerpos[1]-(44/2))` – user3707377 Jun 04 '14 at 20:12

0 Answers0