def rotate(self):
#Save the original rect center
self.saved_center=self.rect.center
#Rotates a saved image every time to maintain quality
self.image=pygame.transform.rotate(self.saved_image, self.angle)
#Make new rect center the old one
self.rect.center=self.saved_center
self.angle+=10
When I rotate the image, there is a weird shifting of it despite the fact that I'm saving the old rect center and making the rotated rect center the old one. I want it to rotate right at the center of the square.
Here's what it looks like:
http://i.imgur.com/g6Os9.gif
You are just calculating the new rect wrong. Try this:
def rotate(self):
self.image=pygame.transform.rotate(self.saved_image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.angle+=10
It tells the new rect to center itself around the original center (the center never changes here. Just keeps getting passed along).
The issue was that the self.rect was never being properly updated. You were only changing the center value. The entire rect changes as the image rotates because it grows and shrinks in size. So what you needed to do was completely set the new rect each time.
self.image.get_rect(center=self.rect.center)
This calculates a brand new rect, while basing it around the given center. The center is set on the rect before it calculate the positions. Thus, you get a rect that is properly centered around your point.
I had this issue. My method has a bit of a different purpose, but I solved it quite nicely.
import pygame, math
def draw_sprite(self, sprite, x, y, rot):
#'sprite' is the loaded image file.
#'x' and 'y' are coordinates.
#'rot' is rotation in radians.
#Creates a new 'rotated_sprite' that is a rotated variant of 'sprite'
#Also performs a radian-to-degrees conversion on 'rot'.
rotated_sprite = pygame.transform.rotate(sprite, math.degrees(rot))
#Creates a new 'rect' based on 'rotated_sprite'
rect = rotated_sprite.get_rect()
#Blits the rotated_sprite onto the screen with an offset from 'rect'
self.screen.blit(rotated_sprite, (x-(rect.width/2), y-(rect.height/2)))
Related
I am making a game where there are two players and they can shoot each other. Their movement will be defined by a rotation around a fixed point, the point will be(600, 300), which is the center of our screen. The player will keep rotating around the point as long as they are pressing a certain button(which is keep providing force to our player) else they will fall(due to gravity). I think it would help to think of it as a ball attached to a point using a string. The string is attached as long as a button is pressed and gets unattached as soon as the button is released and the ball flies off. Here is my player class
class Player:
def __init__(self):
self.pos = [500, 200]
self.width = 30
self.height = 30
self.player = pygame.image.load("player.png").convert_alpha()
self.player = pygame.transform.scale(self.player, (self.width, self.height))
self.rect = self.player.get_rect()
self.rotated_player = None
self.anguler_vel = 0
self.Fg = 0.05
self.Fp = 0
self.arm_length = 0
Fp is the force perpendicular to the force of gravityFg. Fg is the force which is pulling it down on our player. Fp is defined by math.sin(theta) * Fg. I am keeping track of Fp because i want the player to keep moving in the direction of rotation after its unattatched from the string. arm_length is the length of the string.
I have a Point class, which is the point about which our player will rotate. Here's the point class.
class Point:
def __init__(self,x, y):
self.pos = [x, y]
dx = self.pos[0] - player.pos[0]
dy = self.pos[1] - player.pos[1]
self.angle = math.atan2(dy, dx)
Now, i need help with the actual rotation itself. I am aware that adding a certain value to the angle every single frame would make it go around. But how would i make it go around a certain point that i specify and how would the arm length tie into this?. I find that it is really difficult to implement all of this because the y-axis is flipped and all the positional values have to be scaled down when using them in calculations because of the FPS rate. Any help on how this is done would be appreciated as well. Thanks
When you use pygame.transform.rotate the size of the new rotated image is increased compared to the size of the original image. You must make sure that the rotated image is placed so that its center remains in the center of the non-rotated image. To do this, get the rectangle of the original image and set the position. Get the rectangle of the rotated image and set the center position through the center of the original rectangle. e.g.:
def rotate_center(image, rect, angle):
rotated_image = pygame.transform.rotate(image, angle)
new_rect = rotated_image.get_rect(center = rect.center)
return rotated_image, new_rect
screen.blit(*rotate_center(image, image_rect, angle))
Alos see How do I rotate an image around its center using PyGame? and How to rotate an image(player) to the mouse direction?
I'm trying to select the center of my image to make a hitbox some pixels around it.
I was able to inflate() the hitbox to make it the right size, but it always uses the top left corner of the image. This is fine when i'm moving left, but when i turn right it goes away from the character (in the image the character is dragging a sword, so it goes way off center).
I've been reading about Vector2, pos and offset, but i can't get it to work.
In conclusion i need to learn a way to find the center of my image in order to place it's hitbox a few pixels to each side. Either that or how to "shift" the corner the hitbox uses so it's always in the front of the char.
Use a pygame.Rect object. Get the rectangle from the pygame.Surface object (image) and set the top left corner position (x, y) of the rectangle. e.g.:
rect = image.get_rect()
rect.topleft = (x, y)
respectively
rect = image.get_rect(topleft = (x, y))
pygame.Rect provides a lot of virtual attributes, which can be used to retrieve the corners, borders, center and size of the rectangle. .center returns the center of the rectangle:
center_x, center_y = rect.center
How can I define a rect collision detection smaller than image in pygame?
I'd like to have a collision patter like the second image , but I'm having a cut image when a try to set the width and height in the method rect.
When I try to set using image size, I have the collision detection in red
self.rect = pygame.rect.Rect(location, self.image.get_size())
If I set the size using width and height, I just have the third image
self.rect = pygame.rect.Rect(location, (32, 150))
I really wouldn't like to use pixel perfect collision, because is the slowest collision detection, so someone have some idea how can I achieve the second image collision approach using Rect? Thanks.
It seems that you are using pygames built in sprite module. (Please correct me if I am wrong)
You might know that each sprite consist of an image (which is drawn on a surface) and a rect object (sets location and size (!) of the image).
As Luke Taylor suggested, you could create a new rect object in your player class …
self.collideRect = pygame.rect.Rect((0, 0), (32, 150))
… and set its location (according to your graphic) to
self.collideRect.midbottom = self.rect.midbottom
Every time you change the position of your player you must call this line too, so your self.collideRect rect object "moves" with your player on screen.
To test if a point (e.g. the mouse coordinates) is inside the self.collideRect, call
if self.collideRect.collidepoint(x, y) == True:
print("You clicked on me!")
Try drawing a completely new rectangle separate from the image that is behind the image, but who's location is constantly set to that if the image.
Im trying to rotate a loaded image but I need it rotated by a specific axis.
I was doing this:
arm = pygame.image.load('w1.png').convert()
arms = [pygame.transform.rotate(arm, deg) for deg in range(0, 360, 4)]
I was then iterating through the indicies of arms with:
count+=1
arms[count]
The rotation does work but it is not rotating on the axis properly. I have written algorithms that rotate lines made with pygame.draw.line but I do not know how to achieve this with an image.
Any and all insight appreciated,
thanks
After rotating your image, and before blitting, get the new rect for the image, and change the positional attributes of the rect back to the original position.
IE: save rect center, rotate image, get new rect, set newrect center, blit.
Using the center attribute with only rotate it around the center of the image, but maybe using one of the corners will put you on the right path.
def RESET_ROTATED_RECT(old_rect,rotated_image):
old_pos=old_rect.center
newrect=rotated_image.get_rect()
newrect.center=old_pos
return newrect
I use the center often, but haven't used the other positions. It may be worth tinkering with.
I am learning pygame and want a graphic for a button with the three states: normal, hover, and pressed. I have an image like this one ...
... and I want to get a new Surface using a portion of it.
I'm loading the image with this code:
buttonStates = pygame.image.load(os.path.join('image','button.png'))
How can I make a new surface using just a portion of that graphic?
cropped = pygame.Surface((80, 80))
cropped.blit(buttonStates, (0, 0), (30, 30, 80, 80))
The blit method on a surface 'pastes' another surface on to it. The first argument to blit is the source surface. The second is the location to paste to (in this case, the top left corner). The third (optional) argument is the area of the source image to paste from -- in this case an 80x80 square 30px from the top and 30px from the left.
You can also use the pygame.Surface.subsurface method to create subsurfaces that share their pixels with their parent surface. However, you have to make sure that the rect is inside of the image area or a ValueError: subsurface rectangle outside surface area will be raised.
subsurface = a_surface.subsurface((x, y, width, height))
There are 2 possibilities.
The blit method allows to specify a rectangular sub-area of the source _Surface:
[...] An optional area rectangle can be passed as well. This represents a smaller portion of the source Surface to draw. [...]
In this way you can blit an area of the source surface directly onto a target:
cropped_region = (x, y, width, height)
target.blit(source_surf, (posx, posy), cropped_region)
Alternatively, you can define a subsurface that is directly linked to the source surface with the subsurface method:
Returns a new Surface that shares its pixels with its new parent. The new Surface is considered a child of the original. Modifications to either Surface pixels will effect each other.
As soon as a subsurface has been created, it can be used as a normal surface at any time:
cropped_region = (x, y, width, height)
cropped_subsurf = source_surf.subsurface(cropped_region)
target.blit(cropped_subsurf, (posx, posy))
I think the best way to do it is crop the image of these 3 kind of buttons in a external program and load in different surface instead use pygame to crop it