Collision between Rect and another unknown Rect - python

I was wondering if it was possible to find out if a Rect is colliding with another Rect. The problem is that I do not know what/where that other Rect is.
I have a Rect which moves around (of which I know where it is).
I have many other Rects on the same "map".
I dont want to make a list of all Rects on the map and then try collideRect with each and every one of them.
Does anyone have an idea under these circumstances for a function that takes a Rect and returns a list of all other Rects with which it collides? (Without using the collideRect function for all existing Rects?)
Can I somehow "scan" only the area of the first Rect and if there is another Rect in the same "spot" I return the other Rect?
I have come up with nothing so far...

At some point, you will need to check to see if the Rect collides with any other Rect. With that in mind, there are some ways to speed things up, basically relying on grouping Rects.
For example, assuming these Rects are objects in the level that don't move around, you could sort them by the X-coordinate, and remember the maximum width. When you want to run collision detection, start at the main Rect's left side minus the maximum width, and loop through until the Rect's right side. Any Rects outside of that range do not have the ability to collide, and so do not need to be checked.
Alternately, you could divide the level into, say, 16 squares, and give each square a list of all Rects within the square. Then, just decide which square the main Rect is in, and just compare with the Rects in there. (With logic for overlaps, of course.)
There's a large number of ways to do this.

Related

Pygame Surface Positioning

As I understood pygame drawing method, the 2nd argument of the blit function (screen.blit(surface, (0,0))) tells pygame where to draw the given surface (like an offset to start drawing from, or rather a placement of the surface on the screen). Although, from recent experimenting, it seems that pygame surfaces placements are fixed, and that (0,0) is used to crop the surface before pygame blit it to the screen, for efficiency purposes.
Are surfaces placement really fixed (is my latter observation correct)? and if so, is there another way to conveniently move an already drawn surface to another position on the screen? Or should i implement my own way of moving complex "drawable" objects?
Thanks!
A pygame.Surface object has no position, it has a size only. Note, the location of the Rect object which is returned by pygame.Surface.get_rect() is always (0, 0).
When you blit a Surface on another Surface, then each pixel is copied and placed at the corresponding position of the destination Surface. Thus always a position has to be specified, when a Surface is blit on a destination Surface.
See also Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
[...] if so, is there another way to conveniently move an already drawn surface to another position [...]
You have a basic misunderstanding. A Surface cannot be "moved". A Surface is copied on the Surface object which is associated to the game window.
A Surface appears to be moving, because the entire scene is drawn in every frame. First the background is drawn, then the objects (Sprites, Surfaces) are drawn on top of the background and finally the display is updated (in every frame). If an object is placed at a slightly different position in every frame, then the object appears to be moving smoothly.

Python, Pygame, Livewires - How to make smooth collisions?

for spike in self.overlapping_sprites:
spike.handle_collide
These two lines are coding magic for beginners. However, I seem to have hit a brick wall in my games. After testing around I cannot find a way to make collisions more Precise! What I mean is that it's not when two sprites actually touch that they count as overlapping, it's when the boxes around them overlap. So how can I somehow make it so that the transparent area around my sprite doesn't count as a collision if it hits other sprites?
The most logical thing to do is to make two rectangles per sprite, outer one with borders and inner one counted as object itself.
Take a 64 x 64 box for example. You can make it like 62 x 62 inner box with 2 pixel margin, shouldn't be too difficult to implement. And check only the inner area for collision if you want area nearby box to be untouchable.

Using colliderect in Pygame

How do I tell python to detect whether two objects/ images touch each other? For example when an image of pacman touches an image of a ghost?
http://www.pygame.org/docs/ref/rect.html#pygame.Rect.colliderect
colliderect()
test if two rectangles overlap
colliderect(Rect) -> bool
Returns true if any portion of either rectangle overlap (except the
top+bottom or left+right edges).
If the only collision detection between sprites is between pac-man and other objects, then just call colliderect on every combination of pacman's collision rectangle and every other collision rectangle.
If every single combination of collisions can be meaningful, then produce a big list of all of them and colliderect each rectangle with each rectangle further along in the list.
Every collision that occurs, you can choose to do something - you could even call both objects, passing the other object that collided, and thereby allowing the logic to be contained within one or both of the objects.
I'm assuming you're using Sprites for your pacman and ghost? If so you want one of the sprite collision functions: http://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide
Otherwise, use the Rect collision Patashu links.

How to avoid copying the level Surface every frame in worms-like game?

