How do I make a rectangle invisible? (Pygame) [duplicate] - python

This question already has answers here:
Draw a transparent rectangles and polygons in pygame
(4 answers)
Closed 7 years ago.
Image showing rectangles used for collisions.
I am making an arcade-style game which involves moving an on-screen sprite. Since the in-game walls are part of the background image I have set up rectangles in order to test when a sprite collides with a wall (to block it from moving through for example). However I would like to know if there is a way to make the rectangles invisible or not viewable by the player (but still using them for collisions). I have just used the pygame.draw.rect() function to create the rectangles:
zone1rect = pygame.draw.rect(SURF, (0,0,0), (200, 418, 52, 188), 1)
EDIT: would it be possible to create a surface under the main one to add these to? And if so would this still allow collision between the sprite (which is on a different surface)?
I am not asking about aloha colours, so please do not assosciate this with another question. Also that question talks about partly transparent rectangles, not what I want to know.

You need pygame.Rect() which everybody use to keep player position, to check collision and to blit player on screen.
Player position and size
player_image = ...
player_rect = pygame.Rect(p_spritex, p_spritey, 30, 40)
check collision
player_rect.colliderect(enemy_rect)
draw player
SURF.blit(player_image, player_rect)
And you don't have to use draw.rect
prect = pygame.draw.rect(SURF, (0,0,0), (p_spritex, p_spritey, 30, 40), 1)
because it is useless
doc: pygame.Rect
tutorial: Program Arcade Games With Python And Pygame

Managed to get it to work in the end so thought I would post here to help anyone else with the same issue.
I set the rectangle variables before the "while True" line (which if you are unsure of what I mean it is the line that sets the code to run whilst the game window is actually open). Then I put the 'blit' section where all the images are set onto the surface, into a single function to run all the time. I made the rectangle variables global and simply called them in this function before the background image, which covered them:
def surface():
global zone1rect
global zone2rect
global zone3rect
global bl
global prect
zone1rect
zone2rect
zone3rect
bl
prect = pygame.draw.rect(SURF, (0,0,0), (p_spritex, p_spritey, 30, 40), 1)
SURF.blit(background,(0,0))
SURF.blit(p_sprite,(p_spritex,p_spritey))
surface()
(^The surface function)
As you can see the player rectangle (surrounding the main sprite) is made global and 'created' in this function, simply because the coordinates change depending on the player's movements so I had to treat them a little differently.
Hope this helps anyone else with this issue, especially those who are unsure of alpha colours and such :)

Related

Why is my mouse movement so sluggish/laggy? [duplicate]

