What is the difference between pygame.Rect() and pygame.draw.rect()? - python

I was learning to create a simple Pygame window which displays a Rectangle. I watched two different videos on Youtube. One of the Youtuber used Pygame.draw.rect() to create a rectangle whereas other Youtuber used both pygame.Rect() and Pygame.draw.rect(). Both gave same result at the end. So what is the difference between these two codes??

pygame.Rect is a class whose instances represent rectangular areas.
pygame.draw.rect is a function that draws rectangles. One of its arguments is a pygame.Rect instance representing the rectangle to draw.
They are completely different things.

pygame.Rect creates a Rect instance to be passed into pygame.draw.rect. Consider this short snippet:
For anyone else who is reading this, first install pygame by running pip install pygame in your IDE's terminal or your default operating system's terminal.
import pygame
SCREEN = pygame.display.set_mode((300, 300))
while True:
pygame.draw.rect(SCREEN, 'white', (30, 30, 100, 100))
pygame.display.update()
The tuple that we passed in to pygame.draw.rect consists of (x, y, width, height):
x being where our rectangle will sit on the vertical axis
y being where our rectangle will sit on the horizontal axis
width being the width of our rectangle
height being the height of our rectangle
This is how you would use pygame.draw.rect by itself. Behind the scenes, pygame transforms this tuple into a pygame.Rect object. So you are only doing a bit more work when using pygame.Rect. For example:
pygame_rect_object = pygame.Rect(30, 30, 100, 100)
pygame.draw.rect(SCREEN, 'white', pygame_rect_object)
As you can see, creating a pygame.Rect object is the same as passing the tuple. A few advantages are readability, and you can pass it into multiple pygame.draw.rect functions without having to repeat the tuple.
Hope this has helped!!!

Related

Pygame rect.inflate function does not seem to work

How does Pygame's "rect.inflate_ip" function work?
In this code, I am trying to inflate this rectangle by some value zoom:
rect = pygame.draw.rect(screen,block.color,(block.x+scx,block.y+scy,10,10))
rect.inflate_ip(zoom,zoom)
But this does not have any effect. Why?
Print rect before and after inflate_ip and you'll see the difference. Of course, it doesn't affect the rectangle drawn on the screen. pygame.draw.rect does not generate an object. This function fills a rectangular area on the screen and returns that area. You have to create a pagame.Rect object and use that for drawing. e.g.:
rect = pygame.Rect(block.x+scx, block.y+scy, 10, 10)
rect.inflate_ip(zoom, zoom)
pygame.draw.rect(screen, block.color, rect)

Pygame: What is the Rect() arguments exactly, and what do they represent?

Here is what I understand so far about Rect, it is a data type that contains all the properties of a given rectangular surface. And is used along the pygame.draw.rect() function as the third argument in order for it to work.
The code follows this syntax (from the official documentation):
Rect(left, top, width, height)
I understand about the third and fourth argument, which suppose to represent the dimensions of the rectangular surface. What I don't understand is the first 2 argument - what is it suppose to represent and what does it do? Why does it always start at (0,0) and what can we use it for?
The first two arguments get assigned to the x and y attributes of the rect, the coordinates of the topleft corner. So if you want to create a rect that is positioned at the coordinates (200, 300), you can write: rect = pygame.Rect(200, 300, 40, 50). The third and fourth argument are indeed the dimensions of the resulting rect instance.
pygame.Rects also have a lot of other attributes which you can use to position your rect.
x,y
top, left, bottom, right
topleft, bottomleft, topright, bottomright
midtop, midleft, midbottom, midright
center, centerx, centery
If you want to move a rect, you can assign the new position to the x, y, topleft or one of the other attributes.
rect.x = 5
rect.y = 10
# That's pretty much the same as writing:
rect.topleft = (5, 10)
# You can also increment the position.
rect.x += 5
# Or use the `rect.move_ip` method.
rect.move_ip(5, 10)
Rects are used to store the blit position of sprites or images and for collision detection, for example with the colliderect or collidepoint methods.

How to get the surface from a rect/line

I am trying to find the point where a line collides with a brick in the arkanoid that i am making. The most logical way i found is getting the mask from the line and use collidemask as it returns the point. Well as i tried with this:
linemask = pygame.mask.from_surface(pygame.draw.line(screen, (0,0,0), bola.line[0], bola.line[1], 2))
it gave me this error:
TypeError: argument 1 must be pygame.Surface, not pygame.Rect
meaning that the input(in this case the line) can't be a rect but needs to be a surface. Do you know how to get the surface from a rect or any alternative solution ?
pygame.draw.line draws on a Surface and returns the affected area in form of a Rect object.
The Surface you drew on is screen. So it's screen you want to create a mask from. Alternatively, create a new Surface that you use pygame.draw on and create a mask from it. Or create a mask from the subsurface of the screen (so you don't have to create a mask from the whole screen), like this:
rect = pygame.draw.line(screen, (0,0,0), bola.line[0], bola.line[1], 2)
surface = screen.subsurface(rect)
mask = pygame.mask.from_surface(surface)

pygame rect collision smaller than image

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.

How can I crop an image with Pygame?

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

Categories

Resources