I am working on a game that has destructible terrain (like in the game Worms, or Scorched Earth) and uses pixel perfect collision detection via masks.
The level is a single surface and how it works now is that I create a copy every frame, draw all sprites that need drawing on it, then blit the visible area to the display surface.
Is there any way to avoid copying the whole level surface every frame and still be able to use the pixel perfect collision tools found in pygame?
I tried blitting the level surface first, then blitting every sprite on the screen (with their blit coordinates adjusted by the camera, except for the player character whose coordinates are static), but in that case the collision detection system falls apart and I can't seem to be able to fix it.
UPDATE
I have managed to make it work the following way:
When drawing the sprites, I convert their game world coordinates (which are basically coordinates relative to the origin of the level bitmap) to screen coordinates (coordinates relative to the camera, which is the currently visible area of the level).
During the collision detection phase I use the coordinates and bounding boxes that are positioned relative to the level surface; so just like above. The thing is that the camera's position is bound to the player's position which is not and should not have been a static value (I am really not sure how I managed to not realize that for so long).
While this fixes my problem, the answer below is a much more comprehensive look on how to improve performance in a situation like this.
I am also open to suggestions to use other libraries that would make the ordeal easier, or faster. I have thought about pyglet and rabbyt, but it looks like the same problem exists there.
This is an issue that used to come up a lot in the days before graphics accelerators, when computers were slow. You basically want to minimize the work required to refresh the screen. You are on the right track, but I recommend the following:
Keep a copy of the background available offscreen, as you are doing
now.
Allocate a working bitmap that is the same size as the screen.
For each sprite, compute the bounding rectangle (bounding box) for
its new and old positions.
If the new and old bounding boxes overlap, combine them into one
larger box. If they do not overlap, treat them separately.
Group all the bounding boxes into sets that overlap. They might all
end up in one set (when the sprites are close to each other), or
each bounding box might be in a set by itself (when the sprites are
far apart).
Copy the background to regions of the working bitmap corresponding
to each bounding box set.
Copy the sprites for each set to the working bitmap in their new
positions (in the correct z-order, of course!).
Finally, copy the finished offscreen bitmap to the display surface,
set bounding box by set bounding box.
This approach minimizes the amount of copying that you have to do, both of background and sprite. If the sprites are small relative to the display area, the savings should be significant. The worst case is where the sprites are all arranged on a diagonal line, just barely overlapping each other. In this case, you might want to switch to a more generalized bounding shape than a box. Take a look at QuickDraw Regions for an example: Wikipedia Discussion Patent Source.
Now, you may be thinking that the work to group the bounding boxes into sets is a O(n^2) operation, and you would be right. But it grows only with the square of the number of sprites. 16 sprites implies 256 comparisons. That's probably less work than a single sprite blit.
I focused on minimizing the pixel copying work. I must admin I am not familiar with the particulars of your collision detection library, but I get the idea. Hopefully that is compatible with the algorithm I have proposed.
Good luck. If you finish the game and post it online, put a link to it in your question or a comment.

Pygame: Updating Rects with scrolling levels

I'm currently making a 2D side-scrolling run'n'jump platform game in PyGame. Most of the stuff is working OK and very well in fact - I am exploiting the fast pyGame Sprite objects & groups.
What I'm interested to know is how people usually deal with Rects for scrolling games. I obviously have a level that is much bigger than visible area, and the player, enemies, bullets etc each have their own (x,y) coordinates which describe where they are in the level.
But now, since we use the "spriteGroup.draw(surface)" call, it will not display them in the right spot unless each objects Rects have been adjusted so that the right part displays on the screen. In other words, everytime a player/enemy/bullet/whatever else is updated, the Camera information needs to be passed, so that their Rect can be updated.
Is this the best method to use? It works but I don't really like passing the camera information to every single object at every update to offset the Rects.
Obviously the ideal method (I think) is to use Rects with "real" coordinates, blit everything to a buffer as big as the level, and then just blit the visible part to the screen, but in practice that slows the game down A LOT.
Any comments/insight would be appreciated.
Thanks
You could extend de Sprite.Group so it recives the camera information.
Then do one of these options:
A. Override the update method so it updates the on-screen coordinates of every sprite.
B. Override the draw method so it updates the on-screen coordinates of every sprite and then calls its parent draw method.
I think A it's easier and cleaner.
I don't really like passing the camera information to every single
object at every update to offset the Rects.
Camera may be global, or, a member of a global Game() class instance. Then your sprite class's draw method doesn't need an argument.
You can override draw yourself, so it does:
dest = game.camera.topleft + self.rect.topleft
screen.blit( self.surface, dest )
This keeps the bullet's rect in world-coordinates, yet blits using screen-coordinates.
One method I found is to keep track of a scrollx and a scrolly. Then, just add scrollx and scroll y to the coordinates when you move the rectangles.
You can have a 2 variables level_landlevel_d which see where you are in the level, Then check which sprites are in the visible area
level_d+height and level_l+width,
and draw them on the screen.
The simple way to do it is like that:
Create a CameraX and CameraY variables, and when you blit objects on the screen use this:
blit(surface, (x -CameraX, y -CameraY))
any object that gets affected by the camera should be drawn like that, but keep in mind that there are objects that you may want to remain uneffected (like health bars or status windows)
just keep in mind everytime you want to move camera do this
#Move Camera Right
CameraX += 10
#Move Camera Left
CameraX -= 10
#Move Camera Down
CameraY += 10
#Move Camera Up
CameraY -= 10
Just keep in mind that if they get negative values they may not work correctly, also you must probably define some limits (you dont want your camera to move over the limits of your map

Categories

Resources