What are the pygame.Surface.get_rect() key arguments? - python

I'm learning to use pygame in my programming course, but I'm confused with the pygame.Surface.get_rect() method. I see that it can take some keyword arguments but I can't find anywhere what they are. I've seen get_rect(center=(num, num)) but I am trying to assign a top left corner coordinate. Is there a way of doing this?

The keyword arguments are documented as being applied to the rect's attributes. Those are documented in the pygame.Rect page:
The Rect object has several virtual attributes which can be used to
move and align the Rect:
x,y
top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
size, width, height
w,h
All of these attributes can be assigned to:
rect1.right = 10
rect2.center = (20,30)
Assigning to size, width or height changes the dimensions of the
rectangle; all other assignments move the rectangle without resizing
it. Notice that some attributes are integers and others are pairs of
integers.
So, if you want to get a rectangle the size of your image, with its top left at a certain point, you'd do:
my_surface.get_rect(topleft=some_point)
You could also pass separate top and left arguments (or x and y).

Related

How to save all pixels that were in the place of a rect in pygame?

Is there a way to save all the pixels that were in the place of a rect before drawing it? So that when I draw that rect again in a different place I can replace the old one with the saved pixels instead of filling that area with colors mamually?
You can define a subsurface that is directly linked to the source surface with the method subsurface:
subsurface(Rect) -> Surface
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.
Create a copy of the subsurface to store it permanently:
rect_area = pygame.Rect(x, y, width, height)
area_surf = screen.subsurface(rect_area).copy()
Use the Surface later to replace the rectangular area:
screen.blit(area_surf, region)

PyQT5 setRect moves origin point in a QGraphicsScene

Whenever i try to move a rectangle in a scene, the origin point of the rectangle appears to change to the location where the rectangle was before updating the location.
So if i create the rectangle at (0,0) and move it using rect.setRect(x, y) then returning the position would yield (0,0) instead of (x, y).
If you would move it in the QGraphicsScene using your mouse it would return the correct (x, y).
The code i use to create the rectangle is as following:
class placeableObject:
def __init__(self, index, scene, QPen, QBrush, width=100, height=100):
"""Parent class for placeable objects"""
self.width = float(width)
self.height = float(height)
self.index = index
self.rect = scene.addRect(0, 0, int(width), int(height), QPen, QBrush)
self.rect.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable)
To move this rectangle i have the following embedded function and a function to return the position:
def getPos(self):
"""Returns a list with the x,y position of the object in the scene"""
return [self.rect.scenePos().x(), self.rect.scenePos().y()]
def move(self, x, y):
"""Moves the object in the editor view to coordinatex x,y"""
self.rect.setRect(x, y, self.width, self.height)
You are forgetting an important aspect of graphics items: their [scene] position is not always the top left corner of the object that is actually shown to the user.
This is clear when adding items using scene.add*() (this was already explained on this question).
As the documentation explains:
Note that the item's geometry is provided in item coordinates, and its position is initialized to (0, 0). For example, if a QRect(50, 50, 100, 100) is added, its top-left corner will be at (50, 50) relative to the origin in the item's coordinate system.
The item position is not the rectangle position, so when you use setRect you are not moving the item, but setting a new rectangle at the specified position, while leaving the item at (0, 0) coordinates of its system; note that this also means that if the item has no parent, scenePos() will be the same as pos(), otherwise it's relative to the parent.
If you want to know the actual position of the top left corner of the rectangle, you could use one of the following:
item.sceneBoundingRect().topLeft()
item.scenePos() + item.rect().topLeft()
If you are always adding the rectangle at (0, 0), then you can just use setPos(), but if you need to compute the position based on the current, actual rectangle position, you have to use one of the functions above.
Note that rectangles can also have negative sizes, so if you need the top left corner of the visible rectangle, you need to normalize it:
item.scenePos() + item.rect().normalized().topLeft()
It seems that i have figured it out.
I changed the move function to the following:
def move(self, x, y):
"""Moves the object in the editor view to coordinatex x,y"""
self.rect.setPos(x, y)
That returns the correct positions within the scene for me!
Thanks anyway :)