I am writing a simple top down rpg in Pygame, and I have found that it is quite slow.... Although I am not expecting python or pygame to match the FPS of games made with compiled languages like C/C++ or event Byte Compiled ones like Java, But still the current FPS of pygame is like 15. I tried rendering 16-color Bitmaps instead of PNGs or 24 Bitmaps, which slightly boosted the speed, then in desperation , I switched everything to black and white monochrome bitmaps and that made the FPS go to 35. But not more. Now according to most Game Development books I have read, for a user to be completely satisfied with game graphics, the FPS of a 2d game should at least be 40, so is there ANY way of boosting the speed of pygame?
Use Psyco, for python2:
import psyco
psyco.full()
Also, enable doublebuffering. For example:
from pygame.locals import *
flags = FULLSCREEN | DOUBLEBUF
screen = pygame.display.set_mode(resolution, flags, bpp)
You could also turn off alpha if you don't need it:
screen.set_alpha(None)
Instead of flipping the entire screen every time, keep track of the changed areas and only update those. For example, something roughly like this (main loop):
events = pygame.events.get()
for event in events:
# deal with events
pygame.event.pump()
my_sprites.do_stuff_every_loop()
rects = my_sprites.draw()
activerects = rects + oldrects
activerects = filter(bool, activerects)
pygame.display.update(activerects)
oldrects = rects[:]
for rect in rects:
screen.blit(bgimg, rect, rect)
Most (all?) drawing functions return a rect.
You can also set only some allowed events, for more speedy event handling:
pygame.event.set_allowed([QUIT, KEYDOWN, KEYUP])
Also, I would not bother with creating a buffer manually and would not use the HWACCEL flag, as I've experienced problems with it on some setups.
Using this, I've achieved reasonably good FPS and smoothness for a small 2d-platformer.
All of these are great suggestions and work well, but you should also keep in mind two things:
1) Blitting surfaces onto surfaces is faster than drawing directly. So pre-drawing fixed images onto surfaces (outside the main game loop), then blitting the surface to the main screen will be more efficient. For exmample:
# pre-draw image outside of main game loop
image_rect = get_image("filename").get_rect()
image_surface = pygame.Surface((image_rect.width, image_rect.height))
image_surface.blit(get_image("filename"), image_rect)
......
# inside main game loop - blit surface to surface (the main screen)
screen.blit(image_surface, image_rect)
2) Make sure you aren't wasting resources by drawing stuff the user can't see. for example:
if point.x >= 0 and point.x <= SCREEN_WIDTH and point.y >= 0 and point.y <= SCREEN_HEIGHT:
# then draw your item
These are some general concepts that help me keep FPS high.
When using images it is important to convert them with the convert()-function of the image.
I have read that convert() disables alpha which is normally quite slow.
I also had speed problems until I used a colour depth of 16 bit and the convert function for my images. Now my FPS are around 150 even if I blit a big image to the screen.
image = image.convert()#video system has to be initialed
Also rotations and scaling takes a lot of time to calculate. A big, transformed image can be saved in another image if it is immutable.
So the idea is to calculate once and reuse the outcome multiple times.
When loading images, if you absolutely require transparency or other alpha values, use the Surface.convert_alpha() method. I have been using it for a game I've been programming, and it has been a huge increase in performance.
E.G: In your constructor, load your images using:
self.srcimage = pygame.image.load(imagepath).convert_alpha()
As far as I can tell, any transformations you do to the image retains the performance this method calls. E.G:
self.rotatedimage = pygame.transform.rotate(self.srcimage, angle).convert_alpha()
becomes redundant if you are using an image that has had convert_alpha() ran on it.
First, always use 'convert()' because it disables alpha which makes bliting faster.
Then only update the parts of the screen that need to be updated.
global rects
rects = []
rects.append(pygame.draw.line(screen, (0, 0, 0), (20, 20), (100, 400), 1))
pygame.display.update(rects) # pygame will only update those rects
Note:
When moving a sprite you have to include in the list the rect from their last position.
You could try using Psyco (http://psyco.sourceforge.net/introduction.html). It often makes quite a bit of difference.
There are a few things to consider for a well-performing Pygame application:
Ensure that the image Surface has the same format as the display Surface. Use convert() (or convert_alpha()) to create a Surface that has the same pixel format. This improves performance when the image is blit on the display, because the formats are compatible and blit does not need to perform an implicit transformation. e.g.:
surf = pygame.image.load('my_image.png').convert_alpha()
Do not load the images in the application loop. pygame.image.load is a very time-consuming operation because the image file must be loaded from the device and the image format must be decoded. Load the images once before the application loop, but use the images in the application loop.
If you have a static game map that consists of tiles, you can buy performance by paying with memory usage. Create a large Surface with the complete map. blit the area which is currently visible on the screen:
game_map = pygame.Surface((tile_size * columns, tile_size * rows))
for x in range(columns):
for y in range(rows):
tile_image = # image for this row and column
game_map.blit(tile_image , (x * tile_size, y * tile_size))
while game:
# [...]
map_sub_rect = screen.get_rect(topleft = (camera_x, camera_y))
screen.blit(game_map, (0, 0), map_sub_rect)
# [...]
If the text is static, the text does not need to be rendered in each frame. Create the text surface once at the beginning of the program or in the constructor of a class, and blit the text surface in each frame.
If the text is dynamic, it cannot even be pre-rendered. However, the most time-consuming is to create the pygame.font.Font/pygame.font.SysFont object. At the very least, you should avoid creating the font in every frame.
In a typical application you don't need all permutations of fonts and font sizes. You just need a couple of different font objects. Create a number of fonts at the beginning of the application and use them when rendering the text. For Instance. e.g.:
fontComic40 = pygame.font.SysFont("Comic Sans MS", 40)
fontComic180 = pygame.font.SysFont("Comic Sans MS", 180)

Its my first python project and i have been trying to move a simple rectangle down the screen as tetris blocks fall down but failing [duplicate]

This question already has an answer here:
How can I move the ball instead of leaving a trail all over the screen in pygame?
(1 answer)
Closed 1 year ago.
I have tried many other methods, including creating a rectangle and iterating the y coordinate but that creates a long line which i do not want. This is my OOB approach to it, which is not great, but please bear with me as i am a beginner. I have also tried out the code in the documentation and it also created a line of rectangles. Kindly help me
class Rectangle:
def __init__(self, x, y):
self.x = x
self.y = y
self.rect=pygame.draw.rect(window,(255,0,0),pygame.Rect(x,y,50,50))
def draw_shape(self,x,y):
self.rect=pygame.draw.rect(window,(255,0,0),pygame.Rect(x,y,50,50))
pygame.display.update()
def move_shape(self,x):
for i in range(0,10):
x=x+50*i
self.draw_shape(x, 0)
recT= Rectangle(0,0)
recT.draw_shape(0,0)
while running==True:
for event in pygame.event.get():
if event.type==pygame.QUIT:
running=False
recT.move_shape(0)
pygame.display.update()
strong text
You are not clearing the screen so the old rectangles are still there.
screen.fill((0,0,0))
will fill it with black. Also take update out of draw shape. You should have one update method to update everything and one draw method to draw everything.

How can I find out if the mouse is hovering/clicking on a pygame sprite (not shaped as a rectangle or circle)

So I am making this button and I need to find out two things
1 - When is the user hovering over it?
2 - When is the user clicking on it?
As you can see these buttons are oddly shaped
I have tried looking on the pygame forums but those only state how to find out if two sprites are colliding.
Just handle the MOUSEBUTTONDOWN event, and check the event.pos against the rect of each of your sprites. If there's a collision, that's the one that was clicked on.
It's the same for hovering, except you need to grab the mouse position each frame via the pygame.mouse.get_pos() function.
If you put all your button sprites into a SpriteGroup, it will be easy to do these checks.
For oddly shaped sprites, it may help to use a Sprite mask. This is an extra-check made (after the rectangle collision shows true) at the per-pixel level. So while the mouse may have done some action within the bounds of the Sprite's rectangle, maybe it was not truly over the visible part of the Sprite's image.
Luckily the PyGame Sprite library makes the creation of a mask easy, and if defined, it will automatically be used as part of the collision detection.
class MaskedSprite( pygame.sprite.Sprite ):
def __init__( self, image, x, y ):
pygame.sprite.Sprite.__init__( self )
self.image = image.convert_alpha()
self.mask = pygame.mask.from_surface( self.image ) # <<-- HERE
self.rect = self.image.get_rect()
self.rect.center = ( x, y )
There are a few simple rules around mask creation. If the image has a transparent background, that will be used to determine what is part/not-part of the sprite-collision.
Otherwise there needs to be a difference in colour between the background/foreground of 127 colour units (the default). This can be changed with the threshold parameter to pygame.mask.from_surface().
There is also the pygame.mask.from_threshold() function.
To be honest, just use a transparent background, and everything will be OK.
Everything.

Pygame Surface layers: Transparent sprite layer issues

I am drawing a small colored square, using surface.fill(color, rect), on a sprite surface, which is then be displayed on top of the other surfaces in the following order:
Background surface
Maze surface
Sprite surface
Currently I am having a problem where my sprites smear across the screen because I'm not wiping the screen each time. How can I eliminate this smearing effect, while keeping the sprite layer from covering the other layers?
Layers code - Initialized, but not updated each frame.
game_surface = pygame.Surface((self.canvas.get_size()))
game_surface = game_surface.convert()
game_surface.fill((0,0,255))
maze_surface = pygame.Surface((self.canvas.get_size()))
maze_surface = maze_surface.convert_alpha()
maze_surface.fill((0,0,255,0))
play_surface = pygame.Surface((self.canvas.get_size()))
play_surface = play_surface.convert_alpha()
play_surface.fill((0,0,0,0))
Presently only the play_surface actually actually uses transparency, but eventually both the play_surface and the maze_surface will need to be transparent.
Sprites layer update - Called every time the sprite is moved.
def update(self, canvas):
# move sprite if keys pressed (not shown)
self.surface.fill((0,0,0,0)) # Newest screen fill attempt. Causes smearing effect
self.surface.fill(self.color, self.rect) # draw green square
canvas.blit(self.surface, (0,0))
Smear effect: Red = maze_layer, Green = smeared sprite
Alternate sprite fill - Altered version of the above
def update(self, canvas):
# move sprite if keys pressed (not shown)
self.surface.fill((0,0,0)) # Original screen fill. Covers up lower layers
self.surface.fill(self.color, self.rect) # draw green square
canvas.blit(self.surface, (0,0))
Non-transparency - Black = filled color (I want to do this without covering up the other layers)
How can I eliminate this smearing effect (pic 1), while keeping the sprite layer from covering up the other layers (pic 2)? Any assistance is very much appreciated.
You need to redraw the background covered by your sprites, update their positions, and then redraw the sprites in their new position.
You may be interested in exploring the pygame.sprite module which has built-in support for this functionality. You can create subclasses of pygame.sprite.Sprite that:
Override the update(self) method (optional)
Have self.image and self.rect attributes that are used to draw them to screen
Are added to pygame.sprite.Group instances
Pygame's sprite group classes (such as Group, GroupSingle, LayeredUpdates, etc.) include methods like pygame.sprite.Group.clear(surface_dest, background) (see the documentation) to facilitate the task of re-drawing background images on your sprites before moving and re-drawing them elsewhere.

python pygame physics rectange - circle collision

Again this question is on PyParticles4.
Link to last question for reference
Comment if unclear...
I am working on a Shooter game, much like this, but on a flat land with a wall that varies it's height on every turn(something for fun in the game) and with 2 players,each with a cannon that can move some distance (there's a limit, and they can't move beyond a certain amount from their start position) on each turn(the player decides if he wishes to move).
My code so far(for the Bullet and Shooter)
class Bullet(PyParticles.Particle):
def hit(self,shooterlist):
for shoot in shooterlist:
#confusion
dist = math.hypot(dx,dy)
# other funcs to added
class Shooter:
def __init__(self,pos,size):
self.rect = pygame.Rect(pos,size)
# other funcs to added
My Problems
Collision of the bullet with the Shooter. Any Ideas on how to know when the bullet collides with the rect?
I have been advised by someone to look at all the points on the edge of the rect, and see if it is within the circle but it seems to be very slow.
I think something faster would be better..
..
..
Update:
The circle can have a rect around it, which if collides with the rect, I now know when the rect is close to the circle, maybe even touching it.. How do i move forward??(Thx to PygameNerd)
I am not sure what you mean by your question, but I thingk that you need the colliderect function.
rect.colliderect(rect): Return bool
Put this in the code somewhere, and if it returns true, have the ball explode.
You can run another test every time colliderect returns true. There are a few options for what that test can be. One is similar to the advice you already received but with the wall and circle switched. Check the points on the circumference of the circle to see if one of them collides with the wall using Rect.collidepoint. In pygame, you can get a list of circumference points by creating a Mask from the circle surface and running Mask.outline. You probably won't need every point, so you could get just every Nth point. In fact, you may only need the midpoints of the edges of the circle's rect, which you can get from the Rect object itself.
You're asking for precise collision, and that would give you pixel perfect collision detection between a circle and a rectangle. You may not need it in your game, though, so I suggest trying with just colliderect first. If you're interested in collision detection in pygame in general, there are a lot of options that are directly implemented in or based on functions described in the Mask and Sprite documentation.
You can use Rect.collidepoint with the center of the circle but make rect bigger
collide_rect = Rect(x - p.radius,y - p.radius,w + 2 * p.radius,h + 2 * p.radius)
if collide_rect.collide_point(p.pos):
# Collision Resolution

Categories