PyGame Platformer with Interactive Platforms "Drawn" In

I'm looking for the easiest way to implement this. I'm trying to implement platforms (with full collision detection) that you can draw in via mouse. Right now I have a line drawing function that actually draws small circles, but they're so close together that they more or less look like a line. Would the best solution be to create little pygame.Rect objects at each circle? That's going to be a lot of rect objects. It's not an image so pixel perfect doesn't seem like an option?
def drawGradientLine(screen, index, start, end, width, color_mode):
#color values change based on index
cvar1 = max(0, min(255, 9 * index-256))
cvar2 = max(0, min(255, 9 * index))
#green(0,255,0), blue(0,0,255), red(255,0,0), yellow(255,255,0)
if color_mode == 'green':
color = (cvar1, cvar2, cvar1)
elif color_mode == 'blue':
color = (cvar1, cvar1, cvar2)
elif color_mode == 'red':
color = (cvar2, cvar1, cvar1)
elif color_mode == 'yellow':
color = (cvar2, cvar2, cvar1)
dx = end[0] - start[0]
dy = end[1] - start[1]
dist = max(abs(dx), abs(dy))
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx)
y = int(start[1]+float(i)/dist*dy)
pygame.draw.circle(screen, color, (x, y), width)
That's my drawing function. And here's my loop that I have put in my main game event loop.
i = 0
while (i < len(pointList)-1):
drawGradientLine(screen, i, pointList[i], pointList[i + 1], r, mode)
i += 1
Thanks for any help, collision detection is giving me a huge headache right now (still can't get it right for my tiles either..).
Any reason you want to stick with circles?
Rectangles will make the line/rectangle a lot more smooth and will make collision detecting a lot easier unless you want to look into pixel perfect collision.
You also don't seem to save your drawn objects anywhere (like in a list or spritegroup), so how are you going to check for collision?
Here's a leveleditor I did for game awhile back, it's not perfect, but it works:
https://gist.github.com/marcusmoller/bae9ea310999db8d8d95
How it works:
The whole game level is divided up into 10x10px grid for easier drawing
The leveleditor check if the mouse is being clicked and then saves that mouse position
The player now moves the mouse to another position and releases the mouse button, the leveleditor now saves that new position.
You now have two different coordinates and can easily make a rectangle out of them.
Instead of creating a whole bunch of rect objects to test collision against, I'm going to recommend creating something called a mask of the drawn-in collideable object, and test for collision against that. Basically, a mask is a map of which pixels are being used and which are not in an image. You can almost think of it as a shadow or silhouette of a surface.
When you call pygame.draw.circle, you are already passing in a surface. Right now you are drawing directly to the screen, which might not be as useful for what I'm suggesting. I would recommend creating a rect which covers the entire area of the line being drawn, and then creating a surface of that size, and then draw the line to this surface. My code will assume you already know the bounds of the line's points.
line_rect = pygame.Rect(leftmost, topmost, rightmost - leftmost, bottommost - topmost)
line_surf = pygame.Surface((line_rect.width, line_rect.height))
In your drawGradientLine function, you'll have to translate the point coordinates to the object space of the line_surf.
while (i < len(pointList)-1):
drawGradientLine(line_surf, (line_rect.x, line_rect.y), i, pointList[i], pointList[i+1], r, mode)
i += 1
def drawGradientLine(surf, offset, index, start, end, width, color_mode):
# the code leading up to where you draw the circle...
for i in xrange(dist):
x = int(start[0]+float(i)/dist*dx) - offset[0]
y = int(start[1]+float(i)/dist*dy) - offset[1]
pygame.draw.circle(surf, color, (x, y), width)
Now you'll have a surface with the drawn object blitted to it. Note that you might have to add some padding to the surface when you create it if the width of the lines you are drawing is greater than 1.
Now that you have the surface, you will want to create the mask of it.
surf_mask = pygame.mask.from_surface(line_surf)
Hopefully this isn't getting too complicated for you! Now you can either check each "active" point in the mask for collision within a rect from your player (or whatever other objects you want to collide withe drawn-in platforms), or you can create a mask from the surface of such a player object and use the pygame.Mask.overlap_area function to check for pixel-perfect collision.
# player_surf is a surface object I am imagining exists
# player_rect is a rect object I am imagining exists
overlap_count = surf_mask.overlap_area(player_surf, (line_rect.x - player_rect.x, line_rect.y - player_rect.y))
overlap_count should be a count of the number of pixels that are overlapping between the masks. If this is greater than zero, then you know there has been a collision.
Here is the documentation for pygame.Mask.overlap_area: http://www.pygame.org/docs/ref/mask.html#pygame.mask.Mask.overlap_area

How to Use the Pygame Rect

I have been experimenting with Pygame, and have come across a problem that I could not find the answer to. In this paste, my basic game framework is exhibited. How can i complete my ballSnapLeft() definition correctly?
Edit: I am not looking for my code to be completed, but I am looking for someone to explain how the 'Rect' class(?) works, and how it could be applied.
Edit2: I have tried to use the x and y coordinates to do so, but I think there is a simpler way that can actually work, instead of using brute coordinates.
From Making Games With Python and Pygame:
myRect.left The int value of the X-coordinate of the left side of the
rectangle.
myRect.right
The int value of the X-coordinate of the right side of the rectangle.
myRect.top
The int value of the Y-coordinate of the top side of the rectangle.
myRect.bottom
The int value of the Y-coordinate of the bottom side.
Because all of these attributes return integers, that's probably why your code isn't working.
Also, if your goal with ballSnapLeft() is to move the ball to a position away from the player, ballRect.right = playerRect.left - distance would only change the X coordinate of the rect. To make the ball also move in the Y coordinate you could do something like
def ballSnapTop():
ballRect.top = playerRect.bottom - distance
Are you getting an error when you execute ballRect.right = playerRect.left - (0, 1)?
ballRect.right and ballRect.left, along with the related top, bottom, width, height values, are int types and can't have tuples added or subtracted from them.
You might want to take a look at the pygame.Rect documentation, and consider using pygame.Rect.move(x,y) which will shift the coordinates of the rectangle for you.
It's also worth noting that if you change, for example, myRect.topleft, then the corresponding top, left, bottom, etc... values will change as well so that the rect translates and preserves its size.

How to change a regular image to a Rect in pygame [duplicate]

This question already has an answer here:
converting an image to a rect
(1 answer)
Closed 9 years ago.
I need the simplest way of making an image a Rect, is there a function for this or do I need to make it a Rect from the beginning? I have a script were the player moves around but I want the image of the character to be a Rect, so I can do more with it.
I want the image of the character to be a rect doesn't make a lot of sense, but you're probably looking for the get_rect method of the Surface class:
get_rect()
get the rectangular area of the Surface
get_rect(**kwargs) -> Rect
Returns a new rectangle covering the entire surface. This rectangle will always start at 0, 0 with a width. and height the same size as the image...
(I guess with regular image you mean a Surface, since a Surface is how pygame represents images)
Note that the Rect returned by this function always starts at 0, 0. If you already track the position of your object somehow (say a x and y variable), you could either
drop those variables and only use a Rect to keep track of the position of your object. If you move your object, instead of altering the x and y variable (e.g. x += mx/y += my), you would just update the Rect (e.g. pos.move_ip(mx, my) where pos is the Rect).
create a new Rect whenever you need a Rect, and make sure it points to the right location, using named arguments (e.g. your_surface.get_rect(x=x, y=y))
use a Sprite, which is basically a combination of a Surface and a Rect.

Categories